From 2c9a74f849e7689970e5c33f43d360d301535ab8 Mon Sep 17 00:00:00 2001 From: "Adriano (goldrake)" Date: Sat, 26 Sep 2026 13:11:38 +0000 Subject: [PATCH] longevity: verifica misure fuori range in scheda cliente, link a crea accesso Scheda cliente ([code].astro): sezione "Misure da verificare" da misureDaVerificare (id, range) al posto di misureFuoriRange (nessun id, nessuna azione); ogni riga ha due form verso /api/longevity/gestionale/verifica (conferma/scarta), messaggio dopo la decisione, e link "Crea accesso al referto" quando il cliente non ha ancora un accesso. gestionale.ts: rimossa misureFuoriRange (e il tipo MisuraFuoriRange), sostituita ovunque da misureDaVerificare (misure.ts), l'unica con id e range utile alle azioni. accesso.astro: link "Torna alla scheda" in tutti gli stati. Refs #7 Refs #8 Co-Authored-By: Claude Sonnet 5 --- .../platforms/src/lib/longevity/gestionale.ts | 24 ---------- .../longevity/gestionale/cliente/[code].astro | 45 +++++++++++++++---- .../gestionale/cliente/[code]/accesso.astro | 4 ++ .../tests/longevity/gestionale.test.ts | 9 ++-- apps/platforms/tests/longevity/pagine.test.ts | 22 ++++++++- 5 files changed, 67 insertions(+), 37 deletions(-) diff --git a/apps/platforms/src/lib/longevity/gestionale.ts b/apps/platforms/src/lib/longevity/gestionale.ts index b48371c..84abcb5 100644 --- a/apps/platforms/src/lib/longevity/gestionale.ts +++ b/apps/platforms/src/lib/longevity/gestionale.ts @@ -132,27 +132,3 @@ export function sessioniDi(db: Database.Database, code: string): SessioneElenco[ }; }); } - -export type MisuraFuoriRange = { - test_id: string; - etichetta: string; - valore: number | null; - unita: string | null; - data: string; -}; - -/** Le misure di un cliente marcate fuori dal range di plausibilità del registro - * (`misure.fuori_range = 1`, scritto da `registraMisure`): quelle che oggi non contano per - * nessun calcolo (`misureValideAlla` le esclude) ma restano da mostrare e, un domani, - * confermare (#7). Più recente prima. */ -export function misureFuoriRange(db: Database.Database, code: string): MisuraFuoriRange[] { - return db.prepare( - `SELECT m.test_id AS test_id, r.etichetta AS etichetta, m.valore_num AS valore, - m.unita AS unita, s.data AS data - FROM misure m - JOIN sessioni s ON s.id = m.sessione_id - JOIN registro_test r ON r.test_id = m.test_id - WHERE m.client_code = ? AND m.fuori_range = 1 - ORDER BY s.data DESC, s.id DESC, m.id DESC` - ).all(code) as MisuraFuoriRange[]; -} diff --git a/apps/platforms/src/pages/longevity/gestionale/cliente/[code].astro b/apps/platforms/src/pages/longevity/gestionale/cliente/[code].astro index cb0de4e..73bf6cf 100644 --- a/apps/platforms/src/pages/longevity/gestionale/cliente/[code].astro +++ b/apps/platforms/src/pages/longevity/gestionale/cliente/[code].astro @@ -8,7 +8,8 @@ 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, misureFuoriRange } from '../../../../lib/longevity/gestionale'; +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'; @@ -22,7 +23,7 @@ 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 fuoriRange = misureFuoriRange(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; @@ -37,6 +38,13 @@ const params = Astro.url.searchParams; const salvato = params.get('salvato'); 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; --- - Nuovo check-up +
+ {!cliente.ha_accesso && ( + Crea accesso al referto + )} + Nuovo check-up +
{errore &&

{errore}

} {salvato &&

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

} + {messaggioVerifica &&

{messaggioVerifica}

} {fuoriCount && (

{fuoriCount} {Number(fuoriCount) === 1 ? 'misura è' : 'misure sono'} fuori dal range di @@ -129,18 +143,33 @@ const errore = params.get('errore'); )} - {fuoriRange.length > 0 && ( + {daVerificare.length > 0 && (

-

Misure fuori range

+

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. +

- + - {fuoriRange.map((m) => ( + {daVerificare.map((m) => ( + + ))} diff --git a/apps/platforms/src/pages/longevity/gestionale/cliente/[code]/accesso.astro b/apps/platforms/src/pages/longevity/gestionale/cliente/[code]/accesso.astro index f27e423..a06cf95 100644 --- a/apps/platforms/src/pages/longevity/gestionale/cliente/[code]/accesso.astro +++ b/apps/platforms/src/pages/longevity/gestionale/cliente/[code]/accesso.astro @@ -34,12 +34,14 @@ if (fascicolo && fascicolo.userId === null && Astro.request.method === 'POST') { {!fascicolo && (

Nessun fascicolo con codice {code}.

+

Torna alla scheda

)} {fascicolo && fascicolo.userId !== null && (

Questo fascicolo ha già un accesso collegato: non se ne crea un secondo.

+

Torna alla scheda

)} @@ -48,6 +50,7 @@ if (fascicolo && fascicolo.userId === null && Astro.request.method === 'POST') {

Accesso creato. Comunicala al cliente: non verrà mostrata di nuovo.

Utente: {creato.username}

Password: {creato.password}

+

Torna alla scheda

)} @@ -67,6 +70,7 @@ if (fascicolo && fascicolo.userId === null && Astro.request.method === 'POST') { />
+ Torna alla scheda
diff --git a/apps/platforms/tests/longevity/gestionale.test.ts b/apps/platforms/tests/longevity/gestionale.test.ts index 5e160f7..bbfb897 100644 --- a/apps/platforms/tests/longevity/gestionale.test.ts +++ b/apps/platforms/tests/longevity/gestionale.test.ts @@ -9,7 +9,8 @@ import { join } from 'node:path'; import { tmpdir } from 'node:os'; import { createLongevityDb, createIdentityDb } from '../../src/lib/longevity/db'; import { seedRegistro, seedPesi, MODEL_VERSION } from '../../src/lib/longevity/registro'; -import { salvaCheckup, sessioniDi, misureFuoriRange } from '../../src/lib/longevity/gestionale'; +import { salvaCheckup, sessioniDi } from '../../src/lib/longevity/gestionale'; +import { misureDaVerificare } from '../../src/lib/longevity/misure'; import { salvaDalForm, refertoDi } from '../../src/lib/longevity/vista'; import { creaCliente, elencoClientiParam, clienteDaCodiceParam, etaDelClienteParam, apriFascicolo, @@ -125,7 +126,7 @@ describe('salvaCheckup rifiuta prima di scrivere: nessuna traccia parziale', () }); describe('le misure fuori range restano nel fascicolo, marcate e leggibili', () => { - it('una misura oltre il range di plausibilità (f_peso_kg, max 250) si salva, si marca, ed esce da misureFuoriRange', () => { + it('una misura oltre il range di plausibilità (f_peso_kg, max 250) si salva, si marca, ed esce da misureDaVerificare', () => { const db = dbPronto(); const esito = salvaCheckup(db, { client_code: 'ISL-0001', data: '2026-09-26', eta: 38, operatore: 'n', @@ -140,9 +141,9 @@ describe('le misure fuori range restano nel fascicolo, marcate e leggibili', () ).get(esito.sessioneId) as { fuori_range: number }; expect(riga.fuori_range).toBe(1); - const elenco = misureFuoriRange(db, 'ISL-0001'); + const elenco = misureDaVerificare(db, 'ISL-0001'); expect(elenco.length).toBe(1); - expect(elenco[0]).toMatchObject({ test_id: 'f_peso_kg', valore: 500, data: '2026-09-26' }); + expect(elenco[0]).toMatchObject({ test_id: 'f_peso_kg', valore_num: 500, data: '2026-09-26' }); }); }); diff --git a/apps/platforms/tests/longevity/pagine.test.ts b/apps/platforms/tests/longevity/pagine.test.ts index c558e98..5d55d28 100644 --- a/apps/platforms/tests/longevity/pagine.test.ts +++ b/apps/platforms/tests/longevity/pagine.test.ts @@ -1,6 +1,6 @@ // tests/longevity/pagine.test.ts import { describe, it, expect } from 'vitest'; -import { readFileSync, existsSync } from 'node:fs'; +import { readFileSync, existsSync, readdirSync } from 'node:fs'; import { join } from 'node:path'; const leggi = (p: string) => readFileSync(join(process.cwd(), p), 'utf8'); @@ -146,4 +146,24 @@ describe('il gestionale del trainer (Unità 2b)', () => { expect(src).toMatch(/admin/); expect(src).toMatch(/Astro\.redirect\(\s*['"`]\/longevity\/io['"`]/); }); + + it('la scheda cliente porta le azioni di verifica di una misura fuori range (#7) e il link per creare l\'accesso (#8)', () => { + const src = leggi('src/pages/longevity/gestionale/cliente/[code].astro'); + expect(src).toMatch(/action=["']\/api\/longevity\/gestionale\/verifica["']/); + expect(src).toMatch(/name=["']misura_id["']/); + expect(src).toMatch(/value=["']confermata["']/); + expect(src).toMatch(/value=["']scartata["']/); + expect(src).toMatch(/\/longevity\/gestionale\/cliente\/\$\{cliente\.client_code\}\/accesso/); + }); + + it('nessuna pagina longevity usa piu misureFuoriRange, rimossa da gestionale.ts a favore di misureDaVerificare (#7)', () => { + // Scandisce TUTTE le pagine sotto src/pages/longevity/, stesso criterio di + // referto.test.ts: un elenco scritto qui non protegge una pagina nuova. + const dir = join(process.cwd(), 'src/pages/longevity'); + const pagine = (readdirSync(dir, { recursive: true }) as string[]).filter((f) => f.endsWith('.astro')); + expect(pagine.length).toBeGreaterThan(0); + for (const f of pagine) { + expect(readFileSync(join(dir, f), 'utf8'), `src/pages/longevity/${f}`).not.toMatch(/misureFuoriRange/); + } + }); });
DataTestValore
DataTestValoreRange attesoDecisione
{formattaData(m.data)} {m.etichetta} - {m.valore !== null ? formattaNumero(m.valore) : '—'}{m.unita ? ` ${m.unita}` : ''} + {m.valore_num !== null ? formattaNumero(m.valore_num) : '—'}{m.unita ? ` ${m.unita}` : ''} + {m.range_min ?? '—'}–{m.range_max ?? '—'} +
+ + +
+
+ + +