longevity: modulo check-up con sezioni e alternative del manuale (#13)
strutturaCheckup in gestionale.ts (ordine Pre-test..Strumenti, sezioni ad alternative con soglia testuale, «Altre misure» per i test dismessi in correzione, pressione/SpO2 esclusi); checkup.astro con radio button e fieldset (niente <select>); API checkup.ts scarta i campi delle alternative non scelte e aggiunge una voce di giornale dalla nota operatore sul check-up nuovo. Refs #13 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,11 @@ import type { APIRoute } from 'astro';
|
||||
import { getLongevityDb } from '../../../../lib/longevity/db';
|
||||
import { etaDelCliente } from '../../../../lib/longevity/anagrafica';
|
||||
import { testAttivi, type VoceRegistro } from '../../../../lib/longevity/registro';
|
||||
import { salvaCheckup, correggiCheckup } from '../../../../lib/longevity/gestionale';
|
||||
import {
|
||||
salvaCheckup, correggiCheckup, strutturaCheckup, testIdScartatiDalleAlternative,
|
||||
type SezioneCheckup,
|
||||
} from '../../../../lib/longevity/gestionale';
|
||||
import { aggiungiVoce } from '../../../../lib/longevity/giornale';
|
||||
import type { MisuraIn } from '../../../../lib/longevity/misure';
|
||||
|
||||
export const prerender = false;
|
||||
@@ -52,6 +56,22 @@ export function misureDalForm(
|
||||
return { ok: true, misure };
|
||||
}
|
||||
|
||||
/**
|
||||
* Le scelte del trainer nelle sezioni ad alternative del modulo (#13): legge il radio
|
||||
* `alt_<chiave sezione>` dal form. Una sezione senza radio nel form (JS disattivato che non
|
||||
* ha ancora impostato nulla, o un refuso) resta assente dalla mappa — la sceglie
|
||||
* `testIdScartatiDalleAlternative` per default sulla prima alternativa dell'elenco.
|
||||
*/
|
||||
function sceltePerAlternative(sezioni: SezioneCheckup[], form: FormData): Map<string, string> {
|
||||
const scelte = new Map<string, string>();
|
||||
for (const s of sezioni) {
|
||||
if (s.tipo !== 'alternative') continue;
|
||||
const v = form.get(`alt_${s.chiave}`);
|
||||
if (typeof v === 'string' && v !== '') scelte.set(s.chiave, v);
|
||||
}
|
||||
return scelte;
|
||||
}
|
||||
|
||||
export const POST: APIRoute = async ({ request, locals }) => {
|
||||
const form = await request.formData();
|
||||
const clientCodeForm = String(form.get('client_code') ?? '').trim();
|
||||
@@ -105,22 +125,36 @@ export const POST: APIRoute = async ({ request, locals }) => {
|
||||
const parse = misureDalForm(form, campiAttivi);
|
||||
if (!parse.ok) return erroreAllaScheda(parse.errore);
|
||||
|
||||
// Sezioni ad alternative (#13): si tengono solo i campi dell'alternativa scelta dalla
|
||||
// radio, i compilati di altre alternative della stessa sezione si scartano — anche se il
|
||||
// trainer li ha riempiti (JS disattivato, o un fieldset lasciato visibile per errore).
|
||||
const { sezioni } = strutturaCheckup(db, data);
|
||||
const scartati = testIdScartatiDalleAlternative(sezioni, sceltePerAlternative(sezioni, form));
|
||||
const misure = parse.misure.filter((m) => !scartati.has(m.test_id));
|
||||
|
||||
// L'operatore è SEMPRE quello della sessione loggata (garantita da RULES + middleware:
|
||||
// solo admin/trainer arrivano qui), mai un campo del form — lo stesso principio del
|
||||
// client_code in pages/api/longevity/questionario.ts, applicato al campo che qui conta.
|
||||
const operatore = locals.user!.username;
|
||||
const eta = etaDelCliente(clientCode, data);
|
||||
// Nota operatore (#13, #16): il form la mostra solo per un check-up nuovo (checkup.astro),
|
||||
// quindi in correzione la chiave è assente e questo resta vuoto — nessuna voce si aggiunge.
|
||||
const nota = String(form.get('nota') ?? '').trim();
|
||||
|
||||
if (sessioneId !== null) {
|
||||
const esito = correggiCheckup(db, { sessioneId, data, eta, operatore, misure: parse.misure });
|
||||
const esito = correggiCheckup(db, { sessioneId, data, eta, operatore, misure });
|
||||
if (!esito.ok) return erroreAllaScheda(esito.errore);
|
||||
const params = new URLSearchParams({ corretto: String(esito.sessioneId) });
|
||||
if (esito.fuoriRange.length > 0) params.set('fuori', String(esito.fuoriRange.length));
|
||||
return redirect(`/longevity/gestionale/cliente/${clientCode}?${params.toString()}`);
|
||||
}
|
||||
|
||||
const esito = salvaCheckup(db, { client_code: clientCode, data, eta, operatore, misure: parse.misure });
|
||||
const esito = salvaCheckup(db, { client_code: clientCode, data, eta, operatore, misure });
|
||||
if (!esito.ok) return erroreAllaScheda(esito.errore);
|
||||
// Voce di giornale SOLO dopo il salvataggio riuscito (#13): se la nota fosse rifiutata
|
||||
// (troppo lunga, data futura — non dovrebbe succedere, stessa data già validata sopra)
|
||||
// il check-up resta comunque salvato, non si perde per un problema della nota.
|
||||
if (nota !== '') aggiungiVoce(db, { clientCode, data, autore: operatore, testo: nota });
|
||||
|
||||
const params = new URLSearchParams({ salvato: String(esito.sessioneId) });
|
||||
if (esito.fuoriRange.length > 0) params.set('fuori', String(esito.fuoriRange.length));
|
||||
|
||||
Reference in New Issue
Block a user