Move TimeRange types to lib/timeRange to break import cycle

CollapsibleSection.tsx and LogTimeline.tsx imported from each other
(TimeRange/TimeRangePresetId one way, sectionHeaderClassName the
other). Move the range types/helpers into a dependency-free
webapp/src/lib/timeRange.ts and repoint all importers there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TeyhRfMYjMditek6geDRoQ
This commit is contained in:
Hugh Kaznowski
2026-09-07 10:35:05 +01:00
co-authored by Claude Sonnet 5
parent a47f771087
commit 4cc9205abf
7 changed files with 31 additions and 34 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
import { useState } from 'react'
import type { TimeRange, TimeRangePresetId } from './LogTimeline'
import type { TimeRange, TimeRangePresetId } from '../lib/timeRange'
/// Shared look for a primary section's title bar: shaded so it reads as a
/// distinct band rather than blending into the section body below it.
+1 -1
View File
@@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from 'react'
import type { TimeRange, TimeRangePresetId } from './LogTimeline'
import type { TimeRange, TimeRangePresetId } from '../lib/timeRange'
import { PanelChart } from './PanelChart'
import {
ApiError,
+1 -26
View File
@@ -1,34 +1,9 @@
import { useRef, useState } from 'react'
import type { TimelineBucket } from '../lib/api'
import { TIME_RANGE_PRESETS, type TimeRange, type TimeRangePresetId } from '../lib/timeRange'
import { sectionHeaderClassName, sectionHeaderLeftClassName } from './CollapsibleSection'
import { HelpIcon } from './HelpIcon'
export interface TimeRange {
startMs: number
endMs: number
}
// `'all'` clears the range (the implicit default); the rest resolve to a window ending "now" at the moment they're (re-)computed — see `computePresetRange`.
export type TimeRangePresetId = 'all' | '1m' | '2m' | '5m' | '15m' | '1h' | '24h'
export const TIME_RANGE_PRESETS: { id: TimeRangePresetId; label: string; ms: number | null }[] = [
{ id: 'all', label: 'All', ms: null },
{ id: '1m', label: '1m', ms: 60 * 1000 },
{ id: '2m', label: '2m', ms: 2 * 60 * 1000 },
{ id: '5m', label: '5m', ms: 5 * 60 * 1000 },
{ id: '15m', label: '15m', ms: 15 * 60 * 1000 },
{ id: '1h', label: '1h', ms: 60 * 60 * 1000 },
{ id: '24h', label: '24h', ms: 24 * 60 * 60 * 1000 },
]
// Callers re-invoke this on their existing poll cadence (see `App.tsx`) to keep a rolling preset's window moving with time, rather than freezing it at selection time.
export function computePresetRange(id: TimeRangePresetId): TimeRange | null {
const preset = TIME_RANGE_PRESETS.find((p) => p.id === id)
if (!preset || preset.ms == null) return null
const now = Date.now()
return { startMs: now - preset.ms, endMs: now }
}
/// Buckets a fixed range into this many slots, so e.g. a rolling `5m` preset always requests a resolution fine enough to visibly slide instead of a handful of coarse, jumpy bars.
const TIMELINE_TARGET_BUCKETS = 60
+1 -1
View File
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
import type { TimeRange, TimeRangePresetId } from './LogTimeline'
import type { TimeRange, TimeRangePresetId } from '../lib/timeRange'
import {
ApiError,
declareMetric,
+1 -1
View File
@@ -14,7 +14,7 @@ import {
YAxis,
} from 'recharts'
import type { ChartType, PromqlSeries } from '../lib/api'
import type { TimeRange } from './LogTimeline'
import type { TimeRange } from '../lib/timeRange'
/// Renders a label combination as a series key/legend string, e.g. `{"path":"/a"}` -> `path=/a`; no labels renders as `value`.
export function seriesLabel(labels: Record<string, string>): string {