import { useMemo, useState } from "react"; import { Line } from "react-chartjs-2"; import { getGainDashboardStats } from "../api"; import { buildSharedPlugins, buildSharedScales, CHART_THEMES, lineDatasetStyle } from "../components/chart-shared"; import { formatBytes, formatCompact, formatInteger, formatPercent } from "../data/formatters"; import { useResource } from "../data/useResource"; import type { GainDashboardStats, GainSourceTotals, GainTimeSeriesPoint, TimeRange } from "../types"; import { AsyncBoundary, Panel } from "../ui"; import { useSystemTheme } from "../useSystemTheme"; export interface GainRouteProps { active: boolean; range: TimeRange; refreshTrigger: number; } export function GainRoute({ active, range, refreshTrigger }: GainRouteProps) { const [project, setProject] = useState(null); const { data: stats, error, loading, } = useResource(["gain", range, refreshTrigger, project], signal => getGainDashboardStats(range, project, signal), { pollMs: 30_000, enabled: active, }); return (
{stats && ( <> )}
); } // --------------------------------------------------------------------------- // Project selector // --------------------------------------------------------------------------- function GainProjectSelector({ projects, selected, onChange, }: { projects: string[]; selected: string | null; onChange: (p: string | null) => void; }) { if (projects.length === 0) return null; return (
Project
); } // --------------------------------------------------------------------------- // Overall metrics panel // --------------------------------------------------------------------------- function GainOverallPanel({ overall }: { overall: GainSourceTotals }) { return (
Saved Tokens
{formatCompact(overall.savedTokens)}
Saved Bytes
{formatBytes(overall.savedBytes)}
Reduction
{overall.reductionPercent !== null ? formatPercent(overall.reductionPercent) : "—"}
Total Hits
{formatInteger(overall.hits)}
); } // --------------------------------------------------------------------------- // By-source breakdown panel // --------------------------------------------------------------------------- function SourceCard({ title, totals }: { title: string; totals: GainSourceTotals }) { return (
{title}
Saved Tokens
{formatCompact(totals.savedTokens)}
Saved Bytes
{formatBytes(totals.savedBytes)}
Hits
{formatInteger(totals.hits)}
Reduction
{totals.reductionPercent !== null ? formatPercent(totals.reductionPercent) : "—"}
); } function GainBySourcePanel({ bySource }: { bySource: GainDashboardStats["bySource"] }) { return (
); } // --------------------------------------------------------------------------- // Time series chart (stacked area, daily) // --------------------------------------------------------------------------- const GAIN_COLORS = { snapcompact: "rgb(34, 197, 94)", } as const; function GainTimeSeriesPanel({ timeSeries }: { timeSeries: GainTimeSeriesPoint[] }) { const theme = useSystemTheme(); const chartTheme = CHART_THEMES[theme]; const { data, options } = useMemo(() => { const labelFormatter = new Intl.DateTimeFormat(undefined, { month: "short", day: "numeric", timeZone: "UTC", }); const labels = timeSeries.map(p => labelFormatter.format(new Date(`${p.date}T00:00:00.000Z`))); const chartData = { labels, datasets: [ { label: "Snapcompact", data: timeSeries.map(p => p.snapcompact), ...lineDatasetStyle(GAIN_COLORS.snapcompact), }, ], }; const { sharedScaleBase, yScale } = buildSharedScales({ chartTheme, formatY: n => formatCompact(n), }); const chartOptions = { responsive: true, maintainAspectRatio: false, plugins: buildSharedPlugins({ chartTheme, showLegend: true, defaultLabel: "Tokens Saved", formatValue: formatCompact, }), scales: { x: { ...sharedScaleBase, stacked: true }, y: { ...yScale, stacked: true }, }, }; return { data: chartData, options: chartOptions }; }, [timeSeries, chartTheme]); return (
{timeSeries.length === 0 ? (
No time series data yet
) : ( [0]["options"]} /> )}
); }