--- // La scheda di un cliente: anagrafica, il referto dell'ultima sessione (lo stesso che il // cliente vede da solo su /longevity/io, qui per il trainer), l'elenco delle sessioni e le // misure fuori range. Un codice che non esiste in anagrafica è un 404, non uno schermo // vuoto — non c'è niente da mostrare per un fascicolo che non c'è. import Longevity from '../../../../layouts/Longevity.astro'; import Radar from '../../../../components/longevity/Radar'; import MacroScore from '../../../../components/longevity/MacroScore.astro'; import { getLongevityDb } from '../../../../lib/longevity/db'; import { clienteDaCodice, etaDelCliente } from '../../../../lib/longevity/anagrafica'; import { sessioniDi } from '../../../../lib/longevity/gestionale'; import { misureDaVerificare } from '../../../../lib/longevity/misure'; import { refertoDi, ultimaSessioneDi } from '../../../../lib/longevity/vista'; import { formattaNumero } from '../../../../lib/longevity/formato'; import { vociDelGiornale } from '../../../../lib/longevity/giornale'; export const prerender = false; const { code } = Astro.params; const cliente = code ? clienteDaCodice(code) : null; if (!cliente) return new Response('Cliente non trovato', { status: 404 }); const db = getLongevityDb(); const oggi = new Date().toISOString().slice(0, 10); const eta = etaDelCliente(cliente.client_code, oggi); const sessioni = sessioniDi(db, cliente.client_code); const daVerificare = misureDaVerificare(db, cliente.client_code); const ultimaId = ultimaSessioneDi(db, cliente.client_code); const referto = ultimaId !== null ? refertoDi(db, ultimaId) : null; const voci = vociDelGiornale(db, cliente.client_code); // #16: giornale di bordo, solo staff const ETICHETTA_TIPO: Record<'checkup' | 'questionario', string> = { checkup: 'Check-up', questionario: 'Questionario', }; const formattaData = (iso: string) => new Date(`${iso}T00:00:00`).toLocaleDateString('it-IT', { day: '2-digit', month: 'short', year: 'numeric' }); const params = Astro.url.searchParams; const salvato = params.get('salvato'); const corretto = params.get('corretto'); // #12: redirect di correggiCheckup dopo una correzione const fuoriCount = params.get('fuori'); const errore = params.get('errore'); const verificata = params.get('verificata'); const MESSAGGIO_VERIFICA: Record<'confermata' | 'scartata', string> = { confermata: 'Misura confermata: punteggi ricalcolati.', scartata: 'Misura scartata.', }; const messaggioVerifica = verificata === 'confermata' || verificata === 'scartata' ? MESSAGGIO_VERIFICA[verificata] : null; const giornaleEsito = params.get('giornale'); // #16: 'aggiunta' | 'cancellata', dal redirect dell'API const MESSAGGIO_GIORNALE: Record<'aggiunta' | 'cancellata', string> = { aggiunta: 'Nota aggiunta al giornale di bordo.', cancellata: 'Nota cancellata dal giornale di bordo.', }; const messaggioGiornale = giornaleEsito === 'aggiunta' || giornaleEsito === 'cancellata' ? MESSAGGIO_GIORNALE[giornaleEsito] : null; ---

{cliente.cognome} {cliente.nome}

{cliente.client_code} · {eta !== null ? `${eta} anni` : 'età non registrata'} · accesso {cliente.ha_accesso ? 'attivo' : 'non ancora attivato'}

{!cliente.ha_accesso && ( Crea accesso al referto )} Nuovo check-up
{errore &&

{errore}

} {salvato &&

Check-up salvato (sessione #{salvato}).

} {corretto &&

Check-up corretto (sessione #{corretto}): punteggi ricalcolati.

} {messaggioVerifica &&

{messaggioVerifica}

} {messaggioGiornale &&

{messaggioGiornale}

} {fuoriCount && (

{fuoriCount} {Number(fuoriCount) === 1 ? 'misura è' : 'misure sono'} fuori dal range di plausibilità: verificale più sotto.

)}

Anagrafica

Email: {cliente.email ?? '—'}

Telefono: {cliente.telefono ?? '—'}

Data di nascita: {cliente.data_nascita ? formattaData(cliente.data_nascita) : '—'}

Giornale di bordo

Solo lo staff lo vede. Una nota sbagliata si cancella e si riscrive: non si modifica.

{voci.length === 0 ? (

Nessuna nota ancora.

) : ( {voci.map((v) => ( ))}
DataAutoreNota
{formattaData(v.data)} {v.autore} {v.testo} {(v.autore === Astro.locals.user!.username || Astro.locals.user!.role === 'admin') && (
)}
)}
{!referto && (

Nessuna sessione ancora: apri un check-up per cominciare il fascicolo.

)} {referto && (

Fitness Age

{referto.fitnessAge !== null ? formattaNumero(referto.fitnessAge) : '—'} {referto.fitnessAge !== null && anni}

Copertura dati: {Math.round(referto.fitnessAgeCopertura * 100)}% {referto.fitnessAge === null && ' — non ancora sufficiente per un numero.'}

Radar

{referto.macro.map((m) => ( ))}
)}

Sessioni

{sessioni.length === 0 ? (

Nessuna sessione.

) : ( {sessioni.map((s) => ( ))}
DataTipoOperatoreMisureFitness Age
{formattaData(s.data)} {ETICHETTA_TIPO[s.tipo]} {s.operatore ?? '—'} {s.numeroMisure} {s.fitnessAge !== null ? formattaNumero(s.fitnessAge) : '—'}
)}
{daVerificare.length > 0 && (

Misure da verificare

Una misura confermata è un dato vero: entra nei punteggi e ricalcola quella sessione e le successive. Una scartata è un errore di strumento o protocollo: resta fuori per sempre.

{daVerificare.map((m) => ( ))}
DataTestValoreRange attesoDecisione
{formattaData(m.data)} {m.etichetta} {m.valore_num !== null ? formattaNumero(m.valore_num) : '—'}{m.unita ? ` ${m.unita}` : ''} {m.range_min ?? '—'}–{m.range_max ?? '—'}
)}