Los triggers 1.1, 1.6, 1.13 muestran solo Sí · No (sin "Prefiero no decir")

Al responder "No" en las tres, el formulario salta a 6.4 (no muestra secciones 2-5 ni 6.1-6.3)
La pregunta 6.6 aparece antes del cierre
Las preguntas multiple_choice muestran el hint "Podés marcar más de una opción"
This commit is contained in:
markosbenitez
2026-06-10 15:49:05 -03:00
parent bf963e8894
commit 157e108322
15 changed files with 1096 additions and 161 deletions

View File

@@ -0,0 +1,174 @@
# ETAPA 3I2 — Revisión del instrumento v2.1 (feedback equipo médico)
## 1. Objetivo
Aplicar 10 cambios al instrumento "Mapeo de Cuidadores" resultado del feedback del equipo
de salud. Los cambios afectan SEED_SPEC.md, seed.sql, y el código del formulario Next.js.
---
## 2. Contexto previo
- Instrumento actual: v2, 44 preguntas, 8 secciones, en `SEED_SPEC.md`.
- La DB tiene al menos 1 respuesta real. Algunos cambios son breaking para datos existentes
(opciones renombradas, tipo cambiado) — documentar en OBSERVATIONS.md, no bloquear.
- El formulario Next.js usa un sistema de conditional_rules en el JSON de cada pregunta.
- Los 3 triggers (1.1, 1.6, 1.13) controlan bloques condicionales enteros.
---
## 3. DECISIONES YA TOMADAS — no consultar de nuevo
- Cambio 4: el salto a 6.4 ocurre cuando las TRES preguntas 1.1, 1.6, 1.13 dan "No"
simultáneamente. Si al menos una da "Sí", las secciones 2-5 y 6.1-6.3 se muestran normalmente.
- Cambio 4: las secciones 2, 3, 4, 5, 6.1, 6.2, 6.3 se saltean completamente — ir directo a 6.4.
- Cambio 5: 2.1 pasa a multiple_choice. Es breaking para datos existentes — documentar, no bloquear.
- Cambio 6: 2.4 se elimina del seed. Si hay respuestas con question_id de 2.4, se pierden
al re-seedear. Documentar en OBSERVATIONS.md.
- Cambio 4: pregunta 6.6 opciones: Sí · No · Tal vez.
- Cambio 3: "Tecnología asistiva" va en penúltima posición (antes de "No utilizo"/"No utiliza").
- "Prefiero no decir" se elimina de TODAS las preguntas que lo tengan.
---
## 4. Los 10 cambios — especificación exacta
### Cambio 1 — Eliminar "Prefiero no decir" de todo el instrumento
Preguntas afectadas: A5, A6, 1.1, 1.5, 1.6, 1.13, 2.5, 3.1, 3.4.
En cada una, eliminar la opción "Prefiero no decir" del array de opciones en seed.sql.
En SEED_SPEC.md, eliminarla de la columna Opciones de cada pregunta.
⚠️ Los triggers 1.1, 1.6, 1.13 quedan con solo 2 opciones: Sí · No.
La lógica condicional no cambia — solo "Sí" dispara el bloque.
### Cambio 2 — Subtítulo hint en todas las preguntas multiple_choice
En el componente del formulario que renderiza preguntas de tipo `multiple_choice`,
agregar un subtítulo/hint debajo del prompt: "Podés marcar más de una opción".
Preguntas afectadas: 1.2, 1.3, 1.7, 1.8, 1.9, 1.10, 1.15, 2.3, 3.2, 3.3, 4.2, 6.2.
Es un cambio de UI — no afecta seed.sql ni SEED_SPEC.md.
### Cambio 3 — Agregar "Tecnología asistiva" en 1.3 y 1.10
1.3 opciones actuales: Silla de ruedas · Bastón · Andador · Muletas · Prótesis · No utilizo
1.3 opciones nuevas: Silla de ruedas · Bastón · Andador · Muletas · Prótesis · Tecnología asistiva · No utilizo
1.10 opciones actuales: Silla de ruedas · Bastón · Andador · Muletas · Prótesis · No utiliza
1.10 opciones nuevas: Silla de ruedas · Bastón · Andador · Muletas · Prótesis · Tecnología asistiva · No utiliza
### Cambio 4 — Salto condicional triple + nueva pregunta 6.6
#### 4a. Lógica de salto
Cuando las tres responden "No": 1.1="No" AND 1.6="No" AND 1.13="No",
las secciones 2, 3, 4, 5 completas + preguntas 6.1, 6.2, 6.3 se ocultan.
El formulario salta directamente a 6.4.
Implementación sugerida: agregar una regla condicional en las preguntas de las secciones
2-5 y 6.1-6.3 que las oculte cuando se cumple esa condición triple. La forma exacta de
implementarlo (conditional_rules en cada pregunta vs. lógica en el componente de sección)
la decide Claude Code según la arquitectura existente del formulario.
#### 4b. Nueva pregunta 6.6
Agregar al final de la Sección 6 (después de 6.5, antes de Sección 7):
- Code: 6.6
- Tipo: single_choice
- Pregunta: "¿Estarías dispuesto/a a ser voluntario/a en iniciativas y/o redes de apoyo
a personas con discapacidad y/o cuidadores?"
- Opciones: Sí · No · Tal vez
- is_sensitive: false
- top-level (siempre visible)
Actualizar position de 7.1 para que quede después de 6.6.
Actualizar el conteo total de preguntas en SEED_SPEC.md (pasa de 44 a 45, o 44 si se
compensa con la eliminación de 2.4 — verificar el conteo final).
### Cambio 5 — 2.1 pasa a multiple_choice
Cambiar tipo de `single_choice` a `multiple_choice` en seed.sql y SEED_SPEC.md.
Agregar nota en SEED_SPEC.md:
"⚠️ TECH-DEBT: múltiple selección sin orden de prioridad. Deuda: implementar ranking
(Principal cuidador soy yo > secundario instituto > principal pagado > secundario familiar)."
### Cambio 6 — Eliminar 2.4
Eliminar la pregunta 2.4 ("¿Contás con apoyo de otras personas para el cuidado?")
del seed.sql y SEED_SPEC.md.
Renumerar positions de las preguntas posteriores si es necesario.
Documentar en OBSERVATIONS.md: "2.4 eliminada por feedback equipo médico (similar a 2.1)."
### Cambio 7 — En 3.1 eliminar "Sin gastos significativos"
El cambio 1 ya elimina "Prefiero no decir" de 3.1.
Adicionalmente, eliminar "Sin gastos significativos" de las opciones de 3.1.
3.1 opciones finales: Menos de Gs. 500.000 · Gs. 500.0001.500.000 · Gs. 1.500.0003.000.000
· Gs. 3.000.0006.000.000 · Más de Gs. 6.000.000
### Cambio 8 — En 3.3 cambiar texto de opción
Cambiar: "Es difícil sostenerlo con mis ingresos"
Por: "No puedo sostenerlo con mis ingresos"
En seed.sql y SEED_SPEC.md.
Documentar en OBSERVATIONS.md: "Respuestas existentes con el texto anterior quedan
con valor 'Es difícil sostenerlo con mis ingresos' — breaking change en datos existentes."
### Cambio 9 — En 3.4 reestructurar primera franja salarial
Opción actual: "Sueldo mínimo Gs. 5.000.000"
Reemplazar por dos opciones:
1. "Sueldo mínimo"
2. "Gs. 3.000.000 a 5.000.000"
Las demás franjas no cambian.
Documentar en OBSERVATIONS.md: "Respuestas existentes con 'Sueldo mínimo Gs. 5.000.000'
quedan con valor anterior — breaking change en datos existentes."
### Cambio 10 — En 6.1 cambiar texto de última opción
Cambiar: "No, nunca se ha hablado del tema"
Por: "No, no se puede hablar del tema"
En seed.sql y SEED_SPEC.md.
Documentar en OBSERVATIONS.md: "Breaking change en datos existentes para esta opción."
---
## 5. Archivos a modificar
| Archivo | Cambios |
|---|---|
| `SEED_SPEC.md` | Todos los cambios de opciones, tipos, preguntas nuevas/eliminadas |
| `supabase/seed.sql` | Todos los cambios — es la fuente de verdad para re-seedear |
| Componente formulario (multiple_choice) | Cambio 2: agregar hint |
| Lógica condicional del formulario | Cambio 4a: salto triple |
| `OBSERVATIONS.md` o `TECH_DEBT.md` | Documentar breaking changes y nueva deuda |
---
## 6. Entregables (DoD)
- [ ] SEED_SPEC.md actualizado con todos los cambios.
- [ ] seed.sql actualizado — re-seedeable desde cero produce el instrumento v2.1.
- [ ] Componente multiple_choice muestra hint "Podés marcar más de una opción".
- [ ] Lógica de salto 1.1+1.6+1.13 todos "No" → salta a 6.4.
- [ ] Pregunta 6.6 visible en el formulario.
- [ ] "Prefiero no decir" ausente en todas las preguntas.
- [ ] 2.4 eliminada del seed.
- [ ] Breaking changes documentados en OBSERVATIONS.md.
- [ ] Build exitoso.
- [ ] `git diff --name-only HEAD` en scope.
## 7. Qué reportar
```
Verificado:
- SEED_SPEC.md actualizado ✓
- seed.sql actualizado ✓
- Conteo final de preguntas: [N]
- Hint en multiple_choice: ✓
- Salto triple implementado: ✓ (describir cómo)
- 6.6 agregada ✓
- "Prefiero no decir" eliminado de: [lista de preguntas]
- 2.4 eliminada ✓
- Build exitoso ✓
- Diff en scope ✓
Breaking changes documentados en OBSERVATIONS.md:
- [lista]
Asumido:
- [decisiones de implementación]
```
## 8. Qué NO hacer
- ❌ No re-seedear la DB de producción — solo actualizar seed.sql.
Re-seedear producción requiere aprobación explícita del director.
- ❌ No cambiar la estructura de tablas ni agregar columnas — solo opciones y lógica.
- ❌ No tocar el dashboard ni las RPCs.
- ❌ No resolver ítems de TECH_DEBT que no estén listados acá.
- ❌ No cambiar el diseño visual del formulario más allá del hint de multiple_choice.

View File

@@ -0,0 +1,175 @@
# ETAPA 3V-B — Vista de control: tabla plana de respuestas individuales
## 1. Objetivo
Reemplazar la vista `/admin/responses` actual (una fila por submission) por una tabla plana
con **una fila por respuesta individual** (una fila por cada pregunta respondida), mostrando
todos los campos explícitamente incluyendo nulos marcados visualmente.
---
## 2. Contexto previo
- La Etapa 3V-A implementó la vista de metadatos de submissions (una fila por response).
- El director quiere ver las respuestas individuales a las preguntas, no solo los metadatos.
- El esquema relevante: `responses``answers` (via response_id) → `questions` (via question_id).
- `answers.value` es JSONB. Los tipos de preguntas existentes en el instrumento son:
- `single_choice` → value es string: `"Asunción (Capital)"`
- `multiple_choice` → value es array: `["opción1","opción2"]`
- `rating` → value es número: `3`
- `text_short` → value es string corto
- `text_long`**NUNCA mostrar** — regla no negociable (RLS + privacidad)
---
## 3. DECISIONES YA TOMADAS — no consultar de nuevo
- Ruta: `/admin/responses` — reemplaza la página anterior completamente.
- Sin auth (herramienta interna de control).
- Sin paginación por ahora — máximo 500 filas (limit en la query).
- Ordenado por `responses.submitted_at DESC`, luego por `questions.position ASC` dentro de cada submission.
- Valores nulos mostrados con badge visual "sin dato" — fondo `--color-re-light` (#E7E6E5), texto `--color-re-dark` (#494F4F) opacidad 0.6.
- text_long excluido completamente — ni la columna ni el valor aparecen.
- Convención CSS: `.admin-*` en `app/globals.css`, igual que la etapa anterior.
- Cliente service-role inline en el Server Component, igual que en la versión anterior.
---
## 4. Estructura de la tabla
### 4.1 Encabezado de página
- Título: "Control de respuestas"
- Subtítulo: total de submissions + total de respuestas individuales + fecha/hora actualización.
- Botón "Actualizar" (`<a href="/admin/responses">`).
### 4.2 Columnas de la tabla (una fila por answer)
| Columna | Fuente | Notas |
|---|---|---|
| # submission | índice de la submission (1, 2, 3...) | mismo número para todas las filas de esa submission |
| Fecha | `responses.submitted_at` | DD/MM/YYYY HH:mm — solo en la primera fila de cada submission |
| Código | `questions.code` | A1, A2, 1.1, 1.6, etc. |
| Pregunta | `questions.prompt` | texto de la pregunta, truncado a 60 chars con "..." |
| Tipo | `questions.type` | badge con el tipo |
| Valor | `answers.value` | renderizado según tipo (ver 4.3) |
| Departamento | `responses.qi_zone` | solo en primera fila de cada submission |
| Sector | `responses.qi_sector` | solo en primera fila de cada submission |
### 4.3 Renderizado del valor según tipo
```typescript
function renderValue(value: unknown, type: string): string {
if (value === null || value === undefined) return null // → badge "sin dato"
switch(type) {
case 'single_choice':
case 'text_short':
return String(value).replace(/^"|"$/g, '') // quitar comillas del JSONB string
case 'multiple_choice':
if (Array.isArray(value)) return value.join(', ')
return String(value)
case 'rating':
return String(value) // número, opcionalmente con ★ repetidas
case 'text_long':
return null // NUNCA mostrar — excluir de la query directamente
default:
return String(value)
}
}
```
### 4.4 Agrupación visual de submissions
Las filas de una misma submission deben estar visualmente agrupadas:
- Separador visual entre submissions (border-top más pronunciado o fondo alternado por submission).
- Fecha, Departamento y Sector solo se muestran en la primera fila de cada submission.
- Las filas siguientes de la misma submission muestran esas celdas vacías (o con colspan si es HTML puro).
---
## 5. Query recomendada
```typescript
const { data } = await supabase
.from('responses')
.select(`
id,
submitted_at,
qi_zone,
qi_sector,
qi_age_bucket,
ip_hash,
surveys ( title ),
answers (
value,
questions (
code,
prompt,
type,
position,
is_sensitive
)
)
`)
.order('submitted_at', { ascending: false })
.limit(500)
```
Después en el componente, aplanar el resultado:
```typescript
// Para cada response, para cada answer (excluir text_long e is_sensitive):
// generar una fila con los datos de la response + los datos de la answer/question
const rows = responses.flatMap((response, responseIdx) =>
response.answers
.filter(a => a.questions?.type !== 'text_long' && !a.questions?.is_sensitive)
.sort((a, b) => (a.questions?.position ?? 0) - (b.questions?.position ?? 0))
.map((answer, answerIdx) => ({
responseIdx: responseIdx + 1,
isFirstInGroup: answerIdx === 0,
submitted_at: response.submitted_at,
qi_zone: response.qi_zone,
qi_sector: response.qi_sector,
ip_hash: response.ip_hash,
survey_title: response.surveys?.title,
code: answer.questions?.code,
prompt: answer.questions?.prompt,
type: answer.questions?.type,
value: answer.value,
}))
)
```
---
## 6. Entregables (DoD)
- [ ] Ruta `/admin/responses` renderiza con tabla plana (HTTP 200).
- [ ] Una fila por answer, agrupadas visualmente por submission.
- [ ] Columnas: # submission, Fecha, Código, Pregunta (truncada), Tipo, Valor, Departamento, Sector.
- [ ] `text_long` e `is_sensitive=true` excluidos.
- [ ] Valores nulos muestran badge "sin dato" con estilo tenue.
- [ ] Fecha, Departamento, Sector solo en primera fila de cada grupo.
- [ ] Build exitoso.
- [ ] `git diff --name-only HEAD` solo en `app/admin/` y `app/globals.css`.
## 7. Qué reportar
```
Verificado:
- Ruta /admin/responses accesible ✓
- Total submissions: [N] · Total filas en tabla: [N]
- Tipos de preguntas visibles: [lista]
- text_long excluido ✓ / is_sensitive excluido ✓
- Badge "sin dato" visible en campos nulos ✓ / no hay nulos en los datos actuales
- Build exitoso ✓
- Diff en scope ✓
Asumido:
- [decisiones cosméticas]
```
## 8. Qué NO hacer
- ❌ No mostrar text_long ni is_sensitive bajo ningún concepto.
- ❌ No exponer SUPABASE_SERVICE_ROLE_KEY al cliente.
- ❌ No tocar el formulario ni el dashboard.
- ❌ No agregar dependencias npm.
- ❌ No paginar todavía — limit 500 es suficiente para el piloto.