From 4cc9205abfd79d9d1ad736f0db603d3c32a1edc3 Mon Sep 17 00:00:00 2001 From: Hugh Kaznowski Date: Mon, 7 Sep 2026 10:34:45 +0100 Subject: [PATCH] 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 Claude-Session: https://claude.ai/code/session_01TeyhRfMYjMditek6geDRoQ --- webapp/src/App.tsx | 5 +--- webapp/src/components/CollapsibleSection.tsx | 2 +- webapp/src/components/Dashboards.tsx | 2 +- webapp/src/components/LogTimeline.tsx | 27 +------------------- webapp/src/components/Metrics.tsx | 2 +- webapp/src/components/PanelChart.tsx | 2 +- webapp/src/lib/timeRange.ts | 25 ++++++++++++++++++ 7 files changed, 31 insertions(+), 34 deletions(-) create mode 100644 webapp/src/lib/timeRange.ts diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx index 0751f9f..a966e43 100644 --- a/webapp/src/App.tsx +++ b/webapp/src/App.tsx @@ -3,15 +3,12 @@ import { NavMenu } from './components/NavMenu' import { FilterBar } from './components/FilterBar' import { LogTimeline, - TIME_RANGE_PRESETS, computeBucketMs, - computePresetRange, loadLogSortSettings, saveLogSortSettings, type LogSortSettings, - type TimeRange, - type TimeRangePresetId, } from './components/LogTimeline' +import { TIME_RANGE_PRESETS, computePresetRange, type TimeRange, type TimeRangePresetId } from './lib/timeRange' import { LogPreview } from './components/LogPreview' import { Params } from './components/Params' import { Metrics } from './components/Metrics' diff --git a/webapp/src/components/CollapsibleSection.tsx b/webapp/src/components/CollapsibleSection.tsx index 2471da9..dc390ff 100644 --- a/webapp/src/components/CollapsibleSection.tsx +++ b/webapp/src/components/CollapsibleSection.tsx @@ -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. diff --git a/webapp/src/components/Dashboards.tsx b/webapp/src/components/Dashboards.tsx index 1d138a5..1b9e682 100644 --- a/webapp/src/components/Dashboards.tsx +++ b/webapp/src/components/Dashboards.tsx @@ -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, diff --git a/webapp/src/components/LogTimeline.tsx b/webapp/src/components/LogTimeline.tsx index 2d0e79b..3d61a49 100644 --- a/webapp/src/components/LogTimeline.tsx +++ b/webapp/src/components/LogTimeline.tsx @@ -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 diff --git a/webapp/src/components/Metrics.tsx b/webapp/src/components/Metrics.tsx index 81af441..0e6ca98 100644 --- a/webapp/src/components/Metrics.tsx +++ b/webapp/src/components/Metrics.tsx @@ -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, diff --git a/webapp/src/components/PanelChart.tsx b/webapp/src/components/PanelChart.tsx index 070e6db..2863253 100644 --- a/webapp/src/components/PanelChart.tsx +++ b/webapp/src/components/PanelChart.tsx @@ -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 { diff --git a/webapp/src/lib/timeRange.ts b/webapp/src/lib/timeRange.ts new file mode 100644 index 0000000..4be7642 --- /dev/null +++ b/webapp/src/lib/timeRange.ts @@ -0,0 +1,25 @@ +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 } +}