Skip to content

Commit e521cfc

Browse files
committed
feat: update dashboard to use ComposedChart and include error metrics
1 parent 33c256f commit e521cfc

3 files changed

Lines changed: 14 additions & 7 deletions

File tree

app/page.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1396,7 +1396,7 @@ export default function DashboardPage() {
13961396
</div>
13971397
) : (
13981398
<ResponsiveContainer width="100%" height="100%">
1399-
<LineChart data={overviewData.byDay} margin={{ top: 0, right: 12, left: 0, bottom: 0 }}>
1399+
<ComposedChart data={overviewData.byDay} margin={{ top: 0, right: 12, left: 0, bottom: 0 }}>
14001400
<CartesianGrid stroke="#334155" strokeDasharray="5 5" />
14011401
<XAxis dataKey="label" stroke="#94a3b8" fontSize={12} />
14021402
<YAxis
@@ -1426,7 +1426,7 @@ export default function DashboardPage() {
14261426
content={({ active, payload, label }) => {
14271427
if (!active || !payload || !payload.length) return null;
14281428
const sortedPayload = [...payload].sort((a: any, b: any) => {
1429-
const order: Record<string, number> = { requests: 0, tokens: 1, cost: 2 };
1429+
const order: Record<string, number> = { requests: 0, errors: 1, tokens: 2, cost: 3 };
14301430
return (order[a.dataKey] ?? 999) - (order[b.dataKey] ?? 999);
14311431
});
14321432
return (
@@ -1443,6 +1443,7 @@ export default function DashboardPage() {
14431443
{sortedPayload.map((entry: any, index: number) => {
14441444
let color = entry.color;
14451445
if (entry.name === "请求数") color = darkMode ? "#60a5fa" : "#3b82f6";
1446+
if (entry.name === "错误数") color = darkMode ? "#f87171" : "#ef4444";
14461447
if (entry.name === "Tokens") color = darkMode ? "#4ade80" : "#16a34a";
14471448
if (entry.name === "费用") color = "#fbbf24";
14481449

@@ -1482,10 +1483,11 @@ export default function DashboardPage() {
14821483
}}
14831484
itemSorter={(item: any) => ({ requests: 0, tokens: 1, cost: 2 } as Record<string, number>)[item?.dataKey] ?? 999}
14841485
/>
1486+
<Bar yAxisId={trendConfig.lineAxisMap.requests} dataKey="errors" name="错误数" fill={darkMode ? "#f87171" : "#ef4444"} fillOpacity={0.35} maxBarSize={18} legendType="none" />
14851487
<Line hide={!trendVisible.requests} yAxisId={trendConfig.lineAxisMap.requests} type="monotone" dataKey="requests" stroke={darkMode ? "#60a5fa" : "#3b82f6"} strokeWidth={2} name="请求数" dot={{ r: 3, fill: darkMode ? "#60a5fa" : "#3b82f6", stroke: "#fff", strokeWidth: 1, fillOpacity: 0.2 }} activeDot={{ r: 6, stroke: "#fff", strokeWidth: 2 }} />
14861488
<Line hide={!trendVisible.tokens} yAxisId={trendConfig.lineAxisMap.tokens} type="monotone" dataKey="tokens" stroke={darkMode ? "#4ade80" : "#16a34a"} strokeWidth={2} name="Tokens" dot={{ r: 3, fill: darkMode ? "#4ade80" : "#16a34a", stroke: "#fff", strokeWidth: 1, fillOpacity: 0.2 }} activeDot={{ r: 6, stroke: "#fff", strokeWidth: 2 }} />
14871489
<Line hide={!trendVisible.cost} yAxisId={trendConfig.lineAxisMap.cost} type="monotone" dataKey="cost" stroke="#fbbf24" strokeWidth={2} name="费用" dot={{ r: 3, fill: "#fbbf24", stroke: "#fff", strokeWidth: 1, fillOpacity: 0.2 }} activeDot={{ r: 6, stroke: "#fff", strokeWidth: 2 }} />
1488-
</LineChart>
1490+
</ComposedChart>
14891491
</ResponsiveContainer>
14901492
)}
14911493
</div>
@@ -2155,7 +2157,7 @@ export default function DashboardPage() {
21552157
<div className="mt-4 h-[70vh]">
21562158
{fullscreenChart === "trend" && overviewData && (
21572159
<ResponsiveContainer width="100%" height="100%">
2158-
<LineChart data={overviewData.byDay} margin={{ top: 0, right: 40, left: 0, bottom: 0 }}>
2160+
<ComposedChart data={overviewData.byDay} margin={{ top: 0, right: 40, left: 0, bottom: 0 }}>
21592161
<CartesianGrid stroke="#334155" strokeDasharray="5 5" />
21602162
<XAxis dataKey="label" stroke="#94a3b8" fontSize={12} />
21612163
<YAxis
@@ -2185,7 +2187,7 @@ export default function DashboardPage() {
21852187
content={({ active, payload, label }) => {
21862188
if (!active || !payload || !payload.length) return null;
21872189
const sortedPayload = [...payload].sort((a: any, b: any) => {
2188-
const order: Record<string, number> = { requests: 0, tokens: 1, cost: 2 };
2190+
const order: Record<string, number> = { requests: 0, errors: 1, tokens: 2, cost: 3 };
21892191
return (order[a.dataKey] ?? 999) - (order[b.dataKey] ?? 999);
21902192
});
21912193
return (
@@ -2202,6 +2204,7 @@ export default function DashboardPage() {
22022204
{sortedPayload.map((entry: any, index: number) => {
22032205
let color = entry.color;
22042206
if (entry.name === "请求数") color = darkMode ? "#60a5fa" : "#3b82f6";
2207+
if (entry.name === "错误数") color = darkMode ? "#f87171" : "#ef4444";
22052208
if (entry.name === "Tokens") color = darkMode ? "#4ade80" : "#16a34a";
22062209
if (entry.name === "费用") color = "#fbbf24";
22072210

@@ -2241,10 +2244,11 @@ export default function DashboardPage() {
22412244
}}
22422245
itemSorter={(item: any) => ({ requests: 0, tokens: 1, cost: 2 } as Record<string, number>)[item?.dataKey] ?? 999}
22432246
/>
2247+
<Bar yAxisId={trendConfig.lineAxisMap.requests} dataKey="errors" name="错误数" fill={darkMode ? "#f87171" : "#ef4444"} fillOpacity={0.35} maxBarSize={22} legendType="none" />
22442248
<Line hide={!trendVisible.requests} yAxisId={trendConfig.lineAxisMap.requests} type="monotone" dataKey="requests" stroke={darkMode ? "#60a5fa" : "#3b82f6"} strokeWidth={2} name="请求数" dot={{ r: 3, fill: darkMode ? "#60a5fa" : "#3b82f6", stroke: "#fff", strokeWidth: 1, fillOpacity: 0.2 }} activeDot={{ r: 6, stroke: "#fff", strokeWidth: 2 }} />
22452249
<Line hide={!trendVisible.tokens} yAxisId={trendConfig.lineAxisMap.tokens} type="monotone" dataKey="tokens" stroke={darkMode ? "#4ade80" : "#16a34a"} strokeWidth={2} name="Tokens" dot={{ r: 3, fill: darkMode ? "#4ade80" : "#16a34a", stroke: "#fff", strokeWidth: 1, fillOpacity: 0.2 }} activeDot={{ r: 6, stroke: "#fff", strokeWidth: 2 }} />
22462250
<Line hide={!trendVisible.cost} yAxisId={trendConfig.lineAxisMap.cost} type="monotone" dataKey="cost" stroke="#fbbf24" strokeWidth={2} name="费用" dot={{ r: 3, fill: "#fbbf24", stroke: "#fff", strokeWidth: 1, fillOpacity: 0.2 }} activeDot={{ r: 6, stroke: "#fff", strokeWidth: 2 }} />
2247-
</LineChart>
2251+
</ComposedChart>
22482252
</ResponsiveContainer>
22492253
)}
22502254
{fullscreenChart === "pie" && overviewData && overviewData.models.length > 0 && (

lib/queries/overview.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type TotalsRow = {
2525
successCount: number;
2626
failureCount: number;
2727
};
28-
type DayAggRow = { label: string; requests: number; tokens: number };
28+
type DayAggRow = { label: string; requests: number; errors: number; tokens: number };
2929
type DayModelAggRow = { label: string; model: string; inputTokens: number; outputTokens: number; reasoningTokens: number; cachedTokens: number };
3030
type HourAggRow = {
3131
label: string;
@@ -151,6 +151,7 @@ export async function getOverview(
151151
.select({
152152
label: sql<string>`to_char(${dayExpr}, 'YYYY-MM-DD')`,
153153
requests: sql<number>`count(*)`,
154+
errors: sql<number>`coalesce(sum(case when ${usageRecords.isError} then 1 else 0 end), 0)`,
154155
tokens: sql<number>`sum(${usageRecords.totalTokens})`
155156
})
156157
.from(usageRecords)
@@ -277,6 +278,7 @@ export async function getOverview(
277278
const byDay: UsageSeriesPoint[] = [...byDayRows].reverse().map((row) => ({
278279
label: row.label,
279280
requests: toNumber(row.requests),
281+
errors: toNumber(row.errors),
280282
tokens: toNumber(row.tokens),
281283
cost: Number((dailyCostMap.get(row.label) ?? 0).toFixed(2))
282284
}));

lib/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export type ModelUsage = {
1717
export type UsageSeriesPoint = {
1818
label: string;
1919
requests: number;
20+
errors?: number;
2021
tokens: number;
2122
cost?: number;
2223
inputTokens?: number;

0 commit comments

Comments
 (0)