feat(console/charts): x-axis tick adapts to time-range duration#21
Conversation
Every chart had its own copy of:
function formatAxisTime(epoch) {
const d = new Date(epoch * 1000)
return `${HH}:${MM}`
}
Result: a 7-day window rendered ticks as a wrapping clock face
("00:00", "12:00", "00:00", "12:00", ...) with no day attached.
Same problem at 24h. Easy to mis-read.
Centralize the formatter in lib/format as `formatAxisTime(epoch, span)`
and have it pick the right shape based on the visible window:
span < 24h → HH:MM (5m / 15m / 1h / 6h presets)
24h ≤ span < 7d → MM-DD HH:MM (24h preset)
span ≥ 7d → MM-DD (7d preset; time-of-day is noise
when ticks come ~daily)
Each chart derives span from its data (last timestamp − first), so the
formatter requires no toolbar dependency and naturally handles partial
ranges (e.g. tail of a 7d window after retention trimmed the head).
Replaces the inline copies in:
- timeseries-line-chart (Overview latency, Models, Performance)
- request-volume-chart (Overview)
- latency-overview-chart (Overview)
- stacked-bar-chart (Performance, Traffic)
6 unit tests in lib/format.test.ts cover each duration bucket plus the
24h / 7d inclusive boundaries and the single-point fallback (span = 0
→ HH:MM). Tests assert *shape* not literal values so they pass under
any TZ.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
# Conflicts: # console/src/components/charts/timeseries-line-chart.tsx
There was a problem hiding this comment.
Based on my review of the PR diff and all affected files, here is my assessment:
Summary
This PR centralizes chart x-axis time formatting by replacing 4 inline formatAxisTime functions with a single adaptive formatter in lib/format.ts. The formatter adjusts output based on data span (<24h → HH:MM, 24h-7d → MM-DD HH:MM, ≥7d → MM-DD). Six unit tests cover duration buckets and boundary conditions. The change is well-scoped and directly addresses the problem stated in the commit message. APPROVE.
Verified
- Schema contract:
TimeseriesData.timestampsare epoch seconds (i64 in Rust). The formatter correctly multiplies by 1000 fornew Date(). Matches the API contract ints-api/src/routes/metrics.rs:42-44. - Import correctness: All 4 chart components now import
formatAxisTimefrom@/lib/formatand pass the correct signature(epochSec, spanSec). - No duplicate definitions: Grepped for
function formatAxisTime— only the centralized export informat.ts:18remains. - Span calculation: All 4 charts compute
spanSecidentically fromlastTs - firstTswith a0fallback for single-point data. Matches test coverage. - Test coverage: Tests assert shape (regex) rather than literal values, correctly handling TZ independence. Boundary tests at exactly 24h and 7d match the implementation's
<comparisons. - No build config changes: No changes to
package.json,vite.config.ts, ortsconfig.json.
Suggestions
-
console/src/lib/format.ts:26 — The MM-DD format loses year context. For a 7-day window spanning Dec 31 → Jan 1, ticks would show "12-31" → "01-01" without year. This is acceptable for a 7d window, but if longer windows are added later (30d/90d), consider adding year prefix. Not blocking.
-
console/src/lib/format.ts:18-31 — Consider documenting that this uses local timezone (via
Date.getHours()/getMonth()). The test file already notes this, but a brief doc comment in the function would help future readers. Non-blocking.
🤖 Reviewed by vivi • workflow run
Why
Every chart had its own copy of `formatAxisTime` that always rendered `HH:MM`. A 7-day window therefore showed ticks like `00:00, 12:00, 00:00, 12:00, ...` — a wrapping clock face with no day attached. Easy to mis-read. Same problem at 24h.
What
Centralize `formatAxisTime(epoch, span)` in `lib/format` and pick the shape from the visible window:
Each chart derives `span` from its own data (`timestamps[last] - timestamps[0]`), so the formatter has no toolbar coupling and naturally handles partial windows (e.g. tail of a 7d range after retention trimmed the head).
Refactors the four charts that had inline copies:
Verification
Test plan
🤖 Generated with Claude Code