import { useEffect, useMemo, useState } from "react"; import { api } from "../api.js"; interface FunnelDef { name: string; steps: string[]; } interface FunnelResult { name: string; startDate: string; endDate: string; steps: Array<{ step: number; event: string; count: number }>; conversionFromFirst: Array<{ step: number; event: string; rate: number }>; } interface RetentionResult { cohorts: Record< string, { cohortSize: number; retention: Array<{ day: number; active: number; rate: number }> } >; } interface EventCountsResult { counts: Array<{ date: string; event_name: string; total: number; unique_sessions: number; unique_users: number; }>; } function today() { return new Date().toISOString().slice(0, 10); } function weekAgo() { const d = new Date(); d.setUTCDate(d.getUTCDate() - 7); return d.toISOString().slice(0, 10); } export function AnalyticsPage() { const [funnels, setFunnels] = useState([]); const [funnelName, setFunnelName] = useState(""); const [funnelStart, setFunnelStart] = useState(weekAgo()); const [funnelEnd, setFunnelEnd] = useState(today()); const [funnelResult, setFunnelResult] = useState(null); const [retStart, setRetStart] = useState(weekAgo()); const [retEnd, setRetEnd] = useState(today()); const [retention, setRetention] = useState(null); const [countsStart, setCountsStart] = useState(weekAgo()); const [countsEnd, setCountsEnd] = useState(today()); const [counts, setCounts] = useState(null); const [error, setError] = useState(null); useEffect(() => { api<{ funnels: FunnelDef[] }>("/admin/v1/analytics/funnels") .then((res) => { setFunnels(res.funnels); if (res.funnels[0]) setFunnelName(res.funnels[0].name); }) .catch((e) => setError(String(e.message))); }, []); const loadFunnel = () => { if (!funnelName) return; api( `/admin/v1/analytics/funnels/${encodeURIComponent(funnelName)}?startDate=${funnelStart}&endDate=${funnelEnd}`, ) .then(setFunnelResult) .catch((e) => setError(String(e.message))); }; const loadRetention = () => { api( `/admin/v1/analytics/retention?startDate=${retStart}&endDate=${retEnd}`, ) .then(setRetention) .catch((e) => setError(String(e.message))); }; const loadCounts = () => { api( `/admin/v1/analytics/event-counts?startDate=${countsStart}&endDate=${countsEnd}`, ) .then(setCounts) .catch((e) => setError(String(e.message))); }; const countsByEvent = useMemo(() => { if (!counts) return []; const grouped = new Map(); for (const row of counts.counts) { const existing = grouped.get(row.event_name); if (!existing) { grouped.set(row.event_name, { dates: [row.date], totals: [Number(row.total)] }); } else { existing.dates.push(row.date); existing.totals.push(Number(row.total)); } } return Array.from(grouped.entries()).map(([event, data]) => ({ event, ...data })); }, [counts]); return (

Analytics

{error &&
{error}
}

Funnels

setFunnelStart(e.target.value)} /> setFunnelEnd(e.target.value)} />
{funnelResult && ( {funnelResult.steps.map((s, i) => ( ))}
Steg Event Antal Konvertering från första
{s.step} {s.event} {s.count} {(funnelResult.conversionFromFirst[i]?.rate ?? 0 * 100).toFixed(1)}%
)}

Retention

setRetStart(e.target.value)} /> setRetEnd(e.target.value)} />
{retention && ( {Array.from({ length: 8 }, (_, i) => ( ))} {Object.entries(retention.cohorts).map(([date, cohort]) => ( {cohort.retention.slice(0, 8).map((r) => ( ))} ))}
Kohort StorlekDag {i}
{date} {cohort.cohortSize}{(r.rate * 100).toFixed(0)}%
)}

Eventvolymer

setCountsStart(e.target.value)} /> setCountsEnd(e.target.value)} />
{countsByEvent.map(({ event, dates, totals }) => (
{event}
{dates.map((d, i) => (
{totals[i]}
{d.slice(5)}
))}
))}
); }