'use client' import { useCallback, useEffect, useRef, useState, useTransition } from 'react' import type { FormAnswers, Question, Survey } from '@/lib/types' import { clearDraft, loadDraft, saveDraft } from '@/lib/storage' import { getHiddenCodes, isQuestionVisible } from '@/lib/conditional' import { submitSurvey } from '@/app/actions/submit-survey' import { ConsentScreen } from './consent-screen' import { ProgressBar } from './progress-bar' import { QuestionRenderer } from './question-renderer' // Section metadata (code prefix → display title) const SECTIONS: { prefix: string; title: string }[] = [ { prefix: 'A', title: 'Datos Generales' }, { prefix: '1', title: 'Prevalencia: cuidado y discapacidad' }, { prefix: '2', title: 'Intensidad y rol del cuidado' }, { prefix: '3', title: 'Impacto económico del cuidado' }, { prefix: '4', title: 'Tiempo y descanso' }, { prefix: '5', title: 'Impacto en el trabajo' }, { prefix: '6', title: 'Conciencia organizacional' }, { prefix: '7', title: 'Para cerrar' }, ] function getSectionPrefix(code: string): string { const m = code.match(/^([A-Za-z]+|\d+)/) return m ? m[1] : code } type FormStage = 'consent' | 'form' | 'success' | 'error' interface SurveyFormProps { survey: Survey questions: Question[] } export function SurveyForm({ survey, questions }: SurveyFormProps) { const [stage, setStage] = useState('consent') const [consentOperativo, setConsentOperativo] = useState(false) const [consentMacroN1, setConsentMacroN1] = useState(false) const [answers, setAnswers] = useState({}) const [errors, setErrors] = useState>({}) const [submitError, setSubmitError] = useState(null) const [isPending, startTransition] = useTransition() const [a11yAnnounce, setA11yAnnounce] = useState('') const [hydrated, setHydrated] = useState(false) const prevAnswers = useRef({}) // Hydrate from localStorage on mount useEffect(() => { const draft = loadDraft(survey.id) if (Object.keys(draft).length > 0) setAnswers(draft) setHydrated(true) }, [survey.id]) // Persist to localStorage after hydration useEffect(() => { if (!hydrated) return saveDraft(survey.id, answers) prevAnswers.current = answers }, [answers, hydrated, survey.id]) const handleChange = useCallback( (code: string, value: FormAnswers[string]) => { setAnswers((prev) => { const updated = { ...prev, [code]: value } const toHide = getHiddenCodes(code, value, questions, prev) for (const hiddenCode of toHide) { updated[hiddenCode] = undefined as unknown as FormAnswers[string] } if (toHide.length > 0) { setA11yAnnounce('Se ocultaron preguntas relacionadas y se limpiaron sus respuestas.') } else { const appeared = questions.some( (q) => q.conditional_rules?.some((r) => r.if_question === code) && isQuestionVisible(q, updated) && !isQuestionVisible(q, prev) ) if (appeared) setA11yAnnounce('Aparecieron nuevas preguntas según tu respuesta.') } return updated }) setErrors((prev) => { if (!prev[code]) return prev const { [code]: _, ...rest } = prev return rest }) }, [questions] ) function validate(): boolean { const newErrors: Record = {} for (const q of questions) { if (!q.required) continue if (!isQuestionVisible(q, answers)) continue const val = answers[q.code] if (val === null || val === undefined || val === '' || (Array.isArray(val) && val.length === 0)) { newErrors[q.code] = 'Este campo es obligatorio.' } } setErrors(newErrors) return Object.keys(newErrors).length === 0 } function handleSubmit(e: React.FormEvent) { e.preventDefault() if (isPending) return if (!validate()) { const firstErrorCode = Object.keys(errors)[0] if (firstErrorCode) document.getElementById(firstErrorCode)?.focus() return } const questionMap = Object.fromEntries(questions.map((q) => [q.code, q.id])) startTransition(async () => { const result = await submitSurvey({ surveyId: survey.id, answers: answers as Record, questionMap, consentOperativo, consentMacroN1, }) if (result.success) { clearDraft(survey.id) setStage('success') } else { setSubmitError(result.error) setStage('error') } }) } // ── Stage: consent ────────────────────────────────────────── if (stage === 'consent') { return ( { setConsentOperativo(op) setConsentMacroN1(macro) setStage('form') }} /> ) } // ── Stage: success ────────────────────────────────────────── if (stage === 'success') { return (

¡Gracias por participar!

Tu respuesta fue registrada correctamente. Recordá que la encuesta es anónima: no hay forma de vincular tus respuestas con tu identidad.

Los resultados agregados ayudarán a {survey.brand_config?.org_name ?? 'tu empresa'} a diseñar mejores apoyos para las personas cuidadoras.

) } // ── Stage: error ──────────────────────────────────────────── if (stage === 'error') { return (

No se pudo enviar la encuesta

Hubo un error al guardar tus respuestas ({submitError}). Podés intentarlo de nuevo.

) } // ── Stage: form ───────────────────────────────────────────── const visibleQuestions = questions.filter((q) => isQuestionVisible(q, answers)) const answeredCount = visibleQuestions.filter((q) => { const v = answers[q.code] return v !== null && v !== undefined && v !== '' && !(Array.isArray(v) && v.length === 0) }).length type Section = { prefix: string; title: string; questions: Question[] } const sectionMap = new Map() for (const q of visibleQuestions) { const prefix = getSectionPrefix(q.code) if (!sectionMap.has(prefix)) { const meta = SECTIONS.find((s) => s.prefix === prefix) sectionMap.set(prefix, { prefix, title: meta?.title ?? prefix, questions: [] }) } sectionMap.get(prefix)!.questions.push(q) } const bc = survey.brand_config return (
Ir al formulario
{a11yAnnounce}
{/* Header contextual con org/branch si está configurado */} {bc?.org_name && (

Estás respondiendo esto porque pertenecés a {bc.org_name} {bc.branch_name && ( <> y participás en las oficinas de {bc.branch_name} {bc.branch_city && <> en {bc.branch_city}}. )}

)}

{survey.title}

Esta encuesta es anónima. Tus respuestas no te identifican.

{Array.from(sectionMap.values()).map((section) => (

Sección {section.prefix} · {section.title}

{section.questions.map((q) => (
))}
))}
) }