mirror of
https://github.com/router-for-me/Cli-Proxy-API-Management-Center.git
synced 2026-09-24 16:04:35 +08:00
fix: add missing commas and improve code formatting in TokenBreakdownChart and UsagePage
This commit is contained in:
@@ -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
@@ -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,
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user