6.7 KiB
ETAPA 2A — Motor del formulario (render + lógica condicional, sin persistir)
Prompt versionado (Patrón B.1). Primera pieza de cara al usuario. Claude Code lo ejecuta tras leer
CLAUDE.md,DATA_MODEL.md,SEED_SPEC.md,ADR-002,sprints/sprint-2/BRIEF.mdy este prompt.
1. Objetivo
Construir el motor del formulario: una app Next.js que lee la encuesta live desde Supabase con la
anon key, renderiza las 44 preguntas (instrumento v2) con el widget correcto por tipo, ejecuta la lógica condicional,
valida, es mobile-first y accesible (WCAG AA), no pierde lo cargado al refrescar, y al enviar muestra
un resumen de respuestas. NO escribe nada en la base (eso es 2B).
2. Contexto previo
La fundación de datos (Sprint 1) está cerrada y verificada en Cloud: esquema, RLS, seed real de 44
preguntas (instrumento v2, Jun 2026), k-anonimato. El proyecto Supabase está linkeado. Todavía no existe app frontend — esta
etapa la scaffolding. responses.consent_record_id es NOT NULL, por eso 2A no persiste: la
escritura requiere el consentimiento, que es 2B. El mockup formulario_mapeo.html (en el historial
del proyecto) es el contrato visual: replicar su look y flujo, pero leyendo del esquema, no hardcoded.
3. Decisiones ya tomadas (no consultar de nuevo)
- Stack: Next.js (App Router) + react-hook-form + zod + @supabase/supabase-js (o
@supabase/ssr). Sin librería de gráficos (eso es Sprint 3). - El cliente usa la
anonkey (pública, respeta RLS), desdeNEXT_PUBLIC_SUPABASE_URLyNEXT_PUBLIC_SUPABASE_ANON_KEYen.env.local. Elservice_roleNUNCA va a una variableNEXT_PUBLIC_ni al cliente (regla 9 de CLAUDE.md). options: array parasingle_choice/multiple_choice; objeto{min,max,min_label,max_label}pararating(3.5, 4.3); null paratext_short/text_long. Manejar las tres formas.- Tipos de widget:
single_choice→radio,multiple_choice→checkbox,rating→escala 1-5,text_long→textarea,text_short→<input type="text">de una línea (A1/A2/A3: País, Ciudad, Sede). - Condicionales: cada pregunta dependiente trae
conditional_rulescon{if_question, op:'eq', value:'Sí', action:'show'}. Triggers: 1.1 (→1.2–1.5), 1.6 (→1.7–1.12), 1.13 (→1.14–1.16). Con "No"/"Prefiero no decir" ocultar y limpiar las respuestas del bloque. - 6.2 =
multiple_choiceconmax_select=3(impedir marcar más de 3). - 2A no escribe
responses/answers/consent_records. Al enviar → pantalla de resumen.
4. Tareas
- Scaffolding de la app Next.js (App Router) en el repo. Setear el cliente Supabase con la
anonkey desde.env.local. Crear.env.example(sin valores) documentando las dos variables. - Leer desde Supabase (anon) la encuesta
status='live'+ sus 44questionsordenadas porposition, con susoptions,type,max_select,is_sensitive,conditional_rules. (Si la encuesta seed no estálive, dejar documentado que el director debe setearla alive, o leer por id — NO cambiar el estado desde el cliente.) - Renderizar cada tipo con su widget:
single_choice→radios,multiple_choice→checkboxes (con topemax_select),rating→escala 1-5 (usando el objeto options),text_long→textarea,text_short→input de una línea (A1/A2/A3). - Motor de lógica condicional data-driven: evaluar
conditional_rulespara mostrar/ocultar; al ocultar un bloque, limpiar sus valores. - Validación con zod (tipos correctos; respetar
max_select;requiredsegún el camporequiredde cada pregunta — hoy todasfalse, así que validación liviana). - UX: mobile-first fluida, replicando el contrato visual del mockup (secciones, progreso, estados de
opción). Accesibilidad WCAG 2.2 AA: HTML semántico,
<label>asociado a cada input, navegación y operación completa por teclado, foco visible, contraste suficiente, y anunciar a lectores de pantalla la aparición/desaparición de los bloques condicionales (aria-expanded/aria-live). - Resiliencia lite: persistir las respuestas en progreso en
localStorage(clave porsurvey_id) para sobrevivir un refresh; limpiar al enviar. No guardar nada identificable. - Al enviar: validar y mostrar una pantalla de resumen con las respuestas dadas. NO llamar a la base para escribir.
5. Definition of Done
npm run devlevanta la app y carga la encuesta desde Supabase (anon).- Se renderizan las 44 preguntas; los 3 bloques condicionales aparecen/desaparecen con 1.1/1.6/1.13.
- 6.2 no deja marcar más de 3; rating (3.5/4.3) funciona con el formato objeto; A1/A2/A3 son inputs de texto.
- Navegable y operable solo con teclado; pasa una auditoría a11y básica (axe o Lighthouse) sin errores AA.
- Refresh conserva lo cargado; enviar limpia el almacenamiento y muestra el resumen.
npm run buildlimpio; ninguna variableNEXT_PUBLIC_contiene elservice_role.git diff --name-only HEADsolo con archivos de la app nueva +.env.example.
6. Validaciones automatizadas mandatorias (antes de entregar)
- La carga lee 44 preguntas desde la base (logear el conteo), no un número hardcodeado.
- Las 13 condicionales se evalúan desde
conditional_rules(no conifcableados por código). max_selectde 6.2 se respeta en runtime (test o verificación manual reproducible).npm run buildsin errores.- Auditoría a11y (axe-core o
lighthouse) sobre la página del formulario: 0 violaciones de nivel AA. - Grep de seguridad:
service_roleNO aparece en ningún archivo del cliente ni enNEXT_PUBLIC_*. - Si CUALQUIERA falla: NO declarar lista la etapa. Reportar y esperar.
7. Qué reportar
- Verificado: conteo de preguntas cargadas, resultado de la auditoría a11y (con la herramienta y
el puntaje/violaciones), confirmación de que el build pasa, salida del grep de
service_role. - Asumido / Hallazgos / Decisiones por iniciativa (clasificadas por nivel).
git diff --name-only HEADpegado.- Indicar cómo levantar la app localmente para que el director la abra (el comando y la URL).
8. Qué NO hacer
- NO escribir en
responses,answersniconsent_records(es 2B). - NO construir el flujo de consentimiento ni el dashboard.
- NO hardcodear las preguntas: todo se lee del esquema.
- NO poner el
service_roleen el cliente ni en variablesNEXT_PUBLIC_. - NO recolectar ni guardar nada identificable del respondente (sin
respondent_id, sin fingerprinting, sin claves de localStorage que identifiquen a la persona). El anonimato es la premisa. - NO cambiar el estado de la encuesta (
draft/live) desde el cliente. - Ante conflicto entre una restricción y "que compile/funcione": detenerse y reportar.