import type { CostHistogramResponse } from "@/lib/types/logs"; import { useMemo } from "react"; import { Area, AreaChart, Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis } from "recharts"; import { formatCost, formatFullTimestamp, formatTimestamp, getModelColor } from "../../utils/chartUtils"; import { ChartErrorBoundary } from "./chartErrorBoundary"; import type { ChartType } from "./chartTypeToggle"; interface CostChartProps { data: CostHistogramResponse | null; chartType: ChartType; startTime: number; endTime: number; selectedModel: string; } function CustomTooltip({ active, payload, selectedModel, models }: any) { if (!active || !payload || !payload.length) return null; const data = payload[0]?.payload; if (!data) return null; return (
{formatFullTimestamp(data.timestamp)}
{selectedModel === "all" ? ( <> {models.map((model: string, idx: number) => { const cost = data.by_model?.[model] || 0; if (cost === 0) return null; return (
{model} {formatCost(cost)}
); })}
Total {formatCost(data.total_cost)}
) : (
{selectedModel} {formatCost(data.by_model?.[selectedModel] || 0)}
)}
); } export function CostChart({ data, chartType, startTime, endTime, selectedModel }: CostChartProps) { const { chartData, displayModels } = useMemo(() => { if (!data?.buckets || !data.bucket_size_seconds) { return { chartData: [], displayModels: [] }; } const models = selectedModel === "all" ? data.models : [selectedModel]; const processed = data.buckets.map((bucket, index) => { const item: any = { ...bucket, index, formattedTime: formatTimestamp(bucket.timestamp, data.bucket_size_seconds), }; // Flatten by_model for easier chart access models.forEach((model, idx) => { item[`model_${idx}`] = bucket.by_model?.[model] || 0; }); return item; }); return { chartData: processed, displayModels: models }; }, [data, selectedModel]); if (!data?.buckets || chartData.length === 0) { return
No data available
; } const commonProps = { data: chartData, margin: { top: 6, right: 4, left: 4, bottom: 0 }, }; return ( {chartType === "bar" ? ( chartData[Math.round(idx)]?.formattedTime || ""} interval="preserveStartEnd" /> formatCost(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 0.01)]} allowDataOverflow={false} /> } cursor={{ fill: "#8c8c8f", fillOpacity: 0.15 }} /> {displayModels.map((model, idx) => ( ))} ) : ( chartData[Math.round(idx)]?.formattedTime || ""} interval="preserveStartEnd" /> formatCost(v)} domain={[0, (dataMax: number) => Math.max(dataMax, 0.01)]} allowDataOverflow={false} /> } /> {displayModels.map((model, idx) => ( ))} )} ); }