fix: add missing commas and improve code formatting in TokenBreakdownChart and UsagePage

This commit is contained in:
Supra4E8C
2026-04-30 01:02:24 +08:00
parent 7b49063264
commit d1733497ba
2 changed files with 40 additions and 68 deletions
+28 -50
View File
@@ -1,13 +1,13 @@
import { useState, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import type { ChartData, ChartOptions } from 'chart.js';
import type { ChartData, ChartOptions, TooltipItem } from 'chart.js';
import { Bar } from 'react-chartjs-2';
import { Card } from '@/components/ui/Card';
import { Button } from '@/components/ui/Button';
import {
buildHourlyTokenBreakdown,
buildDailyTokenBreakdown,
type TokenCategory
type TokenCategory,
} from '@/utils/usage';
import { buildChartOptions, getHourChartMinWidth } from '@/utils/usage/chartConfig';
import type { UsagePayload } from './hooks/useUsageData';
@@ -17,7 +17,7 @@ const TOKEN_COLORS: Record<TokenCategory, string> = {
input: '#8CC21F',
output: '#FA6450',
cached: '#F5ED58',
reasoning: '#00ABA5'
reasoning: '#00ABA5',
};
const CATEGORIES: TokenCategory[] = ['input', 'output', 'cached', 'reasoning'];
@@ -41,7 +41,7 @@ export function TokenBreakdownChart({
loading,
isDark,
isMobile,
hourWindowHours
hourWindowHours,
}: TokenBreakdownChartProps) {
const { t } = useTranslation();
const [period, setPeriod] = useState<'hour' | 'day'>('hour');
@@ -55,54 +55,42 @@ export function TokenBreakdownChart({
input: t('usage_stats.input_tokens'),
output: t('usage_stats.output_tokens'),
cached: t('usage_stats.cached_tokens'),
reasoning: t('usage_stats.reasoning_tokens')
reasoning: t('usage_stats.reasoning_tokens'),
};
const categoryTotals: Record<TokenCategory, number> = {
input: 0,
output: 0,
cached: 0,
reasoning: 0
};
CATEGORIES.forEach((cat) => {
categoryTotals[cat] = series.dataByCategory[cat].reduce(
(sum, val) => sum + (Number(val) || 0),
0,
);
});
const data: ChartData<'bar'> = {
labels: series.labels,
datasets: CATEGORIES.map((cat) => ({
label: categoryLabels[cat],
categoryId: cat,
data: series.dataByCategory[cat],
backgroundColor: TOKEN_COLORS[cat],
borderColor: isDark ? '#0f172a' : '#ffffff',
borderWidth: 1,
borderSkipped: false,
grouped: false,
// Higher order is drawn earlier by Chart.js, so larger series stay behind smaller ones.
order: categoryTotals[cat],
categoryPercentage: 0.78,
barPercentage: 0.82
}))
grouped: true,
categoryPercentage: 0.82,
barPercentage: 0.88,
})),
};
const baseOptions = buildChartOptions({ period, labels: series.labels, isDark, isMobile }) as ChartOptions<'bar'>;
const baseOptions = buildChartOptions({
period,
labels: series.labels,
isDark,
isMobile,
}) as ChartOptions<'bar'>;
const options: ChartOptions<'bar'> = {
...baseOptions,
scales: {
...baseOptions.scales,
y: {
...baseOptions.scales?.y,
stacked: false
stacked: false,
},
x: {
...baseOptions.scales?.x,
stacked: false
}
stacked: false,
},
},
plugins: {
...baseOptions.plugins,
@@ -111,16 +99,13 @@ export function TokenBreakdownChart({
itemSort: (a, b) => a.datasetIndex - b.datasetIndex,
callbacks: {
...baseOptions.plugins?.tooltip?.callbacks,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
label: function (context: any) {
label: function (context: TooltipItem<'bar'>) {
const val = Number(context.raw) || 0;
const cat = context.dataset.categoryId;
const cat = CATEGORIES[context.datasetIndex];
let text = `${context.dataset.label}: ${formatTokens(val)}`;
if (cat === 'cached') {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const inputDs = context.chart.data.datasets.find((ds: any) => ds.categoryId === 'input');
const inputVal = inputDs ? (Number(inputDs.data[context.dataIndex]) || 0) : 0;
const inputVal = Number(series.dataByCategory.input[context.dataIndex]) || 0;
if (inputVal > 0) {
const perc = ((val / inputVal) * 100).toFixed(2);
text += ` (${perc}%)`;
@@ -128,19 +113,9 @@ export function TokenBreakdownChart({
}
return text;
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
footer: function (tooltipItems: any[]) {
let total = 0;
const dataIndex = tooltipItems[0].dataIndex;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
tooltipItems[0].chart.data.datasets.forEach((ds: any) => {
total += Number(ds.data[dataIndex]) || 0;
});
return `Total: ${formatTokens(total)}`;
}
}
}
}
},
},
},
};
return { chartData: data, chartOptions: options };
@@ -180,7 +155,10 @@ export function TokenBreakdownChart({
className={styles.legendItem}
title={dataset.label}
>
<span className={styles.legendDot} style={{ backgroundColor: TOKEN_COLORS[CATEGORIES[index]] }} />
<span
className={styles.legendDot}
style={{ backgroundColor: TOKEN_COLORS[CATEGORIES[index]] }}
/>
<span className={styles.legendLabel}>{dataset.label}</span>
</div>
))}
+12 -18
View File
@@ -10,7 +10,7 @@ import {
Title,
Tooltip,
Legend,
Filler
Filler,
} from 'chart.js';
import { Button } from '@/components/ui/Button';
import { LoadingSpinner } from '@/components/ui/LoadingSpinner';
@@ -34,14 +34,14 @@ import {
ServiceHealthCard,
useUsageData,
useSparklines,
useChartData
useChartData,
} from '@/components/usage';
import {
getModelNamesFromUsage,
getApiStats,
getModelStats,
filterUsageByTimeRange,
type UsageTimeRange
type UsageTimeRange,
} from '@/utils/usage';
import styles from './UsagePage.module.scss';
@@ -72,7 +72,7 @@ const TIME_RANGE_OPTIONS: ReadonlyArray<{ value: UsageTimeRange; labelKey: strin
const HOUR_WINDOW_BY_TIME_RANGE: Record<Exclude<UsageTimeRange, 'all'>, number> = {
'7h': 7,
'24h': 24,
'7d': 7 * 24
'7d': 7 * 24,
};
const isUsageTimeRange = (value: unknown): value is UsageTimeRange =>
@@ -145,7 +145,7 @@ export function UsagePage() {
handleImportChange,
importInputRef,
exporting,
importing
importing,
} = useUsageData();
useHeaderRefresh(loadUsage);
@@ -178,13 +178,13 @@ export function UsagePage() {
const openaiProvidersForUsage =
openaiProviderState && openaiProviderState.source === openaiCompatibilityConfig
? openaiProviderState.providers
: openaiCompatibilityConfig ?? [];
: (openaiCompatibilityConfig ?? []);
const timeRangeOptions = useMemo(
() =>
TIME_RANGE_OPTIONS.map((opt) => ({
value: opt.value,
label: t(opt.labelKey)
label: t(opt.labelKey),
})),
[t]
);
@@ -193,8 +193,7 @@ export function UsagePage() {
() => (usage ? filterUsageByTimeRange(usage, timeRange) : null),
[usage, timeRange]
);
const hourWindowHours =
timeRange === 'all' ? undefined : HOUR_WINDOW_BY_TIME_RANGE[timeRange];
const hourWindowHours = timeRange === 'all' ? undefined : HOUR_WINDOW_BY_TIME_RANGE[timeRange];
const handleChartLinesChange = useCallback((lines: string[]) => {
setChartLines(normalizeChartLines(lines));
@@ -225,13 +224,8 @@ export function UsagePage() {
const nowMs = lastRefreshedAt?.getTime() ?? 0;
// Sparklines hook
const {
requestsSparkline,
tokensSparkline,
rpmSparkline,
tpmSparkline,
costSparkline
} = useSparklines({ usage: filteredUsage, loading, nowMs });
const { requestsSparkline, tokensSparkline, rpmSparkline, tpmSparkline, costSparkline } =
useSparklines({ usage: filteredUsage, loading, nowMs });
// Chart data hook
const {
@@ -242,7 +236,7 @@ export function UsagePage() {
requestsChartData,
tokensChartData,
requestsChartOptions,
tokensChartOptions
tokensChartOptions,
} = useChartData({ usage: filteredUsage, chartLines, isDark, isMobile, hourWindowHours });
// Derived data
@@ -336,7 +330,7 @@ export function UsagePage() {
tokens: tokensSparkline,
rpm: rpmSparkline,
tpm: tpmSparkline,
cost: costSparkline
cost: costSparkline,
}}
/>