# 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 34 preguntas 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 34 preguntas, 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` (F3, G4). Manejar las dos formas. - Condicionales: cada pregunta dependiente trae `conditional_rules` con `{if_question, op:'eq', value:'Sí', action:'show'}`. Mostrar solo si el disparador (B1/C1/D1) = "Sí"; con "No"/"Prefiero no decir" ocultar **y limpiar** las respuestas de ese bloque. - H1 = `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 34 `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. 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, `