Control de respuestas
No fue posible conectarse a la base de datos.
{questionsError.message}
import { createServerClient } from '@supabase/ssr' import { cookies } from 'next/headers' import { ResponsesTable, type DisplayRow } from './responses-table' import { SurveySelect } from './survey-select' import { AdminNavBar } from '@/components/admin-nav-bar' import { formatDate, renderValue } from './admin-format' export const dynamic = 'force-dynamic' const PAGE_SIZE = 50 const PROMPT_MAX_LEN = 60 interface AllowedQuestion { id: string code: string prompt: string type: string position: number } interface AnswerRow { value: unknown question_id: string } interface ResponseRow { id: string submitted_at: string qi_zone: string | null qi_sector: string | null answers: AnswerRow[] } interface SurveyOption { id: string title: string } async function getSupabaseSession() { const cookieStore = await cookies() return createServerClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { cookies: { getAll() { return cookieStore.getAll() }, setAll(cookiesToSet) { try { cookiesToSet.forEach(({ name, value, options }) => cookieStore.set(name, value, options) ) } catch { // Server Component — no puede mutar cookies, se ignora } }, }, } ) } function truncate(text: string, max = PROMPT_MAX_LEN): string { return text.length > max ? `${text.slice(0, max)}...` : text } function pageHref(surveyId: string | undefined, page: number): string { const params = new URLSearchParams() if (surveyId) params.set('survey_id', surveyId) params.set('page', String(page)) return `/admin/responses?${params.toString()}` } export default async function AdminResponsesPage({ searchParams, }: { searchParams: Promise<{ survey_id?: string; page?: string }> }) { const { survey_id: surveyId, page: pageParam } = await searchParams const page = Math.max(1, Number(pageParam) || 1) const supabase = await getSupabaseSession() // 0 y 1 no dependen entre sí — en paralelo. // 0. Surveys para el filtro — dinámico, no hardcodeado. Solo trae lo que // RLS deja ver al usuario autenticado. Si falla, el filtro se degrada a // "sin opciones" en vez de tirar abajo la página entera — la lista de // respuestas no depende de esta query. // 1. Preguntas permitidas — text_long e is_sensitive=true se excluyen ACÁ, // antes de tocar la tabla answers. Sus valores nunca se consultan. const [ { data: surveysData, error: surveysError }, { data: questionsData, error: questionsError }, ] = await Promise.all([ supabase.from('surveys').select('id, title').order('title'), supabase .from('questions') .select('id, code, prompt, type, position') .eq('is_sensitive', false) .neq('type', 'text_long') .order('position', { ascending: true }), ]) const surveys = (surveysData ?? []) as SurveyOption[] const selectedSurvey = surveys.find((s) => s.id === surveyId) if (questionsError) { return (
No fue posible conectarse a la base de datos.
{questionsError.message}
No fue posible conectarse a la base de datos.
{responsesError.message}
{totalSubmissions ?? 0} submissions · {totalAnswers ?? 0} respuestas individuales ·{' '} {selectedSurvey ? selectedSurvey.title : 'Todos los surveys'} · actualizado{' '} {formatDate(new Date().toISOString())}
Página {page} de {totalPages} · {totalSubmissions ?? 0} submissions totales
No se pudo cargar la lista de surveys para el filtro — se muestra "Todos".
)}Sin respuestas registradas todavía.
)}