"use client";
import { useMemo } from "react";
import { useTranslations } from "next-intl";
import Card from "../Card";
import { getModelColor } from "@/shared/constants/colors";
import { fmtCompact as fmt, fmtCost } from "@/shared/utils/formatting";
import { ChartLoadingCard, DarkTooltip, useRecharts } from "./rechartsCore";
// ── DailyTrendChart (Recharts) ─────────────────────────────────────────────
export function DailyTrendChart({ dailyTrend }) {
const t = useTranslations("analytics");
const chartData = useMemo(() => {
return (dailyTrend || []).map((d) => ({
date: d.date.slice(5),
[t("chartInput")]: d.promptTokens,
[t("chartOutput")]: d.completionTokens,
[t("chartCost")]: d.cost || 0,
}));
}, [dailyTrend, t]);
const hasCost = useMemo(() => chartData.some((d) => d[t("chartCost")] > 0), [chartData, t]);
if (!chartData.length) {
return (
{t("chartModelUsageOverTime")}
{t("chartNoData")}
);
}
return ;
}
function DailyTrendChartBody({ chartData, hasCost }) {
const t = useTranslations("analytics");
const recharts = useRecharts();
if (!recharts) {
return ;
}
const { ComposedChart, Bar, Line, XAxis, YAxis, Tooltip, ResponsiveContainer } = recharts;
return (
{t("chartModelUsageOverTime")}
{hasCost && (
`$${v.toFixed(2)}`}
width={36}
/>
)}
} cursor={{ fill: "rgba(255,255,255,0.04)" }} />
{hasCost && (
)}
{t("chartInput")}
{t("chartOutput")}
{hasCost && (
{t("chartCost")} ($)
)}
);
}
// ── Cost-aware Tooltip ─────────────────────────────────────────────────────
function CostTooltip({
active,
payload,
label,
}: {
active?: boolean;
payload?: any[];
label?: any;
}) {
const t = useTranslations("analytics");
if (!active || !payload?.length) return null;
return (
{label &&
{label}
}
{payload.map((entry, i) => (
{entry.name}:
{entry.name === t("chartCost") ? fmtCost(entry.value) : fmt(entry.value)}
))}
);
}
// ── WeeklyPattern (Recharts) ───────────────────────────────────────────────
function WeeklyPattern({ weeklyPattern }) {
const t = useTranslations("analytics");
const chartData = useMemo(() => {
return (weeklyPattern || []).map((w) => ({
day: w.day.slice(0, 3),
Tokens: w.totalTokens,
}));
}, [weeklyPattern]);
return ;
}
function WeeklyPatternBody({ chartData, title }) {
const recharts = useRecharts();
if (!recharts) {
return (
{title}
);
}
const { ResponsiveContainer, BarChart, XAxis, Tooltip, Bar } = recharts;
return (
{title}
}
cursor={{ fill: "rgba(255,255,255,0.04)" }}
/>
);
}
// ── MostActiveDay7d ────────────────────────────────────────────────────────
export function ModelOverTimeChart({ dailyByModel, modelNames }) {
const t = useTranslations("analytics");
const data = useMemo(() => dailyByModel || [], [dailyByModel]);
const models = useMemo(() => modelNames || [], [modelNames]);
// Prepare chart data — format dates (must be before early return for rules-of-hooks)
const chartData = useMemo(() => {
return data.map((d) => {
const row = { ...d };
// Short date label
if (d.date) {
const parts = d.date.split("-");
row.dateLabel = `${parts[1]}/${parts[2]}`;
}
return row;
});
}, [data]);
if (!data.length || !models.length) {
return (
{t("chartModelUsageOverTime")}
{t("chartNoData")}
);
}
return ;
}
function ModelOverTimeChartBody({ chartData, models }) {
const t = useTranslations("analytics");
const recharts = useRecharts();
if (!recharts) {
return ;
}
const { AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer } = recharts;
return (
{t("chartModelUsageOverTime")}
fmt(v)}
axisLine={false}
tickLine={false}
width={50}
/>
} />
{models.map((m, i) => (
))}
{models.map((m, i) => (
{m}
))}
);
}
// ── ProviderTable ──────────────────────────────────────────────────────────