Files
roboco/panel/src/components/metrics/model-usage-donut.tsx
T
7248e5b722 fix(panel): charts get real axes, humanized ticks, and dark-theme tooltips (#611)
Every chart hand-rolled its own k-only formatter (22596k-style ticks, left-
clipped y labels), used recharts' default white tooltip (invisible header on
the dark theme), and two charts pinned a numeric XAxis interval that collapses
short series to a single tick. The agent Token Activity chart had no axes at
all and blanked its tooltip date on purpose.

One shared formatTokens/formatBucket (lib/format.ts) and one shared themed
tooltip style (components/charts/chart-tooltip.tsx) now feed all 10 charts;
axis widths/margins sized to the labels; preserveStartEnd tick intervals.

Co-authored-by: Renn F <rennf93@users.noreply.github.com>
2026-07-20 20:29:45 +02:00

90 lines
2.6 KiB
TypeScript

"use client";
import {
PieChart,
Pie,
Cell,
Tooltip,
ResponsiveContainer,
Legend,
} from "recharts";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
import { HelpTip } from "@/components/ui/help-tip";
import { useIsMobile } from "@/hooks/use-is-mobile";
import { chartTooltipStyle } from "@/components/charts/chart-tooltip";
import type { ModelUsageSlice } from "@/types";
// Design-system chart tokens — resolves to theme-aware palette
const CHART_COLORS = [
"var(--chart-1)",
"var(--chart-2)",
"var(--chart-3)",
"var(--chart-4)",
"var(--chart-5)",
];
interface ModelUsageDonutProps {
data: ModelUsageSlice[] | undefined;
isLoading: boolean;
}
export function ModelUsageDonut({ data, isLoading }: ModelUsageDonutProps) {
const isMobile = useIsMobile();
const chartData = (data ?? []).map((s) => ({
name: s.model,
value: s.total_tokens,
cost: s.cost_usd,
pct: s.pct_of_total,
}));
return (
<Card>
<CardHeader className="pb-2">
<HelpTip label="Share of total tokens consumed per model in the selected window — hover a slice for exact tokens and cost">
<CardTitle className="text-base">By Model</CardTitle>
</HelpTip>
</CardHeader>
<CardContent>
{isLoading ? (
<Skeleton className="h-52 w-full" />
) : chartData.length === 0 ? (
<p className="text-sm text-muted-foreground py-16 text-center">
No usage recorded in this window yet.
</p>
) : (
<ResponsiveContainer width="100%" height={208}>
<PieChart>
<Pie
data={chartData}
cx="50%"
cy="50%"
innerRadius={isMobile ? 44 : 52}
outerRadius={isMobile ? 68 : 80}
dataKey="value"
paddingAngle={3}
>
{chartData.map((_, idx) => (
<Cell
key={idx}
fill={CHART_COLORS[idx % CHART_COLORS.length]}
/>
))}
</Pie>
<Tooltip
{...chartTooltipStyle}
formatter={(value, name) => [
(typeof value === "number" ? value : 0).toLocaleString() +
" tokens",
name,
]}
/>
<Legend wrapperStyle={{ fontSize: isMobile ? 9 : 11 }} />
</PieChart>
</ResponsiveContainer>
)}
</CardContent>
</Card>
);
}