'use client';
import { useEffect, useState } from 'react';
import { getStats, type AdminStats } from '@/lib/api';
function KpiCard({
title,
value,
sub,
}: {
title: string;
value: string | number;
sub?: string;
}) {
return (
{title}
{value}
{sub &&
{sub}
}
);
}
function ReactionBar({ reactions }: { reactions: Record }) {
const total = Object.values(reactions).reduce((a, b) => a + b, 0);
if (total === 0) return No reactions yet.
;
const COLORS: Record = {
done: 'bg-emerald-500',
snooze: 'bg-yellow-400',
dismiss: 'bg-red-500',
};
return (
{Object.entries(reactions).map(([action, count]) => (
))}
);
}
export function OverviewDashboard() {
const [stats, setStats] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
getStats()
.then(setStats)
.catch((e) => setError(String(e.message)));
}, []);
if (error) {
return Failed to load stats: {error}
;
}
const activationPct =
stats && stats.totalUsers > 0
? ((stats.activatedUsers / stats.totalUsers) * 100).toFixed(1)
: null;
return (
Overview
Last 7 days unless noted
{/* KPI grid */}
{/* Reactions */}
Reactions last 7 days
{stats ? (
) : (
Loading…
)}
{/* Activation funnel */}
Activation funnel
{stats ? (
a + b, 0)}
max={stats.tipsServedLast7d}
dimMax
/>
) : (
Loading…
)}
);
}
function FunnelRow({
label,
value,
max,
dimMax,
}: {
label: string;
value: number;
max: number;
dimMax?: boolean;
}) {
const pct = max > 0 ? (value / max) * 100 : 0;
return (
{label}
{value}
{!dimMax && max > 0 && (
{pct.toFixed(0)}%
)}
);
}