Skip to content

Commit 682da5b

Browse files
authored
fix(dashboard): format chart timestamps as readable time labels (#403)
1 parent 7fc63da commit 682da5b

2 files changed

Lines changed: 28 additions & 11 deletions

File tree

packages/frontend/src/components/dashboard/tabs/UsageTab.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import { useEffect, useMemo, useState } from 'react';
2929
* from the `GET /v0/management/concurrency?timeRange=...` endpoint.
3030
*/
3131
import { api, UsageData, PieChartDataPoint, type ConcurrencyData } from '../../../lib/api';
32-
import { formatNumber, formatTokens } from '../../../lib/format';
32+
import { formatNumber, formatTokens, formatTimeLabel, formatDateTimeLabel } from '../../../lib/format';
3333
import { Card } from '../../ui/Card';
3434
import { TotalEnergyComparison } from '../../TotalEnergyComparison';
3535
import { TimeRangeSelector } from '../TimeRangeSelector';
@@ -432,14 +432,15 @@ export const UsageTab: React.FC<UsageTabProps> = ({
432432
<ResponsiveContainer width="100%" height="100%">
433433
<AreaChart data={data}>
434434
<CartesianGrid strokeDasharray="3 3" stroke="var(--color-border-glass)" />
435-
<XAxis dataKey="timestamp" stroke="var(--color-text-secondary)" />
435+
<XAxis dataKey="timestamp" stroke="var(--color-text-secondary)" tickFormatter={(v) => formatTimeLabel(String(v))} />
436436
<YAxis stroke="var(--color-text-secondary)" tickFormatter={formatNumber} />
437437
<Tooltip
438438
contentStyle={{
439439
backgroundColor: 'var(--color-bg-card)',
440440
borderColor: 'var(--color-border)',
441441
color: 'var(--color-text)',
442442
}}
443+
labelFormatter={(label) => formatDateTimeLabel(String(label))}
443444
formatter={(value) => formatNumber(value as number)}
444445
/>
445446
<Area
@@ -576,14 +577,15 @@ export const UsageTab: React.FC<UsageTabProps> = ({
576577
<ResponsiveContainer width="100%" height="100%">
577578
<AreaChart data={data}>
578579
<CartesianGrid strokeDasharray="3 3" stroke="var(--color-border-glass)" />
579-
<XAxis dataKey="timestamp" stroke="var(--color-text-secondary)" />
580+
<XAxis dataKey="timestamp" stroke="var(--color-text-secondary)" tickFormatter={(v) => formatTimeLabel(String(v))} />
580581
<YAxis stroke="var(--color-text-secondary)" tickFormatter={formatTokens} />
581582
<Tooltip
582583
contentStyle={{
583584
backgroundColor: 'var(--color-bg-card)',
584585
borderColor: 'var(--color-border)',
585586
color: 'var(--color-text)',
586587
}}
588+
labelFormatter={(label) => formatDateTimeLabel(String(label))}
587589
formatter={(value) => formatTokens(value as number)}
588590
/>
589591
<Legend />

packages/frontend/src/lib/format.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -155,19 +155,34 @@ export function formatPercent(value: number, decimals: number = 1): string {
155155
* Handles ISO strings and epoch-millisecond numeric strings.
156156
*/
157157
export function formatTimeLabel(timestamp: string): string {
158-
const date = new Date(timestamp);
159-
if (!isNaN(date.getTime())) {
160-
return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
158+
const date = parseTimestamp(timestamp);
159+
if (date) return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
160+
return timestamp;
161+
}
162+
163+
/**
164+
* Format a timestamp string into a detailed date-time label for chart tooltips.
165+
* Includes date and time (e.g., "2025/05/15 14:00").
166+
*/
167+
export function formatDateTimeLabel(timestamp: string): string {
168+
const date = parseTimestamp(timestamp);
169+
if (date) {
170+
const dateStr = date.toLocaleDateString([], { year: 'numeric', month: '2-digit', day: '2-digit' });
171+
const timeStr = date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
172+
return `${dateStr} ${timeStr}`;
161173
}
162-
// Try parsing as a numeric string (epoch ms)
174+
return timestamp;
175+
}
176+
177+
function parseTimestamp(timestamp: string): Date | null {
178+
const date = new Date(timestamp);
179+
if (!isNaN(date.getTime())) return date;
163180
const num = Number(timestamp);
164181
if (!isNaN(num)) {
165182
const dateFromNum = new Date(num);
166-
if (!isNaN(dateFromNum.getTime())) {
167-
return dateFromNum.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
168-
}
183+
if (!isNaN(dateFromNum.getTime())) return dateFromNum;
169184
}
170-
return timestamp;
185+
return null;
171186
}
172187

173188
/**

0 commit comments

Comments
 (0)