longevity: gestionale trainer (clienti, check-up, scheda cliente) #2
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
COSA: pagine /longevity/gestionale per trainer e admin: elenco clienti, nuovo fascicolo, scheda cliente (sessioni, referto, misure fuori range), inserimento check-up con misure fisiche. API sotto /api/longevity/gestionale/.
PERCHÉ: metà del prodotto; oggi l'unico ingresso di dati è il questionario del cliente, i fascicoli si aprono solo da scripts/crea-fascicolo.ts.
FATTO QUANDO: un trainer crea un cliente, inserisce un check-up, vede il referto aggiornato; nomi letti solo tramite anagrafica.ts; test verdi.
DIPENDE DA: issue test fisici nel registro.
🤖 goldrake — preso in carico 2026-09-26T12:24:46Z
ramo:
lavoro/goldrake· capacità: docker, pubblica, sempre-accesa, systemdESITO: richiesta
RIF: gitea:Adriano/InsanityLab-web#2
COSA: analisi (passo 1). Gestionale trainer in due unità: 2a libreria + API, 2b pagine. Parte dopo #1 (usa calcolaSessione con le sessioni combinate).
RAMO: lavoro/goldrake — clone goldrake:~/lavoro/InsanityLab-web, app apps/platforms
Vincoli verificati
src/lib/auth.tsRULES:/longevity/gestionalee/api/longevity/gestionalegià riservati a admin+trainer. Tutto il resto di/api/longevity/è aperto ai clienti: ogni endpoint nuovo sta sotto/api/longevity/gestionale/.tests/longevity/anagrafica.test.ts: fuori dalib/longevity/anagrafica.tsedb.tsnessun file .ts/.astro può contenere insieme le stringheIdentityeLongevity. Le pagine importano il layoutLongevity→ le pagine non nominano mai identity: i nomi arrivano da funzioni di anagrafica.ts che aprono identity da sole (comecodicePerUtenteLoggato).src/layouts/Longevity.astro: propvariante: 'referto' | 'gestionale', la seconda dichiarata e non implementata.src/pages/longevity/index.astro: redirect fisso a/longevity/io.Unità 2a — libreria e API
lib/longevity/anagrafica.ts, funzioni nuove sul singleton privatogetIdentityDb(), congetLongevityDb()da db.ts dove serve:elencoClienti(): { client_code, nome, cognome, data_nascita: string|null, ha_accesso: boolean }[]ordinato per cognome, nomeclienteDaCodice(code): { client_code, nome, cognome, data_nascita, email, telefono, ha_accesso } | nulletaDelCliente(code, alla: string): number | null(viaetaAllaData; null senza data di nascita)apriFascicolo(dati):creaCliente(getIdentityDb(), getLongevityDb(), dati), senza user_id (l'accesso è #8)lib/longevity/gestionale.ts(solo longevity, mai identity):campiDelCheckup(db, alla):testAttivifiltrati sul prefissof_, raggruppati perassenell'ordine dei pesi; stesso tipo diCampoQuestionario(vista.ts)salvaCheckup(db, { client_code, data, eta: number|null, operatore, misure: MisuraIn[] })→{ ok: true, sessioneId, fuoriRange: string[] } | { ok: false, errore }. Rifiuta: nessuna misura; test_id che non inizia perf_; soggetto inesistente. Validazione prima, poi una transazione:apriSessionetipocheckup,registraMisurefontemanuale,salvaScore(calcolaSessione). Stesso schema disalvaDalForm(vista.ts).sessioniDi(db, code): id, data, tipo, operatore, numero misure, fitness age congelata (o null), ordine data DESC, id DESCmisureFuoriRange(db, code): test_id, etichetta, valore, unità, data sessione?errore=<testo>):pages/api/longevity/gestionale/clienti.tsPOST: nome, cognome, sesso M|F obbligatori; data_nascita (AAAA-MM-GG, non futura), email, telefono facoltativi →apriFascicolo→ 303 a/longevity/gestionale/cliente/<code>pages/api/longevity/gestionale/checkup.tsPOST:client_code(formato^ISL-\d{4}$e presente insoggetti),data(AAAA-MM-GG, non futura), campif_*. Un campo vuoto = non misurato. La virgola decimale si accetta ("32,5"). Un campo non numerico → errore, niente sessione. Chiavi che non sono campi attivif_*ignorate.operatore=locals.user.username, mai dal corpo.eta=etaDelCliente(code, data). → 303 alla scheda cliente con?salvato=<sessioneId>; se ci sono misure fuori range, anche&fuori=<n>.Il
client_codequi arriva dal corpo, ed è corretto: il trainer lavora su tutti i fascicoli. Il ruolo lo garantisce il middleware.TEST 2a:
tests/longevity/gestionale.test.tscheckup, fontemanuale, score congelati; dopo un questionario (salvaDalForm) il referto del check-up ha gli assi del questionario e quelli fisici (sessioni combinate da #1)q_*nel check-up, nessuna misura →ok:falsee nessuna riga insessionimisureFuoriRange:memory:passati come fanno i test esistenti (se il singleton lo impedisce: variante con parametri, comecodicePerUtenteaccanto acodicePerUtenteLoggato)misureDalForm(form, campi)) esportata e testata: virgola, vuoto, non numerico, chiave estraneacanAccessPath('cliente', '/api/longevity/gestionale/checkup')= false, trainer = trueUnità 2b — pagine
layouts/Longevity.astrovariantegestionale: barra laterale sul modello dilayouts/StressIndex.astro, voci «Clienti» (/longevity/gestionale) e «Nuovo cliente». CSS instyles/longevity.css, solo classilg-*e colori ammessi dal test intests/longevity/pagine.test.ts.pages/longevity/gestionale/:index.astro: tabella clienti (codice, cognome nome, età, accesso sì/no, data e tipo dell'ultima sessione) con link alla schedanuovo.astro: form verso l'API clienticliente/[code].astro: anagrafica; pulsante «Nuovo check-up»; referto dell'ultima sessione (riusarefertoDi,Radar,MacroScore); tabella sessioni; misure fuori range (colore--lg-fuori-range); esiti?salvato/?fuori/?erroremostrati. Codice inesistente → 404.cliente/[code]/checkup.astro: data (default oggi), campi dacampiDelCheckupraggruppati per asse, etichetta + unità, inputinputmode="decimal", tutti facoltativi.pages/longevity/index.astro: trainer e admin →/longevity/gestionale; cliente →/longevity/iocome oggi. Nel gestionale, un link «Il mio referto» a/longevity/io.TEST 2b: estendere
tests/longevity/pagine.test.ts(controlli statici come gli esistenti): le pagine del gestionale usanovariante="gestionale", non contengonoIdentity, non interrogano il DB direttamente (.prepare(assente nelle .astro); index.astro smista per ruolo.NON TOCCARE
db.ts),auth.tsRULES, motore (motore/*),registro.ts: sono di longevity: test fisici nel registro e nel calcolo (prerequisito del gestionale) (#1)pages/api/longevity/questionario.tse il flusso del clienteapps/sito/ESITO: fatto (unità 2a)
RIF: gitea:Adriano/InsanityLab-web#2
COMMIT:
8e0b9e4anagrafica del gestionale ·758463bgestionale.ts ·95ffa08API clienti e checkup + 17 test (ramo lavoro/goldrake, pushato)PROVA: npx vitest run → 229 passed; tsc → 0 errori
CONTROLLO FINALE (Opus): diff conforme all'analisi. Deviazioni accettate: varianti *Param per i test; misureDalForm riceve i campi da testAttivi.
PROSSIMO: unità 2b (pagine).
Soluzione
Gestionale trainer. anagrafica.ts: elencoClienti, clienteDaCodice, etaDelCliente, apriFascicolo. lib/longevity/gestionale.ts: campiDelCheckup, salvaCheckup, sessioniDi. API: pages/api/longevity/gestionale/clienti.ts e checkup.ts. Pagine: pages/longevity/gestionale/index.astro, nuovo.astro, cliente/[code].astro (anagrafica, referto, sessioni, misure da verificare), cliente/[code]/checkup.astro; layout Longevity con barra laterale; /longevity instrada admin/trainer al gestionale. Commit 8e0b9e4..948068b,
2c9a74f. Stato: sul ramo lavoro/goldrake (commit2c9a74f), NON ancora in main né in produzione. PROVA: vitest 259/259, tsc 0 errori, build ok.ESITO: fatto
COSA: in produzione su piattaforme.tielogic.xyz, 2026-09-26 13:17 UTC. main
12b2e91.PROVA: docker compose up -d --build → container Up; migra() → colonne verifica/verificata_da/verificata_at presenti; registro_test f_ = 24; misure 20 prima e dopo.
BACKUP: /opt/docker/insanitylab-platforms/backup/*-pre-gestionale (sqlite3 .backup, integrity ok).