diff --git a/src/components/longevity/Questionario.tsx b/src/components/longevity/Questionario.tsx new file mode 100644 index 0000000..e60cfb6 --- /dev/null +++ b/src/components/longevity/Questionario.tsx @@ -0,0 +1,174 @@ +// Il questionario in pagina: quattro schermate — le tre aree di domande punteggiate, +// lette dal registro (props `campi`, costruite da `campiDelQuestionario`), più le note +// libere e il consenso. Avanti/indietro fra i blocchi, invio solo sull'ultimo. +// +// Il consenso qui NON blocca lato client la scelta "No": bloccarlo qui ripeterebbe +// l'errore del prototipo del cliente in un altro punto (un controllo che sembra +// proteggere e non protegge). La verifica che conta è quella del server +// (src/lib/longevity/vista.ts, salvaDalForm): se il consenso non è affermativo, la +// compilazione non si salva e la risposta lo dice — questa isola si limita a mostrarlo. +import { useMemo, useState } from 'react'; +import type { CampoQuestionario } from '../../lib/longevity/vista'; + +interface Props { + campi: CampoQuestionario[]; +} + +const CAMPI_LIBERI: { id: string; etichetta: string }[] = [ + { id: 'farmaci', etichetta: 'Farmaci assunti abitualmente' }, + { id: 'problematiche_attuali', etichetta: 'Problematiche di salute attuali' }, + { id: 'problematiche_pregresse', etichetta: 'Problematiche di salute pregresse' }, + { id: 'obiettivi', etichetta: 'Obiettivi del percorso' }, +]; + +const BLOCCHI = [ + { titolo: 'Sonno & Recupero', sottoDominio: 'questionario_sonno' }, + { titolo: 'Energia & Stress', sottoDominio: 'questionario_energia_stress' }, + { titolo: 'Stile di Vita', sottoDominio: 'questionario_lifestyle' }, +] as const; + +type Esito = { tipo: 'ok'; sessioneId: number } | { tipo: 'errore'; messaggio: string }; + +export default function Questionario({ campi }: Props) { + const [passo, setPasso] = useState(0); + const [risposte, setRisposte] = useState>({}); + const [liberi, setLiberi] = useState>({}); + const [consenso, setConsenso] = useState<'' | 'Si' | 'No'>(''); + const [eta, setEta] = useState(''); + const [inviando, setInviando] = useState(false); + const [esito, setEsito] = useState(null); + + // Le domande arrivano dal registro in ordine di test_id, non raggruppate: qui si + // ricostruiscono i tre blocchi filtrando sul sotto-dominio, senza toccare i dati. + const gruppi = useMemo( + () => BLOCCHI.map((b) => ({ ...b, campi: campi.filter((c) => c.sottoDominio === b.sottoDominio) })), + [campi] + ); + + const totalePassi = gruppi.length + 1; // + il blocco note/consenso + const ultimo = passo === totalePassi - 1; + + async function invia() { + setInviando(true); + setEsito(null); + + const risposteNumeriche: Record = {}; + for (const [id, v] of Object.entries(risposte)) { + const n = Number(v); + if (v !== '' && Number.isFinite(n)) risposteNumeriche[id] = n; + } + + try { + const res = await fetch('/api/longevity/questionario', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + risposte: risposteNumeriche, + liberi, + consensoSanitario: consenso, + eta: eta === '' ? undefined : Number(eta), + }), + }); + const corpo = await res.json(); + if (res.ok) setEsito({ tipo: 'ok', sessioneId: corpo.sessioneId }); + else setEsito({ tipo: 'errore', messaggio: corpo.error ?? 'Invio non riuscito.' }); + } catch { + setEsito({ tipo: 'errore', messaggio: 'Invio non riuscito: verifica la connessione.' }); + } finally { + setInviando(false); + } + } + + if (esito?.tipo === 'ok') { + return ( +
+

Questionario salvato, grazie.

+
+ ); + } + + return ( +
+

Passo {passo + 1} di {totalePassi}

+ + {passo < gruppi.length && ( +
+

{gruppi[passo].titolo}

+ {gruppi[passo].campi.map((c) => ( + + ))} +
+ )} + + {ultimo && ( +
+

Note e consenso

+ + {CAMPI_LIBERI.map((c) => ( +