diff --git a/frontend/src/views/user/UsageView.vue b/frontend/src/views/user/UsageView.vue index bbf58532ab..eb2b124839 100644 --- a/frontend/src/views/user/UsageView.vue +++ b/frontend/src/views/user/UsageView.vue @@ -677,7 +677,7 @@ const exportToCSV = async () => { headers.map(escapeCSVValue).join(','), ...rows.map((row) => row.join(',')), ].join('\n') - const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' }) + const blob = new Blob(['\uFEFF' + csvContent], { type: 'text/csv;charset=utf-8;' }) const url = window.URL.createObjectURL(blob) const link = document.createElement('a') link.href = url diff --git a/frontend/src/views/user/__tests__/UsageView.spec.ts b/frontend/src/views/user/__tests__/UsageView.spec.ts index 555ea4b0f6..23fc3c256a 100644 --- a/frontend/src/views/user/__tests__/UsageView.spec.ts +++ b/frontend/src/views/user/__tests__/UsageView.spec.ts @@ -237,6 +237,11 @@ describe('user UsageView', () => { })) expect(clickSpy).toHaveBeenCalled() expect(showSuccess).toHaveBeenCalled() + expect(csvContent.startsWith('\uFEFF')).toBe(true) + expect(csvContent.slice(1)).toBe([ + 'Time,API Key Name,Model,Reasoning Effort,Inbound Endpoint,IP Address,Type,Billing Mode,Input Tokens,Output Tokens,Cache Read Tokens,Cache Creation Tokens,Rate Multiplier,Billed Cost,Original Cost,First Token (ms),Duration (ms)', + '2026-03-08T00:00:00Z,demo-key,gpt-5.4,"\'-",,203.0.113.10,Sync,Token,4057,101,278272,4,1,0.09288300,0.09288300,12,345', + ].join('\n')) expect(csvContent).toContain('IP Address') expect(csvContent).toContain('203.0.113.10') expect(csvContent).toContain('Billed Cost')