mirror of
https://github.com/rennf93/roboco.git
synced 2026-08-03 07:23:24 +02:00
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>
90 lines
2.6 KiB
TypeScript
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>
|
|
);
|
|
}
|