-
-
+
+ {VIEW_MODES.map((m) => (
+
+ ))}
{loading ? (
@@ -77,6 +90,10 @@ export default function UsageChart({ period = "7d" }) {
+
+
+
+
@@ -94,7 +111,7 @@ export default function UsageChart({ period = "7d" }) {
tick={{ fontSize: 10, fill: "currentColor", fillOpacity: 0.5 }}
tickLine={false}
axisLine={false}
- tickFormatter={viewMode === "tokens" ? fmtTokens : fmtCost}
+ tickFormatter={cfg.formatter}
width={50}
/>
- name === "tokens" ? [fmtTokens(value), "Tokens"] : [fmtCost(value), "Cost"]
- }
+ formatter={(value) => [cfg.formatter(value), cfg.label]}
+ />
+
- {viewMode === "tokens" ? (
-
- ) : (
-
- )}
)}
diff --git a/src/lib/db/repos/usageRepo.js b/src/lib/db/repos/usageRepo.js
index 6b1966cb..0d8aa96f 100644
--- a/src/lib/db/repos/usageRepo.js
+++ b/src/lib/db/repos/usageRepo.js
@@ -670,7 +670,7 @@ export async function getChartData(period = "7d") {
const startTime = startOfDay.getTime();
const endTime = startTime + bucketCount * bucketMs;
const labelFn = (ts) => new Date(ts).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false });
- const buckets = Array.from({ length: bucketCount }, (_, i) => ({ label: labelFn(startTime + i * bucketMs), tokens: 0, cost: 0 }));
+ const buckets = Array.from({ length: bucketCount }, (_, i) => ({ label: labelFn(startTime + i * bucketMs), tokens: 0, cost: 0, requests: 0 }));
const rows = db.all(
`SELECT timestamp, promptTokens, completionTokens, cost FROM usageHistory WHERE timestamp >= ?`,
@@ -683,6 +683,7 @@ export async function getChartData(period = "7d") {
if (idx >= 0 && idx < bucketCount) {
buckets[idx].tokens += (r.promptTokens || 0) + (r.completionTokens || 0);
buckets[idx].cost += r.cost || 0;
+ buckets[idx].requests += 1;
}
}
return buckets;
@@ -693,7 +694,7 @@ export async function getChartData(period = "7d") {
const bucketMs = 3600000;
const labelFn = (ts) => new Date(ts).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", hour12: false });
const startTime = now - bucketCount * bucketMs;
- const buckets = Array.from({ length: bucketCount }, (_, i) => ({ label: labelFn(startTime + i * bucketMs), tokens: 0, cost: 0 }));
+ const buckets = Array.from({ length: bucketCount }, (_, i) => ({ label: labelFn(startTime + i * bucketMs), tokens: 0, cost: 0, requests: 0 }));
const rows = db.all(
`SELECT timestamp, promptTokens, completionTokens, cost FROM usageHistory WHERE timestamp >= ?`,
@@ -705,6 +706,7 @@ export async function getChartData(period = "7d") {
const idx = Math.min(Math.floor((t - startTime) / bucketMs), bucketCount - 1);
buckets[idx].tokens += (r.promptTokens || 0) + (r.completionTokens || 0);
buckets[idx].cost += r.cost || 0;
+ buckets[idx].requests += 1;
}
return buckets;
}
@@ -727,6 +729,7 @@ export async function getChartData(period = "7d") {
label: labelFn(d),
tokens: dayData ? (dayData.promptTokens || 0) + (dayData.completionTokens || 0) : 0,
cost: dayData ? (dayData.cost || 0) : 0,
+ requests: dayData ? (dayData.requests || 0) : 0,
};
});
}
diff --git a/src/shared/components/UsageStats.js b/src/shared/components/UsageStats.js
index ecab40b1..093c8b3a 100644
--- a/src/shared/components/UsageStats.js
+++ b/src/shared/components/UsageStats.js
@@ -18,6 +18,8 @@ import dynamic from "next/dynamic";
// Lazy-load: keeps @xyflow/react out of the shared bundle until topology renders
const ProviderTopology = dynamic(() => import("@/app/(dashboard)/dashboard/usage/components/ProviderTopology"), { ssr: false });
import UsageChart from "@/app/(dashboard)/dashboard/usage/components/UsageChart";
+import ProviderBarChart from "@/app/(dashboard)/dashboard/usage/components/ProviderBarChart";
+import TopModelsChart from "@/app/(dashboard)/dashboard/usage/components/TopModelsChart";
function timeAgo(timestamp) {
const diff = Math.floor((Date.now() - new Date(timestamp)) / 1000);
@@ -483,6 +485,14 @@ export default function UsageStats({ period: periodProp, setPeriod: setPeriodPro
{/* Token / Cost chart - sync period */}
{loading ? spinner : }
+ {/* Provider and model breakdown charts */}
+ {!loading && (stats.byProvider || stats.byModel) && (
+
+ )}
+
{/* Table with dropdown selector */}