4.8 KiB
4.8 KiB
ETAPA 3V — Vista de control: listado de respuestas
1. Objetivo
Crear una página de control operativo en /admin/responses que muestre todas las
respuestas recibidas, ordenadas por fecha descendente (más recientes primero).
Es una herramienta interna de diagnóstico — sin auth, sin PII, sin datos sensibles.
2. Contexto previo
- La migración está en Supabase self-hosted.
- Las tablas relevantes son
responses,answers,questions,surveys. answers.valuees JSONB — contiene el valor de la respuesta.- Campos sensibles protegidos por RLS:
answersdondequestion.is_sensitive=truey los tipostext_long. No mostrar esos campos aunque lleguen. - La página usa el cliente server-side (
SUPABASE_SERVICE_ROLE_KEY) porque bypasa RLS y permite ver todos los datos. No usar el cliente anon.
3. DECISIONES YA TOMADAS — no consultar de nuevo
- Ruta:
/admin/responses— Server Component de Next.js (no client component). - Sin auth (es herramienta interna de control).
- Sin paginación por ahora — mostrar las últimas 200 respuestas máximo.
- Ordenado por
submitted_at DESC. - HTML semántico limpio, paleta RE existente (
#43BBC8,#494F4F,#F8AD13). - No usar componentes del dashboard existente — página standalone.
4. Qué mostrar
4.1 Encabezado de página
- Título: "Control de respuestas"
- Subtítulo: conteo total de respuestas en la base + fecha/hora de última actualización.
- Botón "Actualizar" que recarga la página (simple
<a href="/admin/responses">).
4.2 Tabla de respuestas (una fila por response)
Columnas:
| Columna | Fuente | Notas |
|---|---|---|
| # | índice visual (1, 2, 3...) | más reciente = 1 |
| Fecha | responses.submitted_at |
formato: DD/MM/YYYY HH:mm |
| Encuesta | surveys.title |
JOIN con surveys |
| Departamento | responses.qi_zone |
puede ser null → mostrar "—" |
| Sector | responses.qi_sector |
puede ser null → mostrar "—" |
| Rango etario | responses.qi_age_bucket |
puede ser null → mostrar "—" |
| Respuestas | count de answers para esa response |
número simple |
| IP hash | responses.ip_hash |
primeros 8 chars + "..." — solo para dedup |
4.3 Detalle expandible (opcional, Nivel 2 — reportar si se implementa)
Si el tiempo lo permite: al hacer click en una fila, mostrar un panel con las respuestas individuales (código de pregunta + valor), excluyendo:
- Preguntas con
is_sensitive = true - Preguntas de tipo
text_long
Si no se implementa el detalle, la tabla sola es suficiente para cerrar la etapa.
5. Implementación
5.1 Query recomendada
// En un Server Component — usar el cliente con service_role
const { data: responses } = await supabase
.from('responses')
.select(`
id,
submitted_at,
qi_zone,
qi_sector,
qi_age_bucket,
ip_hash,
surveys ( title ),
answers ( count )
`)
.order('submitted_at', { ascending: false })
.limit(200)
5.2 Cliente server-side
Usar createClient con SUPABASE_SERVICE_ROLE_KEY (server-side only, no exponer
al cliente). Si existe lib/supabase-server.ts o similar, usarlo. Si no, crear
el cliente inline en el Server Component con la service role key.
5.3 Estilos
- Paleta RE: teal
#43BBC8, dark#494F4F, orange#F8AD13, light#E7E6E5. - Tabla con filas alternadas (zebra), hover sutil.
- Header de tabla sticky si la lista es larga.
- Responsive — en mobile, las columnas menos importantes se ocultan.
- Fuente: la que ya usa el proyecto (IBM Plex Sans o la que esté configurada).
- NO usar Tailwind si el proyecto no lo usa — verificar primero con
grep "tailwind" package.json.
6. Entregables (DoD)
- Ruta
/admin/responsesaccesible y renderiza. - Tabla muestra respuestas ordenadas por fecha descendente.
- Conteo total en el encabezado.
- Columnas: #, Fecha, Encuesta, Departamento, Sector, Rango etario, Respuestas, IP hash (truncado).
- Campos nulos muestran "—".
- IP hash truncada (primeros 8 chars).
- Build sin errores (
next build). git diff --name-only HEADsolo enapp/admin/y posiblementelib/.
7. Qué reportar
Verificado:
- Ruta /admin/responses accesible ✓
- Columnas presentes: [lista]
- Conteo total: [número]
- Build exitoso ✓
- Diff en scope ✓
Asumido:
- [decisiones cosméticas o de implementación]
Decisiones proactivas (Nivel 2):
- [si implementó el detalle expandible o no]
8. Qué NO hacer
- ❌ No agregar auth — es herramienta interna, la URL es suficiente como "seguridad por oscuridad" por ahora.
- ❌ No mostrar campos text_long ni is_sensitive=true.
- ❌ No exponer SUPABASE_SERVICE_ROLE_KEY al cliente (solo server-side).
- ❌ No tocar el formulario ni el dashboard existente.
- ❌ No agregar dependencias npm.
- ❌ No resolver ítems del backlog que no sean los listados acá.