Instrumento v2: 44 preguntas + widget text_short
This commit is contained in:
174
SEED_SPEC.md
174
SEED_SPEC.md
@@ -1,105 +1,119 @@
|
||||
# SEED_SPEC.md — Contrato del instrumento "Mapeo de cuidadores"
|
||||
# SEED_SPEC.md — Contrato del instrumento "Mapeo de Cuidadores" v2
|
||||
|
||||
> Inventario completo y autoritativo de la encuesta, extraído del mockup real
|
||||
> (`formulario_mapeo.html`). Reemplaza el seed inventado de la Etapa 1 (que tenía 9 de 34 preguntas y
|
||||
> el supuesto incorrecto C1→G4). Claude Code reescribe `seed.sql` a partir de ESTE contrato.
|
||||
> Versión 2 — incorpora revisión de la colega (Jun 2026). Reemplaza la v1 (34 preguntas).
|
||||
> 44 preguntas · 8 secciones · Códigos únicos corregidos.
|
||||
> Claude Code reescribe `seed.sql` a partir de ESTE contrato.
|
||||
|
||||
## Convenciones (decisiones cerradas)
|
||||
- **Mapeo de tipos al esquema:** radio→`single_choice`, checkbox→`multiple_choice`, escala 1-5→
|
||||
`rating`, textarea→`text_long`.
|
||||
- **`conditional_rules` viven en la pregunta DEPENDIENTE** (cada pregunta condicional declara su
|
||||
propia condición de visibilidad: `{if_question, op:'eq', value, action:'show'}`). El motor, al
|
||||
decidir si renderiza X, mira las reglas de X. (Resuelve la ambigüedad que el agente había dejado.)
|
||||
- **B1/C1/D1 son `single_choice` de 3 opciones** (Sí / No / Prefiero no decir), **NO `yes_no`**.
|
||||
Solo el valor **"Sí"** dispara el bloque condicional; "No" y "Prefiero no decir" lo ocultan.
|
||||
- **`is_sensitive = true`** en toda pregunta que elicita salud/discapacidad/diagnóstico (marcadas
|
||||
abajo). La encuesta entera es `data_sensitivity = 'sensible'`.
|
||||
## Convenciones
|
||||
- Tipos: radio→`single_choice`, checkbox→`multiple_choice`, escala 1-5→`rating`,
|
||||
texto libre largo→`text_long`, texto corto→`text_short`.
|
||||
- **`conditional_rules` en la pregunta DEPENDIENTE** (cada pregunta declara su propia condición).
|
||||
- Los 3 triggers (1.1, 1.6, 1.13) son `single_choice` de 3 opciones; solo **"Sí"** dispara el bloque.
|
||||
- `is_sensitive=true` en preguntas de salud/discapacidad/diagnóstico (marcadas ✅).
|
||||
- `3.4` (salary) — `is_sensitive=true` + analyst-only (ADR-001 addendum): nunca en vistas org_admin.
|
||||
- RE-INDEX: las preguntas marcadas son candidatas a índice compuesto (metodología TBD Sprint 3).
|
||||
|
||||
## Estructura condicional (3 bloques, el resto siempre visible)
|
||||
- **cond-B** — visible si `B1 = 'Sí'` → contiene B2, B3, B4, B5
|
||||
- **cond-C** — visible si `C1 = 'Sí'` → contiene C2, C3, C4, C5
|
||||
- **cond-D** — visible si `D1 = 'Sí'` → contiene D2, D3, D4, D5
|
||||
- Secciones A (datos generales), E (rol cuidador), F (económico), G (bienestar), H (necesidades):
|
||||
**top-level, siempre visibles.**
|
||||
## Estructura condicional (3 bloques)
|
||||
- **cond-disc-propia** — 1.1='Sí' → muestra 1.2, 1.3, 1.4, 1.5
|
||||
- **cond-familiar** — 1.6='Sí' → muestra 1.7, 1.8, 1.9, 1.10, 1.11, 1.12
|
||||
- **cond-adulto-mayor** — 1.13='Sí' → muestra 1.14, 1.15, 1.16
|
||||
- Secciones A, 2, 3, 4, 5, 6, cierre: **top-level, siempre visibles.**
|
||||
|
||||
---
|
||||
|
||||
## Sección 0 — Datos Generales (top-level, cuasi-identificadores)
|
||||
| Code | Tipo | Pregunta | Opciones |
|
||||
|---|---|---|---|
|
||||
| A1 | single_choice | ¿En qué área o gerencia trabajás? | Operaciones/Producción · Comercial/Ventas · Administración/Finanzas · RRHH/Cultura · Tecnología/Sistemas · Logística/Distribución · Otra área |
|
||||
| A2 | single_choice | ¿Cuántos años tenés? | Menos de 25 · 25-34 · 35-44 · 45-54 · 55+ · Prefiero no decir |
|
||||
| A3 | single_choice | ¿Con qué género te identificás? | Femenino · Masculino · No binario/Otro · Prefiero no decir |
|
||||
| A4 | single_choice | ¿Cuál es tu modalidad de trabajo actual? | Presencial · Remoto · Híbrido |
|
||||
## Sección A — Datos Generales (top-level)
|
||||
| Code | Tipo | Pregunta | Opciones | Notas |
|
||||
|---|---|---|---|---|
|
||||
| A1 | text_short | País* | — (texto libre) | qi_country (futuro) |
|
||||
| A2 | text_short | Ciudad* | — (texto libre) | qi_city (futuro) |
|
||||
| A3 | text_short | Sucursal o sede | — (texto libre) | opcional |
|
||||
| A4 | single_choice | ¿En qué área o gerencia trabajás?* | Operaciones/Producción · Comercial/Ventas · Administración/Finanzas · Recursos Humanos/Cultura · Tecnología/Sistemas · Logística/Distribución · Otra área | qi_sector |
|
||||
| A5 | single_choice | ¿Cuántos años tenés? | Menos de 25 años · 25 a 34 años · 35 a 44 años · 45 a 54 años · 55 años o más · Prefiero no decir | qi_age_bucket |
|
||||
| A6 | single_choice | ¿Con qué género te identificás? | Femenino · Masculino · No binario/Otro · Prefiero no decir | |
|
||||
| A7 | single_choice | ¿Cuál es tu modalidad de trabajo actual? | Presencial · Remoto (home office) · Híbrido | |
|
||||
|
||||
## Sección 1 — ¿Vivís con discapacidad? *(sensible)*
|
||||
## Sección 1 — Prevalencia: cuidado y discapacidad *(sensible)*
|
||||
| Code | Tipo | Cond. | Pregunta | Opciones | is_sensitive |
|
||||
|---|---|---|---|---|---|
|
||||
| B1 | single_choice | trigger | ¿Vos mismo/a tenés alguna discapacidad o condición de salud…? | Sí · No · Prefiero no decir | ✅ |
|
||||
| B2 | multiple_choice | B1=Sí | ¿Qué tipo de discapacidad tenés? (más de una) | Física/motriz · Visual · Auditiva · Intelectual · TEA · Psicosocial · Condición crónica · Otra | ✅ |
|
||||
| B3 | single_choice | B1=Sí | ¿Contás con certificado oficial de discapacidad? | Sí · En trámite · No | ✅ |
|
||||
| B4 | single_choice | B1=Sí | ¿Tu discapacidad afecta tu desempeño o comodidad en el trabajo? | Sí, significativamente · Sí, aspectos puntuales · No, bien gestionada · Prefiero no decir | ✅ |
|
||||
| B5 | single_choice | B1=Sí | ¿La empresa conoce tu situación? | Sí, con apoyo · Sí, sin apoyo · No, privado | ✅ |
|
||||
| 1.1 | single_choice | trigger | ¿Vos mismo/a vivís con alguna discapacidad o condición de salud que afecte tu funcionamiento cotidiano?* | Sí · No · Prefiero no decir | ✅ |
|
||||
| 1.2 | multiple_choice | 1.1=Sí | ¿Qué tipo de discapacidad o condición tenés? | Discapacidad física/motriz · Visual · Auditiva · Intelectual · TEA · Psicosocial/salud mental · Condición crónica que genera limitaciones · Otra | ✅ |
|
||||
| 1.3 | multiple_choice | 1.1=Sí | ¿Usás algún elemento de apoyo para la movilidad o autonomía? | Silla de ruedas · Bastón · Andador · Muletas · Prótesis · No utilizo | ✅ |
|
||||
| 1.4 | single_choice | 1.1=Sí | ¿Contás con certificado oficial de discapacidad? | Sí · No, pero estoy en trámite · No | ✅ |
|
||||
| 1.5 | single_choice | 1.1=Sí | ¿Tu condición afecta tu desempeño o comodidad en el trabajo? | Sí, de forma significativa · Sí, pero en aspectos puntuales · No, está bien gestionada · Prefiero no decir | ✅ |
|
||||
| 1.6 | single_choice | trigger | ¿Tenés algún familiar cercano (hijo/a, pareja, hermano/a, padre/madre) que viva con discapacidad?* | Sí · No · Prefiero no decir | ✅ |
|
||||
| 1.7 | multiple_choice | 1.6=Sí | ¿Cuál es tu vínculo con esa persona? | Hijo/a · Cónyuge/pareja · Padre o madre · Hermano/a · Otro familiar | ✅ |
|
||||
| 1.8 | multiple_choice | 1.6=Sí | ¿Qué tipo de discapacidad tiene? | Física/motriz · Visual · Auditiva · Intelectual · TEA · Psicosocial/salud mental · Condición crónica severa · Otra | ✅ |
|
||||
| 1.9 | multiple_choice | 1.6=Sí | ¿Qué tipo de apoyo o acompañamiento necesita principalmente? | Movilidad física · Comunicación · Apoyo escolar/educativo · Inclusión laboral · Terapias/rehabilitación · Apoyo emocional/psicosocial | ✅ |
|
||||
| 1.10 | multiple_choice | 1.6=Sí | ¿Usa algún elemento de apoyo para la movilidad o autonomía? | Silla de ruedas · Bastón · Andador · Muletas · Prótesis · No utiliza | ✅ |
|
||||
| 1.11 | single_choice | 1.6=Sí | Si es niño/a o adolescente en edad escolar: ¿está escolarizado/a y enfrenta barreras de aprendizaje? | Escolarizado/a, sin barreras importantes · Escolarizado/a, enfrenta barreras o faltan ajustes · No está escolarizado/a · No aplica | ✅ |
|
||||
| 1.12 | single_choice | 1.6=Sí | ¿La persona con discapacidad se encuentra trabajando actualmente? | Sí, está trabajando · No, y está buscando empleo · No, y no está buscando · No aplica | ✅ |
|
||||
| 1.13 | single_choice | trigger | ¿Tenés algún adulto mayor (60+ años) a tu cuidado o bajo tu responsabilidad?* | Sí · No · Prefiero no decir | |
|
||||
| 1.14 | single_choice | 1.13=Sí | ¿Qué nivel de dependencia tiene? (la más representativa) | Independiente, pero necesita supervisión · Dependencia leve · Dependencia moderada · Dependencia severa | ✅ |
|
||||
| 1.15 | multiple_choice | 1.13=Sí | ¿Tiene una condición de salud que genere mayor necesidad de cuidado? | Demencia/Alzheimer · ACV/Parálisis · Enfermedad crónica grave · Movilidad reducida · Deterioro cognitivo · Sin condición específica | ✅ |
|
||||
| 1.16 | single_choice | 1.13=Sí | ¿Vive con vos o en otra vivienda? | Vive en mi hogar · Vive en otra vivienda · Está en residencia/institución | |
|
||||
|
||||
## Sección 2 — Familiares con Discapacidad *(sensible)*
|
||||
| Code | Tipo | Cond. | Pregunta | Opciones | is_sensitive |
|
||||
|---|---|---|---|---|---|
|
||||
| C1 | single_choice | trigger | ¿Tenés algún familiar cercano que viva con discapacidad? | Sí · No · Prefiero no decir | ✅ |
|
||||
| C2 | multiple_choice | C1=Sí | ¿Cuál es el vínculo? (más de una) | Hijo/a · Cónyuge/pareja · Padre/madre · Hermano/a · Otro familiar | ✅ |
|
||||
| C3 | multiple_choice | C1=Sí | ¿Qué tipo de discapacidad tiene? (más de una) | Física/motriz · Visual · Auditiva · Intelectual · TEA · Psicosocial · Condición crónica | ✅ |
|
||||
| C4 | single_choice | C1=Sí | ¿Esta persona requiere apoyo o cuidado cotidiano? | Alta dependencia · Apoyo parcial · Autónomo/a | ✅ |
|
||||
| C5 | single_choice | C1=Sí | ¿Quién es el responsable principal del cuidado? | Yo principal · Compartido · Cuidador pagado · Institución | |
|
||||
|
||||
## Sección 3 — Adultos Mayores a Cargo *(sensible)*
|
||||
| Code | Tipo | Cond. | Pregunta | Opciones | is_sensitive |
|
||||
|---|---|---|---|---|---|
|
||||
| D1 | single_choice | trigger | ¿Tenés algún adulto mayor (60+) a tu cuidado? | Sí · No · Prefiero no decir | |
|
||||
| D2 | single_choice | D1=Sí | ¿Cuántos adultos mayores están bajo tu cuidado? | 1 · 2 · 3+ | |
|
||||
| D3 | single_choice | D1=Sí | ¿Qué nivel de dependencia tiene/n? | Supervisión · Dependencia leve · Dependencia moderada · Dependencia severa | ✅ |
|
||||
| D4 | multiple_choice | D1=Sí | ¿Tiene condición diagnosticada? (más de una) | Demencia/Alzheimer · ACV/Parálisis · Enf. crónica · Movilidad reducida · Deterioro cognitivo · Ninguna específica | ✅ |
|
||||
| D5 | single_choice | D1=Sí | ¿La persona vive con vos o en otra vivienda? | Vive conmigo · Otra vivienda · Institución | |
|
||||
|
||||
## Sección 4 — Tu Rol como Cuidador/a (top-level)
|
||||
## Sección 2 — Intensidad y rol del cuidado (top-level)
|
||||
| Code | Tipo | Pregunta | Opciones |
|
||||
|---|---|---|---|
|
||||
| E1 | single_choice | ¿Cuántas horas por día dedicás, en promedio, al cuidado directo? | Menos de 1h · 1-3h · 3-6h · Más de 6h · Variable |
|
||||
| E2 | multiple_choice | ¿En qué momentos realizás principalmente el cuidado? (más de una) | Antes del trabajo · Durante horario laboral · Al llegar · Durante la noche · Fines de semana · Continuo |
|
||||
| E3 | single_choice | ¿Contás con apoyo de otras personas para el cuidado? | Apoyo familiar suficiente · Apoyo insuficiente · Único responsable · Apoyo contratado |
|
||||
| E4 | single_choice | ¿Tuviste que faltar al trabajo por el cuidado en el último año? | Nunca · Pocas veces (1-3) · Varias veces (4-10) · Con frecuencia (10+) |
|
||||
| 2.1 | single_choice | ¿Quién es el/la responsable principal del cuidado? | Yo soy el/la principal · Comparto con otro familiar · Hay cuidador/a pagado/a · Está en institución/centro de día |
|
||||
| 2.2 | single_choice | ¿Cuántas horas por día dedicás, en promedio, al cuidado directo? | Menos de 1h · Entre 1 y 3h · Entre 3 y 6h · Más de 6h · Varía mucho |
|
||||
| 2.3 | multiple_choice | ¿En qué momentos del día cuidás principalmente? | Antes del trabajo · Durante horario laboral (llamadas/emergencias) · Al llegar del trabajo · Durante la noche · Los fines de semana · Es continuo sin horario fijo |
|
||||
| 2.4 | single_choice | ¿Contás con apoyo de otras personas para el cuidado? | Sí, apoyo familiar suficiente · Algo de apoyo, no suficiente · No, soy el/la único/a responsable · Tengo apoyo contratado |
|
||||
| 2.5 | single_choice | Como cuidador/a primario, ¿presentás signos de agotamiento o estrés crónico? | Sí, frecuentemente · A veces · No, lo llevo bien · Prefiero no decir |
|
||||
|
||||
## Sección 5 — Impacto Económico (top-level)
|
||||
## Sección 3 — Impacto económico del cuidado (top-level)
|
||||
| Code | Tipo | Pregunta | Opciones | Notas |
|
||||
|---|---|---|---|---|
|
||||
| 3.1 | single_choice | ¿Cuánto estimás que gastás por mes en el cuidado? | Sin gastos significativos · Menos de Gs. 500.000 · Gs. 500.000–1.500.000 · Gs. 1.500.000–3.000.000 · Gs. 3.000.000–6.000.000 · Más de Gs. 6.000.000 · Prefiero no decir | |
|
||||
| 3.2 | multiple_choice | ¿Qué servicios o apoyos utilizás actualmente? | Terapias especializadas · Cuidador/a particular · Centro de día/institución · Transporte adaptado · Medicación alto costo · Equipamiento médico/tecnología asistiva · No utilizo servicios formales | |
|
||||
| 3.3 | multiple_choice | ¿Cómo sostenés los gastos y responsabilidades del cuidado? | Con mi empleo de tiempo completo · Con mi empleo de tiempo parcial · Con trabajo independiente/emprendimiento · Me ayudan mis familiares · Es difícil sostenerlo con mis ingresos | |
|
||||
| 3.4 | single_choice | ¿En qué franja salarial mensual te ubicás? *(CONFIDENCIAL — analyst-only)* | Sueldo mínimo – Gs. 5.000.000 · Gs. 5.000.001–8.000.000 · Gs. 8.000.001–12.000.000 · Gs. 12.000.001–15.000.000 · Gs. 15.000.001–18.000.000 · Gs. 18.000.001–25.000.000 · Gs. 25.000.001–50.000.000 · Gs. 50.000.001 o más · Prefiero no decir | is_sensitive=true; never in org_admin views |
|
||||
| 3.5 | rating | ¿Los gastos de cuidado impactan en tu situación económica personal? | 1=Sin impacto · 2=Leve · 3=Moderado · 4=Alto · 5=Muy alto | RE-INDEX |
|
||||
|
||||
## Sección 4 — Tiempo y descanso (top-level)
|
||||
| Code | Tipo | Pregunta | Opciones |
|
||||
|---|---|---|---|
|
||||
| F1 | single_choice | ¿Cuánto estimás que gastás mensualmente en el cuidado? | Sin gastos significativos · Menos de 500k · 500k-1.5M · 1.5M-3M · 3M-6M · Más de 6M · Prefiero no decir |
|
||||
| F2 | multiple_choice | ¿Qué servicios o apoyos utilizás actualmente? (más de una) | Terapias especializadas · Cuidador particular · Centro de día · Transporte adaptado · Medicación alto costo · Tecnología asistiva · Apoyo ONG · Sin servicios formales |
|
||||
| F3 | rating | ¿Los gastos de cuidado impactan en tu situación económica personal? | Escala 1 (Sin impacto) → 5 (Muy alto) |
|
||||
| 4.1 | single_choice | ¿Cómo describirías tu nivel de descanso habitual? | Descanso bien, me siento recuperado/a · Descanso de forma irregular, pero lo manejo · Me cuesta descansar, me siento cansado/a con frecuencia · Descanso muy poco, el agotamiento afecta mi día a día | RE-INDEX |
|
||||
| 4.2 | multiple_choice | ¿Qué aspectos de tu vida se ven más afectados por tu rol de cuidador/a? | Tiempo libre y ocio · Sueño y descanso · Vida social y amistades · Vida en pareja/familiar · Salud física personal · Salud emocional/mental · Desarrollo profesional · Situación económica | |
|
||||
| 4.3 | rating | En una escala del 1 al 5, ¿cómo evaluás tu bienestar general en este momento? | 1=Muy mal · 2=Mal · 3=Regular · 4=Bien · 5=Muy bien | RE-INDEX |
|
||||
|
||||
## Sección 6 — Bienestar y Descanso (top-level)
|
||||
## Sección 5 — Impacto en el trabajo (top-level)
|
||||
| Code | Tipo | Pregunta | Opciones |
|
||||
|---|---|---|---|
|
||||
| G1 | single_choice | ¿Cómo describirías tu nivel de descanso habitual? | Descanso bien · Descanso irregular · Me cuesta descansar · Muy poco descanso |
|
||||
| G2 | single_choice | En los últimos 3 meses, ¿con qué frecuencia el cuidado afectó tu concentración o rendimiento laboral? | Nunca/casi nunca · Algunas veces al mes · Varias veces por semana · Casi todos los días |
|
||||
| G3 | multiple_choice | ¿Qué aspectos de tu vida se ven más afectados? (más de una) | Tiempo libre · Sueño y descanso · Vida social · Vida en pareja/familia · Salud física · Salud emocional · Desarrollo profesional · Situación económica |
|
||||
| G4 | rating | ¿Cómo evaluás tu bienestar general en este momento? | Escala 1 (Muy mal) → 5 (Muy bien) |
|
||||
| G5 | single_choice | ¿Sentís que tu empresa comprende la realidad de ser cuidador/a? | Sí, acompañado/a · Parcialmente · No, invisible · Nunca lo supo |
|
||||
| 5.1 | single_choice | En los últimos 3 meses, ¿con qué frecuencia sentiste que el cuidado afectó tu concentración o rendimiento? | Nunca o casi nunca · Algunas veces al mes · Varias veces por semana · Casi todos los días | RE-INDEX |
|
||||
| 5.2 | single_choice | En el último año, ¿faltaste al trabajo por situaciones relacionadas al cuidado? | Nunca · Pocas veces (1–3 veces al año) · Varias veces (4–10 veces al año) · Con frecuencia (más de 10 veces al año) | |
|
||||
|
||||
## Sección 7 — Necesidades (top-level)
|
||||
| Code | Tipo | Pregunta | Opciones / nota |
|
||||
## Sección 6 — Conciencia organizacional (top-level)
|
||||
| Code | Tipo | Pregunta | Opciones | Notas |
|
||||
|---|---|---|---|---|
|
||||
| 6.1 | single_choice | ¿Sentís que tu empresa conoce o comprende la realidad del cuidado y la discapacidad de su gente?* | Sí, me siento acompañado/a · Parcialmente, hay cierta comprensión · No, es algo bastante invisible · No, nunca se ha hablado del tema | RE-INDEX |
|
||||
| 6.2 | multiple_choice (**max_select=3**) | ¿Qué apoyos de la empresa te ayudarían más? Elegí hasta 3 | Flexibilidad horaria o trabajo remoto · Apoyo económico o convenios/descuentos · Acceso a terapias o salud · Contención/apoyo emocional o psicológico · Capacitación y autocuidado · Redes de apoyo/grupos de pares · Información sobre recursos y servicios disponibles | ⚠️ Ranking → multiple_choice para v1. Ranking widget al backlog. |
|
||||
| 6.3 | single_choice | ¿Estarías dispuesto/a a participar en un grupo de apoyo para cuidadores dentro de la empresa? | Sí, definitivamente · Tal vez, si hay más personas en la misma situación · No por ahora · Prefiero mantenerlo en privado | |
|
||||
| 6.4 | single_choice | ¿Conocés a algún compañero/a de trabajo que esté al cuidado de una persona con discapacidad o dependiente? | Sí, conozco a varias personas · Sí, conozco a una persona · No conozco a nadie · No estoy seguro/a | Código corregido (era 6.2 duplicado) |
|
||||
| 6.5 | single_choice | ¿Considerás que la empresa debería ofrecer apoyos a quienes cuidan a una persona dependiente? | Sí, definitivamente · Tal vez, dependería del caso · No lo considero necesario · No lo sé | Código corregido (era 6.3 duplicado) |
|
||||
|
||||
## Sección 7 — Para cerrar (top-level)
|
||||
| Code | Tipo | Pregunta | Notas |
|
||||
|---|---|---|---|
|
||||
| H1 | multiple_choice (**max_select=3**) | ¿Qué tipo de apoyo de tu empresa te ayudaría más? (hasta 3) | Flexibilidad horaria/remoto · Info recursos y servicios · Apoyo psicológico · Espacio de escucha · Convenios descuentos · Grupos de pares · Capacitación autocuidado · Nada, estoy bien |
|
||||
| H2 | single_choice | ¿Estarías dispuesto/a a participar en un grupo de apoyo para cuidadores? | Sí, definitivamente · Tal vez · No por ahora · Prefiero privacidad |
|
||||
| H3 | text_long | ¿Querés compartir algo más sobre tu experiencia? (opcional) | Texto libre, opcional. **No entra al store analítico crudo** (regla 4 CLAUDE.md). |
|
||||
| 7.1 | text_long | ¿Te gustaría dejar un comentario o sugerencia especial? (opcional) | No entra al store analítico crudo (regla 4 CLAUDE.md) |
|
||||
|
||||
---
|
||||
|
||||
## Mapeo a cuasi-identificadores (`responses.qi_*`)
|
||||
- `qi_age_bucket` ← **A2** (ya viene en bucket — bien).
|
||||
- `qi_sector` ← **A1** (área).
|
||||
- `qi_zone` ← **no aplica en este instrumento** (no se pregunta zona geográfica; es intra-empresa). Va NULL.
|
||||
- `qi_age_bucket` ← A5 (ya en bucket)
|
||||
- `qi_sector` ← A4 (área)
|
||||
- `qi_zone` ← NULL (no aplica en este instrumento)
|
||||
- `qi_city` / `qi_country` ← A2/A1: **Sprint 3** — se agregan como columnas qi en migration futura.
|
||||
Por ahora A1/A2/A3 se capturan en `answers` como text_short.
|
||||
|
||||
## ⚠️ Hallazgo para el director (decisión de modelo de datos, Nivel 3 — NO ejecutar)
|
||||
El dashboard original quería filtrar por **género (A3)** y **modalidad (A4)**, pero `agg_segment`
|
||||
solo tiene en su whitelist `qi_age_bucket / qi_sector / qi_zone`. Para segmentar/filtrar por género o
|
||||
modalidad en el dashboard (Sprint 3), hay que decidir: ¿se agregan columnas `qi_gender` / `qi_modality`
|
||||
(y a la whitelist de `agg_segment`), o se resuelven por otra vía? Es decisión de esquema → consultar
|
||||
antes de Sprint 3, no resolver en el seed.
|
||||
## ⚠️ Decisiones pendientes para el director (NO ejecutar sin aprobación)
|
||||
- **Ranking widget (6.2):** actual es multiple_choice max_select=3. Cuando se implemente el
|
||||
widget de ranking (drag o 3 dropdowns), 6.2 pasa a tipo `ranking` con JSONB `{rank1, rank2, rank3}`.
|
||||
- **qi_city / qi_country:** migration 008 para agregar columnas y extender whitelist de agg_segment.
|
||||
Se hace antes de Sprint 3 (dashboard).
|
||||
- **Salary analytics:** metodología para calcular "carga económica del cuidado como % del ingreso"
|
||||
es trabajo de metodología de investigación (fuera del scope del código).
|
||||
- **RE-INDEX scoring:** algoritmo de composición de los índices (descanso, bienestar, rendimiento,
|
||||
conciencia) — decisión antes de construir los reportes del dashboard.
|
||||
- **Formulario de empresa (Relevamiento Empresa):** instrumento separado con PII de contacto.
|
||||
Ejecutar offline para las primeras empresas. Diseño del modelo de datos en el sprint del builder.
|
||||
@@ -254,6 +254,26 @@ body {
|
||||
.rating-step:hover .rating-dot { border-color: var(--color-accent); background: var(--color-primary-light); }
|
||||
.rating-step:has(input:checked):hover .rating-dot { background: var(--color-primary-hover); border-color: var(--color-primary-hover); color: #fff; }
|
||||
|
||||
/* ── Text short (single-line input) ── */
|
||||
.text-short-input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
border: 1.5px solid var(--color-border);
|
||||
border-radius: var(--radius);
|
||||
font-size: 1rem;
|
||||
font-family: inherit;
|
||||
line-height: 1.5;
|
||||
transition: border-color 0.15s;
|
||||
color: var(--color-text);
|
||||
background: var(--color-surface);
|
||||
}
|
||||
.text-short-input:focus {
|
||||
outline: none;
|
||||
border-color: var(--color-border-focus);
|
||||
box-shadow: 0 0 0 3px rgba(37,99,235,0.15);
|
||||
}
|
||||
|
||||
/* ── Textarea ── */
|
||||
.text-area {
|
||||
display: block;
|
||||
|
||||
@@ -4,6 +4,7 @@ import { RadioGroup } from './widgets/radio-group'
|
||||
import { CheckboxGroup } from './widgets/checkbox-group'
|
||||
import { RatingScale } from './widgets/rating-scale'
|
||||
import { TextAreaField } from './widgets/text-area-field'
|
||||
import { TextShortField } from './widgets/text-short-field'
|
||||
|
||||
interface QuestionRendererProps {
|
||||
question: Question
|
||||
@@ -72,7 +73,20 @@ export function QuestionRenderer({
|
||||
)
|
||||
}
|
||||
|
||||
if (type === 'text_long' || type === 'text_short') {
|
||||
if (type === 'text_short') {
|
||||
return (
|
||||
<TextShortField
|
||||
name={code}
|
||||
label={prompt}
|
||||
value={(answers[code] as string) ?? ''}
|
||||
required={required}
|
||||
onChange={(v) => onChange(code, v)}
|
||||
error={error}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
if (type === 'text_long') {
|
||||
return (
|
||||
<TextAreaField
|
||||
name={code}
|
||||
|
||||
@@ -35,16 +35,22 @@ function formatAnswer(question: Question, raw: FormAnswers[string]): string {
|
||||
return String(raw)
|
||||
}
|
||||
|
||||
// Section titles by first question code prefix
|
||||
// Section titles by code prefix ('A', '1', '2', …)
|
||||
const SECTION_TITLES: Record<string, string> = {
|
||||
A: 'Datos Generales',
|
||||
B: '¿Vivís con discapacidad?',
|
||||
C: 'Familiares con Discapacidad',
|
||||
D: 'Adultos Mayores a Cargo',
|
||||
E: 'Tu Rol como Cuidador/a',
|
||||
F: 'Impacto Económico',
|
||||
G: 'Bienestar y Descanso',
|
||||
H: 'Necesidades',
|
||||
'1': 'Prevalencia: cuidado y discapacidad',
|
||||
'2': 'Intensidad y rol del cuidado',
|
||||
'3': 'Impacto económico del cuidado',
|
||||
'4': 'Tiempo y descanso',
|
||||
'5': 'Impacto en el trabajo',
|
||||
'6': 'Conciencia organizacional',
|
||||
'7': 'Para cerrar',
|
||||
}
|
||||
|
||||
// 'A1' → 'A', '1.1' → '1', '6.4' → '6', '7.1' → '7'
|
||||
function getSectionPrefix(code: string): string {
|
||||
const m = code.match(/^([A-Za-z]+|\d+)/)
|
||||
return m ? m[1] : code
|
||||
}
|
||||
|
||||
export function SummaryScreen({
|
||||
@@ -72,7 +78,7 @@ export function SummaryScreen({
|
||||
|
||||
<dl className="summary-list">
|
||||
{visibleQuestions.map((q) => {
|
||||
const section = q.code.replace(/\d+$/, '')
|
||||
const section = getSectionPrefix(q.code)
|
||||
const showHeader = section !== lastSection
|
||||
lastSection = section
|
||||
|
||||
|
||||
@@ -8,19 +8,23 @@ import { QuestionRenderer } from './question-renderer'
|
||||
import { SummaryScreen } from './summary-screen'
|
||||
|
||||
// Section metadata (code prefix → display title)
|
||||
// Prefixes: 'A' for A1-A7, '1' for 1.1-1.16, '2' for 2.1-2.5, etc.
|
||||
const SECTIONS: { prefix: string; title: string; sensitive?: boolean }[] = [
|
||||
{ prefix: 'A', title: 'Datos Generales' },
|
||||
{ prefix: 'B', title: '¿Vivís con discapacidad?', sensitive: true },
|
||||
{ prefix: 'C', title: 'Familiares con Discapacidad', sensitive: true },
|
||||
{ prefix: 'D', title: 'Adultos Mayores a Cargo', sensitive: true },
|
||||
{ prefix: 'E', title: 'Tu Rol como Cuidador/a' },
|
||||
{ prefix: 'F', title: 'Impacto Económico' },
|
||||
{ prefix: 'G', title: 'Bienestar y Descanso' },
|
||||
{ prefix: 'H', title: 'Necesidades' },
|
||||
{ prefix: '1', title: 'Prevalencia: cuidado y discapacidad', sensitive: true },
|
||||
{ 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) {
|
||||
return code.replace(/\d+$/, '')
|
||||
// Extracts the leading alpha or numeric segment from a question code.
|
||||
// 'A1' → 'A', '1.1' → '1', '1.16' → '1', '6.4' → '6', '7.1' → '7'
|
||||
function getSectionPrefix(code: string): string {
|
||||
const m = code.match(/^([A-Za-z]+|\d+)/)
|
||||
return m ? m[1] : code
|
||||
}
|
||||
|
||||
interface SurveyFormProps {
|
||||
|
||||
50
components/widgets/text-short-field.tsx
Normal file
50
components/widgets/text-short-field.tsx
Normal file
@@ -0,0 +1,50 @@
|
||||
'use client'
|
||||
|
||||
interface TextShortFieldProps {
|
||||
name: string
|
||||
label: string
|
||||
value: string
|
||||
required: boolean
|
||||
placeholder?: string
|
||||
onChange: (value: string) => void
|
||||
error?: string
|
||||
}
|
||||
|
||||
export function TextShortField({
|
||||
name,
|
||||
label,
|
||||
value,
|
||||
required,
|
||||
placeholder = '',
|
||||
onChange,
|
||||
error,
|
||||
}: TextShortFieldProps) {
|
||||
return (
|
||||
<div className="question-fieldset">
|
||||
<label className="question-legend" htmlFor={name}>
|
||||
{label}
|
||||
{required && <span className="required-mark" aria-hidden="true"> *</span>}
|
||||
{required && <span className="sr-only"> (obligatorio)</span>}
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
id={name}
|
||||
name={name}
|
||||
value={value}
|
||||
required={required}
|
||||
aria-required={required}
|
||||
aria-describedby={error ? `${name}-error` : undefined}
|
||||
aria-invalid={!!error}
|
||||
placeholder={placeholder}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="text-short-input"
|
||||
autoComplete="off"
|
||||
/>
|
||||
{error && (
|
||||
<p role="alert" className="field-error" id={`${name}-error`}>
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -84,6 +84,19 @@ compartidos; la inspección fila-por-fila es solo análisis interno (Modo A).
|
||||
Las preguntas abiertas no se exponen crudas a reportes (vector de re-identificación). Se categorizan
|
||||
antes (a futuro, vía LLM — está en BACKLOG).
|
||||
|
||||
### 11. Salary range — dato de acceso analyst-only (addendum Jun 2026)
|
||||
La pregunta `3.4` captura la franja salarial del respondente. Es el dato clave para calcular la
|
||||
"carga económica del cuidado como % del ingreso" — núcleo del argumento de la economía del cuidado.
|
||||
Decisión del equipo:
|
||||
- Se recolecta y almacena en `answers.value` como cualquier respuesta. `is_sensitive=true`.
|
||||
- Acceso: **exclusivamente rol analyst** (vía `service_role`, que bypassa RLS). Nunca expuesto
|
||||
a org_admin — ni en vistas crudas ni en funciones de agregación accesibles a ese rol.
|
||||
- Ninguna llamada a `agg_segment` ni ningún reporte de org_admin incluye salary como dimensión
|
||||
o como valor. El GRANT existente ya lo bloquea (org_admin tiene 0 filas en `answers`).
|
||||
- Protección "técnicamente casi imposible": RLS bloquea el path de org_admin + cifrado en reposo
|
||||
de Supabase + audit logs de accesos. Riesgo de breach externo es categoría separada
|
||||
(plan de respuesta a incidentes, fuera del scope de este ADR).
|
||||
|
||||
## Alcance de esta etapa (v1)
|
||||
Solo **Modo A**, solo encuestas **anónimas**, **sin Nivel 3 atribuible**. El esquema soporta más
|
||||
(controller_role, respondent_id) pero v1 no lo opera. Principio: el esquema guarda la opción, estos
|
||||
|
||||
75
sprints/sprint-1/ETAPA_1D_PROMPT.md
Normal file
75
sprints/sprint-1/ETAPA_1D_PROMPT.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# ETAPA 1D — Actualización del instrumento a v2 (44 preguntas)
|
||||
|
||||
> Implementa la revisión del instrumento hecha por la colega del equipo.
|
||||
> Leé: `CLAUDE.md`, `DATA_MODEL.md`, `SEED_SPEC.md` (v2) y este prompt.
|
||||
|
||||
## 1. Objetivo
|
||||
Actualizar el seed y la app para que reflejen el instrumento v2 de 44 preguntas según `SEED_SPEC.md`.
|
||||
Dos partes: (A) base de datos en Supabase Cloud, (B) widget `text_short` en el formulario Next.js.
|
||||
|
||||
## 2. Contexto previo
|
||||
La Etapa 1C cerró la fundación de datos con 34 preguntas. Esta etapa la actualiza a 44 por decisión
|
||||
del equipo. El esquema de tablas NO cambia — solo cambia el contenido del seed. Las columnas `qi_city`
|
||||
y `qi_country` se agregan en un sprint futuro (no en esta etapa).
|
||||
|
||||
## 3. Decisiones ya tomadas
|
||||
- 44 preguntas con códigos únicos según `SEED_SPEC.md`. Códigos corregidos: 6.4 y 6.5 (las
|
||||
que en el HTML original eran 6.2 y 6.3 duplicados).
|
||||
- A1/A2/A3 son tipo `text_short` (texto libre corto), sin opciones. Se guardan en `answers`.
|
||||
- Pregunta 6.2 = `multiple_choice max_select=3` (NO ranking todavía).
|
||||
- Pregunta 3.4 (salary) = `is_sensitive=true`. Ninguna vista de org_admin la expone.
|
||||
- Condicionales: cond-disc-propia (1.1=Sí → 1.2–1.5), cond-familiar (1.6=Sí → 1.7–1.12),
|
||||
cond-adulto-mayor (1.13=Sí → 1.14–1.16). Reglas en la pregunta dependiente.
|
||||
- Secciones A, 2, 3, 4, 5, 6, 7 son top-level (siempre visibles).
|
||||
- RE-INDEX es solo una nota en los textos de `SEED_SPEC.md`; NO agrega columna al esquema.
|
||||
|
||||
## 4. Tareas
|
||||
|
||||
### 4A — Base de datos
|
||||
1. Reescribir `supabase/seed.sql` con las 44 preguntas de `SEED_SPEC.md`: tipos, opciones,
|
||||
`max_select` (6.2=3), `is_sensitive` (marcar las indicadas ✅), `conditional_rules` en
|
||||
dependientes, `required=false` para todas (el equipo decidió capturar todas las respuestas
|
||||
para validar el concepto, la obligatoriedad real se define luego).
|
||||
2. Aplicar a Cloud: `supabase db push --include-seed` o equivalente.
|
||||
3. Verificar: `SELECT count(*) FROM questions` = **44**; códigos únicos sin duplicados;
|
||||
pregunta `3.4` con `is_sensitive=true`; los 3 bloques condicionales tienen las reglas correctas.
|
||||
|
||||
### 4B — App (formulario Next.js)
|
||||
4. En `components/widgets/`, crear `text-short-field.tsx`: un `<input type="text">` accesible
|
||||
(label asociado, aria-required, aria-invalid) con el mismo estilo visual que los otros widgets.
|
||||
5. En `components/question-renderer.tsx`: registrar `text_short` y renderizarlo con el nuevo widget.
|
||||
6. Verificar que A1/A2/A3 se renderizan como campos de texto, y que el formulario sigue
|
||||
funcionando end-to-end (cargar las 44 preguntas desde Supabase, condicionales B/C/D, envío
|
||||
al resumen).
|
||||
|
||||
## 5. Definition of Done
|
||||
- [ ] `SELECT count(*) FROM questions` = 44 contra Cloud.
|
||||
- [ ] Códigos únicos: `SELECT code, count(*) FROM questions GROUP BY code HAVING count(*) > 1` = vacío.
|
||||
- [ ] `3.4` tiene `is_sensitive=true`.
|
||||
- [ ] Pregunta `A1` con `type='text_short'` y `options=null`.
|
||||
- [ ] `npm run build` limpio.
|
||||
- [ ] El formulario en `http://localhost:3000` muestra A1/A2/A3 como campos de texto.
|
||||
- [ ] B1=Sí despliega 1.2–1.5; C1=Sí despliega 1.7–1.12; D1=Sí despliega 1.14–1.16.
|
||||
- [ ] `git diff --name-only HEAD` solo con `supabase/seed.sql` + archivos de la app.
|
||||
|
||||
## 6. Validaciones automatizadas mandatorias
|
||||
- Conteo de preguntas: 44 ✓
|
||||
- Sin códigos duplicados (query arriba) ✓
|
||||
- `is_sensitive` en exactamente las preguntas marcadas en SEED_SPEC.md ✓
|
||||
- Al menos una conditional_rule en cada una de las 16 preguntas dependientes ✓
|
||||
- `npm run build` sin errores ✓
|
||||
- Si CUALQUIERA falla: NO declarar lista. Reportar y esperar.
|
||||
|
||||
## 7. Qué reportar
|
||||
- Conteo de preguntas y confirmación de código únicos.
|
||||
- Confirmación de que A1/A2 se renderizan correctamente en el formulario.
|
||||
- Salida de `git diff --name-only HEAD`.
|
||||
- Hallazgos, asumidos, decisiones por iniciativa (con nivel).
|
||||
|
||||
## 8. Qué NO hacer
|
||||
- NO agregar columnas `qi_city`/`qi_country` al esquema (es Sprint 3).
|
||||
- NO implementar widget de ranking (está en backlog).
|
||||
- NO tocar migraciones 001–007.
|
||||
- NO cambiar la lógica de RLS ni los policies.
|
||||
- NO exponer `3.4` en ninguna vista o función que sea accesible a org_admin.
|
||||
- Ante conflicto restricción vs build limpio: **detenerse y reportar**.
|
||||
@@ -17,8 +17,9 @@ distintos y conviene auditarlos por separado:
|
||||
|
||||
## Alcance
|
||||
- **Etapa 2A — Motor del formulario:** lee la encuesta live desde Supabase (anon key), renderiza las
|
||||
34 preguntas con el widget correcto por tipo, ejecuta la lógica condicional, valida, mobile-first,
|
||||
WCAG AA, resiliencia lite. **Al enviar muestra un resumen; NO escribe en la base.**
|
||||
**44 preguntas** (instrumento v2, Jun 2026) con el widget correcto por tipo (`single_choice`,
|
||||
`multiple_choice`, `rating`, `text_long`, `text_short`), ejecuta la lógica condicional, valida,
|
||||
mobile-first, WCAG AA, resiliencia lite. **Al enviar muestra un resumen; NO escribe en la base.**
|
||||
- **Etapa 2B — Consentimiento + persistencia:** pantalla de consentimiento granular versionado
|
||||
(ADR-003) con texto honesto sobre anonimato y no-revocación, que crea el `consent_record` y recién
|
||||
ahí escribe `response` + `answers`, con dedup por `ip_hash`.
|
||||
@@ -34,15 +35,17 @@ distintos y conviene auditarlos por separado:
|
||||
- El formulario corre como **`anon`** (sin login). `service_role` NUNCA en el cliente (regla 9).
|
||||
- El respondente nunca se ata a un IdP (regla 10 / ADR-004).
|
||||
- `options`: array para `single_choice`/`multiple_choice`, objeto `{min,max,min_label,max_label}` para `rating`.
|
||||
- Condicionales en la pregunta dependiente; disparador value `'Sí'` (B1/C1/D1).
|
||||
- Condicionales en la pregunta dependiente; disparador value `'Sí'` (triggers: 1.1/1.6/1.13).
|
||||
- Tipos de widget: `single_choice`→radio, `multiple_choice`→checkbox, `rating`→escala 1-5,
|
||||
`text_long`→textarea, `text_short`→input de texto de una línea (A1/A2/A3).
|
||||
- WCAG 2.2 AA es pilar (el sujeto es la discapacidad), no checkbox.
|
||||
- El mockup `formulario_mapeo.html` es el **contrato visual** (look y flujo ya aprobados); 2A lo
|
||||
replica pero data-driven desde el esquema, no hardcodeado.
|
||||
|
||||
## Criterios de éxito (Etapa 2A)
|
||||
- [ ] Renderiza las 34 preguntas leídas desde Supabase (no hardcodeadas).
|
||||
- [ ] Lógica condicional: B1/C1/D1 = "Sí" despliega sus bloques; "No"/"Prefiero no decir" los oculta y limpia.
|
||||
- [ ] H1 limita a 3 selecciones; `rating` (F3/G4) maneja el formato objeto.
|
||||
- [ ] Renderiza las **44 preguntas** leídas desde Supabase (no hardcodeadas).
|
||||
- [ ] Lógica condicional: 1.1/1.6/1.13 = "Sí" despliegan sus bloques; "No"/"Prefiero no decir" ocultan y limpian.
|
||||
- [ ] 6.2 limita a 3 selecciones; `rating` (3.5/4.3) maneja formato objeto; A1/A2/A3 son `text_short`.
|
||||
- [ ] Mobile-first fluido + navegable solo con teclado + WCAG AA base.
|
||||
- [ ] Refresh no pierde lo cargado (resiliencia lite).
|
||||
- [ ] Enviar muestra resumen; no escribe en la base.
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
|
||||
## 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,
|
||||
`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 34
|
||||
preguntas, k-anonimato. El proyecto Supabase está linkeado. Todavía no existe app frontend — esta
|
||||
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.
|
||||
@@ -23,22 +23,25 @@ del proyecto) es el contrato visual: replicar su look y flujo, pero leyendo del
|
||||
`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.
|
||||
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'}`. 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).
|
||||
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_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
|
||||
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.
|
||||
`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`
|
||||
@@ -54,17 +57,17 @@ del proyecto) es el contrato visual: replicar su look y flujo, pero leyendo del
|
||||
|
||||
## 5. Definition of Done
|
||||
- [ ] `npm run dev` levanta la app y carga la encuesta desde Supabase (anon).
|
||||
- [ ] Se renderizan las 34 preguntas; los 3 bloques condicionales aparecen/desaparecen con B1/C1/D1.
|
||||
- [ ] H1 no deja marcar más de 3; rating funciona con el formato objeto.
|
||||
- [ ] 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 **34** preguntas desde la base (logear el conteo), no un número hardcodeado.
|
||||
- Las 12 condicionales se evalúan desde `conditional_rules` (no con `if` cableados por código).
|
||||
- `max_select` de H1 se respeta en runtime (test o verificación manual reproducible).
|
||||
- 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_*`.
|
||||
|
||||
1002
supabase/seed.sql
1002
supabase/seed.sql
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user