# ETAPA ADMIN — Vista de respuestas mejorada + vista detallada ## 1. Objetivo Mejorar `/admin/responses` para que sea una interfaz de administración real, y crear `/admin/responses/[id]` como vista detallada de una respuesta individual. Todo detrás del middleware de auth existente. ## 2. Contexto - Estado actual: `app/admin/responses/page.tsx` (Server Component, 249 líneas) + `app/admin/responses/responses-table.tsx` (Client Component, 98 líneas). - Auth: ya implementada — getSupabaseSession() con anon key + cookies, respeta RLS. - Exclusión de sensibles: `is_sensitive=false` y `type != 'text_long'` en query de questions — NO tocar, mantener exactamente igual. - SURVEY_OPTIONS hardcodeadas con UUIDs literales — reemplazar por query dinámica. - LIMIT 500 sin paginación real — reemplazar por paginación con page size de 50. ## 3. DECISIONES YA TOMADAS — no consultar - Paginación: page size 50, parámetro `?page=N` en la URL (Server Component, recarga completa — no client-side infinite scroll). - Filtro de survey: `` con onChange que navega a `?survey_id=`. Opción "Todos los surveys" con value=''. **Paginación:** - Leer `searchParams.page` (default 1). - Query de responses: `.range((page-1)*50, page*50-1)` en vez de `.limit(500)`. - Mostrar: "Página N de M · X respuestas totales". - Links Anterior / Siguiente que modifican `?page=N` en la URL. - Si hay menos de 50 resultados, no mostrar paginación. **Columna de acciones:** Agregar columna "Ver" en la fila cabecera de cada submission (isFirstInGroup=true) con link a `/admin/responses/[response_id]`. **Header informativo:** Mostrar claramente: total de submissions, total de answers, survey seleccionado. ### 4.2 Vista detallada — `app/admin/responses/[id]/page.tsx` Server Component. Ruta: `/admin/responses/[id]` donde `id` = `response_id` (uuid). **Query principal:** ```ts // 1. Metadata de la respuesta const { data: response } = await supabase .from('responses') .select('id, submitted_at, qi_zone, qi_sector, qi_age_bucket, survey_id') .eq('id', id) .single() // 2. Preguntas permitidas (mismo filtro que la lista) const { data: questions } = await supabase .from('questions') .select('id, code, prompt, type, position, section_number, section_title') .eq('is_sensitive', false) .neq('type', 'text_long') .eq('survey_id', response.survey_id) .order('position') // 3. Answers de esta respuesta const { data: answers } = await supabase .from('answers') .select('question_id, value, instance_id') .eq('response_id', id) .in('question_id', questions.map(q => q.id)) ``` **Layout:** - Header: fecha, zona, sector, edad — datos de qi_* de la respuesta. - Link "← Volver a la lista" que preserva el `?survey_id` y `?page` anteriores (pasar como searchParam o usar `referrer`). - Respuestas agrupadas por sección (section_number + section_title). - Dentro de cada sección: tabla código | pregunta | valor. - Instancias (instance_id != 'default'): mostrar agrupadas bajo "Familiar N" con badge visual. - Valores nulos: badge "sin respuesta" (igual que la lista). - Si `id` no existe o RLS bloquea: mostrar "Respuesta no encontrada" — no crashear. ### 4.3 Tabla de respuestas — `app/admin/responses/responses-table.tsx` Agregar la columna "Ver" en las filas isFirstInGroup con `` a `/admin/responses/[submissionId]`. Pasar `surveyId` y `page` como props para construir el link de "← Volver" en la vista detallada. ## 5. DoD antes de reportar - [ ] `git log --name-only -1` — listar todos los archivos tocados - [ ] Filtro de survey carga desde DB, no hardcodeado - [ ] Paginación funciona: page=1 muestra primeros 50, page=2 los siguientes - [ ] Link "Ver" en cada submission lleva a `/admin/responses/[id]` - [ ] Vista detallada muestra respuestas agrupadas por sección - [ ] Instancias agrupadas bajo "Familiar N" - [ ] RLS respetado: misma exclusión de sensibles que la lista - [ ] "Respuesta no encontrada" si el id no existe - [ ] Build sin errores: `npm run build` - [ ] `git log --name-only -1` - [ ] Commit + push a Gitea ## 6. Qué reportar ``` Verificado: - Filtro survey: ✓ — query a tabla surveys, no hardcodeado - Paginación: ✓ — page size 50, range correcto - Link "Ver" → /admin/responses/[id]: ✓ - Vista detallada: ✓ — agrupada por sección, instancias bajo "Familiar N" - RLS/exclusión sensibles: ✓ — mismo filtro que antes - "No encontrada" si id inválido: ✓ - Build: ✓ - git log --name-only -1: [lista completa] - Commit: [hash] Decisiones de diseño tomadas: - [lo que haya] Hallazgos: - [lo que aparezca, especialmente sobre questions.section_number/section_title — si esas columnas no existen, reportarlo antes de inventar una solución] ``` ## 7. Qué NO hacer - ❌ No tocar el filtro `is_sensitive=false` ni `type != 'text_long'` - ❌ No usar service_role — solo getSupabaseSession() con RLS - ❌ No agregar librerías de tablas o UI externas - ❌ No implementar búsqueda por texto (fuera de alcance) - ❌ No hacer deploy — solo commit y push a Gitea - ❌ Si `section_number` o `section_title` no existen en questions, reportarlo y NO inventar columnas — esperar instrucción ## 8. Versionado ``` feat(admin): vista respuestas mejorada + vista detallada [admin] /admin/responses: filtro survey dinámico, paginación 50/página, link a vista detallada. /admin/responses/[id]: respuestas por sección, instancias bajo "Familiar N". RLS y exclusión de sensibles intactos. ```