'use client'; import { useEffect, useState } from 'react'; interface SurveyRow { id: string; created_at: string; age_range: string | null; status: string | null; awareness_freq: string | null; tools_used: string[] | null; tools_other: string | null; cost_range: string | null; best_tool: string | null; best_satisfy: number | null; free_opinion: string | null; email: string | null; user_agent: string | null; referrer: string | null; utm_source: string | null; completion_seconds: number | null; } interface Stats { age_range: Record; status: Record; awareness_freq: Record; cost_range: Record; best_tool: Record; satisfy_avg: string; email_rate: string; completion_seconds_median: number; } type Range = 'all' | 'today' | 'week'; export default function AdminSurveyPage() { const [range, setRange] = useState('all'); const [total, setTotal] = useState(0); const [stats, setStats] = useState(null); const [rows, setRows] = useState([]); const [loading, setLoading] = useState(true); const [selected, setSelected] = useState(null); useEffect(() => { async function load(r: Range) { setLoading(true); try { const res = await fetch(`/api/admin/survey?range=${r}`); const data = await res.json(); setTotal(data.total ?? 0); setStats(data.stats ?? null); setRows(data.responses ?? []); } catch (e) { console.error(e); } finally { setLoading(false); } } load(range); }, [range]); function downloadCsv() { window.location.href = `/api/admin/survey?range=${range}&format=csv`; } function fmtCount(counts: Record | undefined): string { if (!counts) return ''; return Object.entries(counts) .sort((a, b) => b[1] - a[1]) .map(([k, v]) => `${k} ${v}`) .join(' · '); } return (

설문 응답

CONTOUR PMF 설문 — 총 {total}건

{(['all', 'today', 'week'] as Range[]).map((r) => ( ))}
{/* 통계 카드 */} {stats && (

Q2 자각 빈도

{fmtCount(stats.awareness_freq) || '데이터 없음'}

Q4 비용

{fmtCount(stats.cost_range) || '데이터 없음'}

Q5 만족도 평균

{stats.satisfy_avg} / 5

Q7 이메일률 / 완료 시간 (중간값)

{stats.email_rate}% · {stats.completion_seconds_median}s

)} {/* 응답 리스트 */} {loading ? (

불러오는 중...

) : rows.length === 0 ? (

응답이 없습니다.

) : (
{rows.map((r) => ( ))}
시각 나이/상황 Q4 비용 Q5 만족 Q6 자유의견 (미리보기) 이메일
{new Date(r.created_at).toLocaleString('ko-KR')} {r.age_range} · {r.status} {r.cost_range ?? '-'} {r.best_satisfy ?? '-'} {r.free_opinion ?? } {r.email ?? '-'}
)} {/* 상세 modal */} {selected && (
setSelected(null)} >
e.stopPropagation()} >

응답 상세

{new Date(selected.created_at).toLocaleString('ko-KR')}

{[ ['Q1 나이대', selected.age_range], ['Q1 상황', selected.status], ['Q2 자각 빈도', selected.awareness_freq], ['Q3 도구', selected.tools_used?.join(', ')], ['Q3 기타', selected.tools_other], ['Q4 비용', selected.cost_range], ['Q5 최고 도구', selected.best_tool], ['Q5 만족도', selected.best_satisfy != null ? `${selected.best_satisfy} / 5` : null], ['Q6 자유 의견', selected.free_opinion], ['Q7 이메일', selected.email], ['user_agent', selected.user_agent], ['referrer', selected.referrer], ['utm_source', selected.utm_source], ['완료 시간', selected.completion_seconds != null ? `${selected.completion_seconds}초` : null], ].map(([k, v]) => (
{k}
{(v as string) || }
))}
)}
); }