longevity: correzioni passo 3 su #13 unità B — scarto alternative e nota

testIdScartatiDalleAlternative: una sezione senza scelta in `scelte` (radio
assente dal form) non scarta più nulla, invece di ripiegare in silenzio
sulla prima alternativa (perdeva campi come f_plank_sec senza alt_core).

API checkup.ts: la nota operatore si valida (lunghezza max 5000, TESTO_MAX
esportato da giornale.ts) PRIMA di salvare il check-up, non più dopo — una
nota troppo lunga non faceva più fallire aggiungiVoce in silenzio ma
perdeva comunque la nota col check-up già scritto. maxlength sulla
textarea in checkup.astro.

Suite: 314 verdi, 4 rossi di piattaforma (baseline, invariati). tsc e
build puliti.

Refs #13
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-27 22:59:19 +02:00
parent 81021d18f3
commit b0f30f9e4c
5 changed files with 48 additions and 19 deletions
@@ -6,7 +6,7 @@ import {
salvaCheckup, correggiCheckup, strutturaCheckup, testIdScartatiDalleAlternative,
type SezioneCheckup,
} from '../../../../lib/longevity/gestionale';
import { aggiungiVoce } from '../../../../lib/longevity/giornale';
import { aggiungiVoce, TESTO_MAX as NOTA_TESTO_MAX } from '../../../../lib/longevity/giornale';
import type { MisuraIn } from '../../../../lib/longevity/misure';
export const prerender = false;
@@ -59,8 +59,9 @@ export function misureDalForm(
/**
* 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.
* ha ancora marcato nulla, o un refuso) resta assente dalla mappa: `testIdScartatiDalleAlternative`
* non scarta NULLA per quella sezione, invece di ripiegare sulla prima alternativa e buttare
* via in silenzio campi compilati di un'altra — correzione del passo 3 su #13.
*/
function sceltePerAlternative(sezioni: SezioneCheckup[], form: FormData): Map<string, string> {
const scelte = new Map<string, string>();
@@ -140,6 +141,12 @@ export const POST: APIRoute = async ({ request, locals }) => {
// 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();
// Validata PRIMA di salvare (correzione del passo 3 su #13): se aggiungiVoce fallisse DOPO
// il check-up (nota troppo lunga), il check-up resterebbe salvato ma la nota andrebbe persa
// in silenzio — stesso limite di aggiungiVoce (giornale.ts), non un numero duplicato qui.
if (nota.length > NOTA_TESTO_MAX) {
return erroreAllaScheda(`Il testo della nota non può superare ${NOTA_TESTO_MAX} caratteri.`);
}
if (sessioneId !== null) {
const esito = correggiCheckup(db, { sessioneId, data, eta, operatore, misure });
@@ -151,9 +158,9 @@ export const POST: APIRoute = async ({ request, locals }) => {
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.
// Voce di giornale SOLO dopo il salvataggio riuscito (#13): la lunghezza è già validata
// sopra, la data è la stessa già validata per il check-up — aggiungiVoce qui non dovrebbe
// più rifiutare nulla.
if (nota !== '') aggiungiVoce(db, { clientCode, data, autore: operatore, testo: nota });
const params = new URLSearchParams({ salvato: String(esito.sessioneId) });