Merge pull request #5243 from wucm667/feat/issue-5240-dashboard-username

fix(dashboard): show usernames in spending ranking
This commit is contained in:
Wesley Liddick
2026-08-04 16:15:21 +08:00
committed by GitHub
6 changed files with 40 additions and 22 deletions
@@ -422,6 +422,7 @@ const otherRankingItem = computed<RankingDisplayItem | null>(() => {
return {
user_id: 0,
email: '',
username: '',
actual_cost: otherActualCost,
requests: otherRequests,
tokens: otherTokens,
@@ -495,7 +496,8 @@ const formatNumber = (value: number): string => {
}
const getRankingUserLabel = (item: UserSpendingRankingItem): string => {
if (item.email) return item.email
if (item.username?.trim()) return item.username.trim()
if (item.email?.trim()) return item.email.trim()
return t('admin.redeem.userPrefix', { id: item.user_id })
}
@@ -30,7 +30,12 @@ vi.mock('vue-i18n', async () => {
return {
...actual,
useI18n: () => ({
t: (key: string) => messages[key] ?? key,
t: (key: string, params?: Record<string, unknown>) => {
const message = messages[key] ?? key
return params
? message.replace(/\{(\w+)\}/g, (_, name: string) => String(params[name]))
: message
},
}),
}
})
@@ -145,14 +150,15 @@ describe('ModelDistributionChart', () => {
expect(wrapper.findAll('tbody tr')[0].findAll('td')).toHaveLength(5)
})
it('renders Others in the spending ranking table and uses a dedicated chart color', async () => {
it('uses the dashboard user label policy and renders Others with a dedicated chart color', async () => {
const wrapper = mount(ModelDistributionChart, {
props: {
modelStats: [],
enableRankingView: true,
rankingItems: [
{ user_id: 1, email: 'alpha@example.com', actual_cost: 12, requests: 10, tokens: 1000 },
{ user_id: 2, email: 'beta@example.com', actual_cost: 8, requests: 6, tokens: 600 },
{ user_id: 1, email: 'alpha@example.com', username: 'alpha', actual_cost: 12, requests: 10, tokens: 1000 },
{ user_id: 2, email: 'beta@example.com', username: ' ', actual_cost: 8, requests: 6, tokens: 600 },
{ user_id: 3, email: ' ', username: '', actual_cost: 0, requests: 0, tokens: 0 },
],
rankingTotalActualCost: 30,
rankingTotalRequests: 20,
@@ -171,20 +177,25 @@ describe('ModelDistributionChart', () => {
const chartData = JSON.parse(wrapper.find('.chart-data').text())
expect(chartData.labels).toEqual([
'#1 alpha@example.com',
'#1 alpha',
'#2 beta@example.com',
'#3 User #3',
'Others',
])
expect(chartData.datasets[0].data).toEqual([12, 8, 10])
expect(chartData.datasets[0].data).toEqual([12, 8, 0, 10])
expect(chartData.datasets[0].backgroundColor[0]).toBe('#3b82f6')
expect(chartData.datasets[0].backgroundColor[2]).toBe('#94a3b8')
expect(chartData.datasets[0].backgroundColor[2]).not.toBe(chartData.datasets[0].backgroundColor[0])
expect(chartData.datasets[0].backgroundColor[3]).toBe('#94a3b8')
expect(chartData.datasets[0].backgroundColor[3]).not.toBe(chartData.datasets[0].backgroundColor[0])
const rows = wrapper.findAll('tbody tr')
expect(rows).toHaveLength(3)
expect(rows[2].text()).toContain('Others')
expect(rows[2].text()).toContain('4')
expect(rows[2].text()).toContain('400')
expect(rows[2].text()).toContain('$10.00')
expect(rows).toHaveLength(4)
expect(rows[0].text()).toContain('alpha')
expect(rows[0].text()).not.toContain('alpha@example.com')
expect(rows[1].text()).toContain('beta@example.com')
expect(rows[2].text()).toContain('User #3')
expect(rows[3].text()).toContain('Others')
expect(rows[3].text()).toContain('4')
expect(rows[3].text()).toContain('400')
expect(rows[3].text()).toContain('$10.00')
})
})
+1
View File
@@ -1855,6 +1855,7 @@ export interface UserUsageTrendPoint {
export interface UserSpendingRankingItem {
user_id: number
email: string
username: string
actual_cost: number
requests: number
tokens: number