longevity: il questionario in pagina, e il consenso che si puo negare davvero
Sostituisce il prototipo del cliente (salvava anche con "No" al consenso sanitario, perche' controllava solo che il campo non fosse vuoto). Qui il consenso si verifica PRIMA di scrivere qualunque dato: se non e' affermativo non si apre nessuna sessione, e la risposta lo dice. Il rifiuto si registra comunque fra le note di profilo, con esito e versione del questionario: va dimostrato tanto quanto un assenso. - src/lib/longevity/vista.ts: campiDelQuestionario (dal registro, non da una lista nel codice) e salvaDalForm (consenso prima di tutto, poi delega a salvaCompilazione). - src/pages/api/longevity/questionario.ts: il client_code arriva SEMPRE da codicePerUtente sulla sessione, mai dal corpo della richiesta. - src/lib/longevity/db.ts: getLongevityDb/getIdentityDb, connessioni singole per processo (come getDb() del sito), cosi' l'endpoint non apre una nuova connessione a ogni richiesta. - src/components/longevity/Questionario.tsx + pages/longevity/questionario.astro: isola React a quattro blocchi (le tre aree di domande + note/consenso), la pagina non interroga mai il database da sola.
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
import type { APIRoute } from 'astro';
|
||||
import { getIdentityDb, getLongevityDb } from '../../../lib/longevity/db';
|
||||
import { codicePerUtente } from '../../../lib/longevity/anagrafica';
|
||||
import { salvaDalForm } from '../../../lib/longevity/vista';
|
||||
import { CAMPI_LIBERI } from '../../../lib/longevity/questionario';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
const json = (status: number, body: object) =>
|
||||
new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json' } });
|
||||
|
||||
export const POST: APIRoute = async ({ request, locals }) => {
|
||||
// Il codice cliente si ricava SEMPRE dalla sessione dell'utente loggato (spec §8), mai dal
|
||||
// corpo della richiesta: qui infatti il corpo non viene mai letto per questo campo.
|
||||
const codice = codicePerUtente(getIdentityDb(), locals.user!.id);
|
||||
if (!codice) return json(403, { error: 'Nessun fascicolo cliente associato a questo utente.' });
|
||||
|
||||
let data: Record<string, unknown>;
|
||||
try { data = await request.json(); } catch { return json(400, { error: 'Dati non validi.' }); }
|
||||
if (typeof data !== 'object' || data === null) return json(400, { error: 'Dati non validi.' });
|
||||
|
||||
const risposteIn = data.risposte;
|
||||
if (typeof risposteIn !== 'object' || risposteIn === null) {
|
||||
return json(400, { error: 'Risposte mancanti.' });
|
||||
}
|
||||
const risposte: Record<string, number> = {};
|
||||
for (const [id, v] of Object.entries(risposteIn as Record<string, unknown>)) {
|
||||
if (typeof v === 'number' && Number.isFinite(v)) risposte[id] = v;
|
||||
}
|
||||
|
||||
// Solo i campi liberi dichiarati: un corpo con chiavi arbitrarie non ne inventa di nuove.
|
||||
const liberiIn = data.liberi;
|
||||
const liberi: Record<string, string> = {};
|
||||
if (typeof liberiIn === 'object' && liberiIn !== null) {
|
||||
for (const campo of CAMPI_LIBERI) {
|
||||
const v = (liberiIn as Record<string, unknown>)[campo];
|
||||
if (typeof v === 'string') liberi[campo] = v;
|
||||
}
|
||||
}
|
||||
|
||||
const consensoSanitario = typeof data.consensoSanitario === 'string' ? data.consensoSanitario : '';
|
||||
const eta = typeof data.eta === 'number' && Number.isFinite(data.eta) ? data.eta : undefined;
|
||||
|
||||
// La data della compilazione è quella del server, non quella (manomettibile) del client.
|
||||
const oggi = new Date().toISOString().slice(0, 10);
|
||||
|
||||
const esito = salvaDalForm(getLongevityDb(), {
|
||||
client_code: codice, data: oggi, eta, risposte, consensoSanitario, liberi,
|
||||
});
|
||||
|
||||
if (!esito.ok) return json(422, { error: esito.errore });
|
||||
return json(201, { sessioneId: esito.sessioneId });
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
---
|
||||
// Il questionario in pagina, sostituisce il prototipo del cliente. La pagina non
|
||||
// interroga il database da sola: chiede i campi a `campiDelQuestionario` (letti dal
|
||||
// registro), l'isola React fa il resto — quattro blocchi, e l'invio va a
|
||||
// /api/longevity/questionario, dove il consenso si verifica davvero.
|
||||
import Longevity from '../../layouts/Longevity.astro';
|
||||
import Questionario from '../../components/longevity/Questionario';
|
||||
import { getLongevityDb } from '../../lib/longevity/db';
|
||||
import { campiDelQuestionario } from '../../lib/longevity/vista';
|
||||
|
||||
export const prerender = false;
|
||||
|
||||
const oggi = new Date().toISOString().slice(0, 10);
|
||||
const campi = campiDelQuestionario(getLongevityDb(), oggi);
|
||||
---
|
||||
<Longevity title="Questionario" crumbs={['Questionario']}>
|
||||
<div class="lg-head">
|
||||
<h1>Questionario</h1>
|
||||
<p>Rispondi con calma: puoi tornare indietro finché non invii.</p>
|
||||
</div>
|
||||
<Questionario client:load campi={campi} />
|
||||
</Longevity>
|
||||
Reference in New Issue
Block a user