import { useEffect, useState } from 'react'; import { ArrowLeft, BarChart3, Wifi, Wrench, Layers } from 'lucide-react'; import { useNavigate } from 'react-router-dom'; import { api } from '@/api/client'; import type { AnalyticsSummary, SessionAnalyticsRow, ToolCostStat, ContextWindowStats, TokenBreakdownAgg, } from '@/api/types'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; // --- Independent section data fetcher --- // Each section manages its own loading/error/data state so one failure doesn't // block the rest of the page. function useFetch(fetcher: () => Promise): { data: T | null; loading: boolean; error: string | null; retry: () => void; } { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); function load() { setLoading(true); setError(null); fetcher() .then(setData) .catch((err: unknown) => { setError(err instanceof Error ? err.message : 'failed to load data'); }) .finally(() => setLoading(false)); } useEffect(() => { load(); }, []); // eslint-disable-line react-hooks/exhaustive-deps return { data, loading, error, retry: load }; } // --- Skeleton pulse placeholder --- function SkeletonBar({ className }: { className?: string }) { return
; } // --- Number formatting --- function formatNumber(n: number | null | undefined): string { if (n == null) return '—'; return n.toLocaleString(); } function formatCost(n: number | null | undefined): string { if (n == null) return '—'; if (n < 0.001) return `$${(n * 1000).toFixed(2)}m`; if (n < 0.01) return `$${n.toFixed(4)}`; return `$${n.toFixed(3)}`; } function formatPct(n: number | null | undefined): string { if (n == null) return '—'; return `${(n * 100).toFixed(1)}%`; } function formatDate(iso: string | null | undefined): string { if (!iso) return '—'; return new Date(iso).toLocaleDateString(undefined, { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit', }); } // --- Summary Cards --- function SummaryCards({ summary }: { summary: AnalyticsSummary }) { const cards = [ { label: 'Total Input Tokens', value: formatNumber(summary.total_input_tokens), icon: BarChart3, color: 'text-blue-500', }, { label: 'Total Output Tokens', value: formatNumber(summary.total_output_tokens), icon: BarChart3, color: 'text-green-500', }, { label: 'Total Cost', value: formatCost(summary.total_cost), icon: Wifi, color: 'text-amber-500', }, { label: 'Sessions Tracked', value: formatNumber(summary.session_count), icon: Layers, color: 'text-purple-500', }, ]; return (
{cards.map((c) => (
{c.value}
{c.label}
))}
); } function SummaryCardsSkeleton() { return (
{[0, 1, 2, 3].map((i) => ( ))}
); } // --- Section wrappers --- function SectionCard({ title, loading, error, onRetry, children, }: { title: string; loading: boolean; error: string | null; onRetry: () => void; children: React.ReactNode; }) { return ( {title} {loading ? (
) : error ? (
{error}
) : ( children )}
); } function EmptyState({ message }: { message: string }) { return

{message}

; } // --- Per-Session Token Table --- function SessionTable({ sessions }: { sessions: SessionAnalyticsRow[] }) { if (sessions.length === 0) { return ; } return (
{sessions.map((s) => ( ))}
Session Input Output Cost Last Active
{s.session_name || 'Untitled'} {formatNumber(s.total_input_tokens)} {formatNumber(s.total_output_tokens)} {formatCost(s.total_cost)} {formatDate(s.last_active_at)}
); } // --- Per-Tool Cost Table --- function ToolTable({ stats }: { stats: ToolCostStat[] }) { if (stats.length === 0) { return ; } return (
{stats.map((t) => ( ))}
Tool Calls Avg Prompt Avg Completion Avg Total
{t.tool_name} {t.n_calls} {formatNumber(t.mean_prompt_tokens)} {formatNumber(t.mean_completion_tokens)} {formatNumber(t.mean_prompt_tokens + t.mean_completion_tokens)}
); } // --- Context Window Utilization --- function ContextSection({ stats }: { stats: ContextWindowStats }) { if (stats.message_count === 0) { return ; } return (
Avg Context Used
{formatNumber(Math.round(stats.avg_ctx_used ?? 0))}
Avg Context Limit
{formatNumber(Math.round(stats.avg_ctx_max ?? 0))}
Avg Utilization
{formatPct(stats.avg_utilization_pct)}
Based on {formatNumber(stats.message_count)} completed assistant messages
); } // --- Token Category Breakdown (CSS stacked bar) --- const CATEGORY_COLORS: Record = { system: 'bg-blue-500', user: 'bg-green-500', assistant: 'bg-amber-500', tools: 'bg-purple-500', reasoning: 'bg-rose-500', }; const CATEGORY_LABELS: Record = { system: 'System', user: 'User', assistant: 'Assistant', tools: 'Tools', reasoning: 'Reasoning', }; function TokenBreakdownSection({ categories }: { categories: TokenBreakdownAgg[] }) { if (categories.length === 0) { return ; } const total = categories.reduce((sum, c) => sum + c.total_tokens, 0); if (total === 0) return ; // Sort in a consistent order const order = ['system', 'user', 'assistant', 'tools', 'reasoning']; const sorted = [...categories].sort( (a, b) => order.indexOf(a.category) - order.indexOf(b.category), ); return (
{sorted.map((c) => { const pct = (c.total_tokens / total) * 100; if (pct < 1) return null; return (
); })}
{sorted.map((c) => { const pct = (c.total_tokens / total) * 100; return (
{CATEGORY_LABELS[c.category] ?? c.category} {pct.toFixed(1)}% ({formatNumber(c.total_tokens)})
); })}
); } // --- Main Page --- export function Analytics() { const navigate = useNavigate(); const summary = useFetch(() => api.analytics.summary()); const sessions = useFetch(() => api.analytics.sessions().then((r) => r.sessions)); const tools = useFetch(() => api.tools.costStats().then((r) => r.stats)); const context = useFetch(() => api.analytics.context()); const breakdown = useFetch(() => api.analytics.tokenBreakdown().then((r) => r.categories)); function handleBack() { if (window.history.length > 1) { navigate(-1); } else { navigate('/'); } } return (

Token Analytics

Aggregate token usage, cost, and context window data across all sessions.

{/* Summary Cards */} {summary.loading ? ( ) : summary.error ? (
{summary.error}
) : summary.data ? ( ) : null} {/* Per-Session Token Breakdown */} {sessions.data && } {/* Per-Tool Cost Breakdown */} {tools.data && } {/* Context Window Utilization */} {context.data && } {/* Token Category Breakdown */} {breakdown.data && }
); }