# 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.value` es JSONB — contiene el valor de la respuesta. - Campos sensibles protegidos por RLS: `answers` donde `question.is_sensitive=true` y los tipos `text_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 ``). ### 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 ```typescript // 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/responses` accesible 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 HEAD` solo en `app/admin/` y posiblemente `lib/`. ## 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á.