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:
@@ -228,8 +228,11 @@ export function valoriUltimoCheckup(db: Database.Database, clientCode: string):
|
||||
/**
|
||||
* I test_id da scartare perché appartengono a un'alternativa NON scelta di una sezione (#13):
|
||||
* `scelte` mappa la chiave di sezione alla chiave dell'alternativa scelta (dalla radio nel
|
||||
* form) — assente = vale la prima alternativa (comportamento del form senza JS/radio
|
||||
* mancante). Una sezione a campi fissi non contribuisce: nulla da scartare lì.
|
||||
* form). Una sezione ASSENTE da `scelte` (nessuna radio arrivata: form senza JS che non ha
|
||||
* ancora marcato nulla, o client non-browser) non scarta NIENTE — ripiegare sulla prima
|
||||
* alternativa butterebbe via in silenzio campi compilati di un'altra (es. f_plank_sec senza
|
||||
* alt_core), correzione del passo 3 su #13. Una sezione a campi fissi non contribuisce: nulla
|
||||
* da scartare lì.
|
||||
*/
|
||||
export function testIdScartatiDalleAlternative(
|
||||
sezioni: SezioneCheckup[], scelte: Map<string, string>
|
||||
@@ -237,7 +240,8 @@ export function testIdScartatiDalleAlternative(
|
||||
const scartati = new Set<string>();
|
||||
for (const s of sezioni) {
|
||||
if (s.tipo !== 'alternative') continue;
|
||||
const scelta = scelte.get(s.chiave) ?? s.alternative[0]?.chiave;
|
||||
const scelta = scelte.get(s.chiave);
|
||||
if (scelta === undefined) continue; // nessuna radio per questa sezione: non si scarta nulla
|
||||
for (const alt of s.alternative) {
|
||||
if (alt.chiave === scelta) continue;
|
||||
for (const c of alt.campi) scartati.add(c.id);
|
||||
|
||||
@@ -7,7 +7,9 @@
|
||||
import type Database from 'better-sqlite3';
|
||||
|
||||
const REGEX_DATA = /^\d{4}-\d{2}-\d{2}$/;
|
||||
const TESTO_MAX = 5000;
|
||||
// Esportato (#13, passo 3): l'API del check-up valida la nota PRIMA di salvare, con lo
|
||||
// stesso limite di aggiungiVoce — un numero duplicato qui e lì marcirebbe silenziosamente.
|
||||
export const TESTO_MAX = 5000;
|
||||
|
||||
export type VoceGiornale = {
|
||||
id: number;
|
||||
|
||||
@@ -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) });
|
||||
|
||||
@@ -125,7 +125,7 @@ const errore = Astro.url.searchParams.get('errore');
|
||||
<h2>Note operatore</h2>
|
||||
<label class="lg-field">
|
||||
<span>Nota per il giornale di bordo (facoltativa)</span>
|
||||
<textarea name="nota" rows="4"></textarea>
|
||||
<textarea name="nota" rows="4" maxlength="5000"></textarea>
|
||||
</label>
|
||||
</section>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user