"use client"; import { useState } from "react"; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, } from "recharts"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; import { SegmentedControl } from "@/components/ui/segmented-control"; import { HelpTip } from "@/components/ui/help-tip"; import { useIsMobile } from "@/hooks/use-is-mobile"; import type { AgentUsageRow } from "@/types"; interface AgentUsageChartProps { data: AgentUsageRow[] | undefined; isLoading: boolean; } function fmtK(n: number): string { if (n >= 1_000) return (n / 1_000).toFixed(0) + "k"; return String(n); } const VIEW_OPTIONS = [ { value: "chart", label: "Chart" }, { value: "table", label: "Table" }, ]; export function AgentUsageChart({ data, isLoading }: AgentUsageChartProps) { const isMobile = useIsMobile(); const [view, setView] = useState<"chart" | "table">("chart"); // Fewer bars on a phone — 10 labels at ~30deg rotation still overlap below // ~400px, so cap the label density instead of shrinking text further. const chartData = [...(data ?? [])] .sort((a, b) => b.total_tokens - a.total_tokens) .slice(0, isMobile ? 6 : 10) .map((row) => ({ name: row.agent_slug, Tokens: row.total_tokens, })); const tableRows = [...(data ?? [])].sort( (a, b) => b.total_tokens - a.total_tokens, ); return (
Agent Tokens setView(v as "chart" | "table")} aria-label="Agent tokens view" />
{isLoading ? ( ) : view === "table" ? (
{tableRows.map((row) => ( ))}
Agent Tokens %
{row.agent_slug} {row.total_tokens.toLocaleString()} {row.pct_of_total.toFixed(1)}
) : ( [ fmtK(typeof value === "number" ? value : 0), "Tokens", ]} contentStyle={{ fontSize: 12 }} /> )}
); }