133 lines
4.8 KiB
Markdown
133 lines
4.8 KiB
Markdown
# 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 `<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
|
|
```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á. |