Files
motor-de-encuestas/sprints/sprint-2/ETAPA_2A_PROMPT.md
2026-06-04 14:30:28 -03:00

91 lines
6.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.md` y 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 **`anon` key** (pública, respeta RLS), desde `NEXT_PUBLIC_SUPABASE_URL` y
`NEXT_PUBLIC_SUPABASE_ANON_KEY` en `.env.local`. **El `service_role` NUNCA va a una variable
`NEXT_PUBLIC_` ni al cliente** (regla 9 de CLAUDE.md).
- `options`: **array** para `single_choice`/`multiple_choice`; **objeto** `{min,max,min_label,max_label}`
para `rating` (3.5, 4.3); **null** para `text_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_rules` con `{if_question, op:'eq',
value:'Sí', action:'show'}`. Triggers: **1.1** (→1.21.5), **1.6** (→1.71.12), **1.13** (→1.141.16).
Con "No"/"Prefiero no decir" ocultar **y limpiar** las respuestas del bloque.
- 6.2 = `multiple_choice` con `max_select=3` (impedir marcar más de 3).
- 2A **no** escribe `responses`/`answers`/`consent_records`. Al enviar → pantalla de resumen.
## 4. Tareas
1. Scaffolding de la app Next.js (App Router) en el repo. Setear el cliente Supabase con la `anon`
key desde `.env.local`. Crear `.env.example` (sin valores) documentando las dos variables.
2. Leer desde Supabase (anon) la encuesta `status='live'` + sus 44 `questions` ordenadas por
`position`, con sus `options`, `type`, `max_select`, `is_sensitive`, `conditional_rules`.
(Si la encuesta seed no está `live`, dejar documentado que el director debe setearla a `live`, o
leer por id — NO cambiar el estado desde el cliente.)
3. Renderizar cada tipo con su widget: `single_choice`→radios, `multiple_choice`→checkboxes (con tope
`max_select`), `rating`→escala 1-5 (usando el objeto options), `text_long`→textarea,
`text_short`→input de una línea (A1/A2/A3).
4. Motor de lógica condicional data-driven: evaluar `conditional_rules` para mostrar/ocultar; al
ocultar un bloque, limpiar sus valores.
5. Validación con zod (tipos correctos; respetar `max_select`; `required` según el campo `required`
de cada pregunta — hoy todas `false`, así que validación liviana).
6. 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`).
7. Resiliencia lite: persistir las respuestas en progreso en `localStorage` (clave por `survey_id`)
para sobrevivir un refresh; limpiar al enviar. No guardar nada identificable.
8. 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 dev` levanta 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 build` limpio; ninguna variable `NEXT_PUBLIC_` contiene el `service_role`.
- [ ] `git diff --name-only HEAD` solo 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 con `if` cableados por código).
- `max_select` de 6.2 se respeta en runtime (test o verificación manual reproducible).
- `npm run build` sin errores.
- Auditoría a11y (axe-core o `lighthouse`) sobre la página del formulario: **0 violaciones de nivel AA**.
- Grep de seguridad: `service_role` NO aparece en ningún archivo del cliente ni en `NEXT_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 HEAD`** pegado.
- 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`, `answers` ni `consent_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_role` en el cliente ni en variables `NEXT_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**.