Merge ticket/023-dashboards-refresh-with-ui
Resolves a POLL_MS import conflict with ticket 025 in App.tsx. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XFUNZwCvz3kcod3352ZR2o
This commit is contained in:
@@ -1,11 +0,0 @@
|
|||||||
# Dashboards should refresh when the rest of the UI refreshes
|
|
||||||
|
|
||||||
`App.tsx` polls logs/timeline every `POLL_MS` (2000ms — `src/App.tsx`) and, for rolling range presets, recomputes `range.startMs`/`range.endMs` on each tick. Dashboard panels (`PanelPreview` in `Dashboards.tsx`) only refetch their PromQL series when `panel.promql`, `panel.chart_type`, `range.startMs`, or `range.endMs` change (`useEffect` dependency array, `Dashboards.tsx` ~line 78). So a dashboard panel only refreshes if the rolling range's bounds actually move; with a fixed/absolute range selected, or between rolling-preset ticks where the resolved bounds happen not to change, panels go stale even though the rest of the UI (log table, timeline) is actively re-polling.
|
|
||||||
|
|
||||||
Dashboards should refresh on the same cadence as the rest of the app, not only when their `range` prop happens to change value.
|
|
||||||
|
|
||||||
## Acceptance criteria
|
|
||||||
- Dashboard panels (`PanelPreview`, and by extension `AllDashboardsView`) refetch their PromQL series on the same polling cadence as `App.tsx`'s log/timeline polling, independent of whether `range.startMs`/`range.endMs` literally changed.
|
|
||||||
- This applies both to the normal `Dashboards` section and the popped-out dashboard view, if it polls independently.
|
|
||||||
- No duplicate/overlapping fetches are introduced (e.g. a range change and the periodic tick firing at the same time shouldn't double-fetch).
|
|
||||||
- Existing refetch-on-range/query/chart-type-change behavior is preserved.
|
|
||||||
+1
-2
@@ -20,8 +20,7 @@ import {
|
|||||||
} from './lib/api'
|
} from './lib/api'
|
||||||
import { DocsNavProvider } from './lib/docsNav'
|
import { DocsNavProvider } from './lib/docsNav'
|
||||||
import { useSettingsPanel } from './lib/useSettingsPanel'
|
import { useSettingsPanel } from './lib/useSettingsPanel'
|
||||||
|
import { POLL_MS } from './lib/polling'
|
||||||
const POLL_MS = 2000
|
|
||||||
|
|
||||||
// Height (px) the log viewer had before it became resizable — kept as the default so existing
|
// Height (px) the log viewer had before it became resizable — kept as the default so existing
|
||||||
// layouts don't jump on first load after this change.
|
// layouts don't jump on first load after this change.
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
type Panel,
|
type Panel,
|
||||||
type PromqlSeries,
|
type PromqlSeries,
|
||||||
} from '../lib/api'
|
} from '../lib/api'
|
||||||
|
import { POLL_MS } from '../lib/polling'
|
||||||
import {
|
import {
|
||||||
CollapseToggle,
|
CollapseToggle,
|
||||||
handleSectionHeaderKeyDown,
|
handleSectionHeaderKeyDown,
|
||||||
@@ -44,11 +45,22 @@ function newPanelId(): string {
|
|||||||
return `panel-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
return `panel-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetches the panel's PromQL series on query/chart-type/range change; a malformed query must render as an inline error rather than crash the page.
|
/// Fetches the panel's PromQL series on query/chart-type/range change, and also refetches on
|
||||||
|
/// the same cadence as the rest of the app's polling (`POLL_MS`) so a panel doesn't go stale
|
||||||
|
/// between rolling-preset ticks or under a fixed range. `tick` is the only thing that interval
|
||||||
|
/// touches; it's folded into this single effect's dependency array (rather than driving a
|
||||||
|
/// second, independent fetch effect) so a range change landing on the same tick still results
|
||||||
|
/// in exactly one fetch instead of two racing ones.
|
||||||
function PanelPreview({ panel, range }: { panel: Panel; range: TimeRange | null }) {
|
function PanelPreview({ panel, range }: { panel: Panel; range: TimeRange | null }) {
|
||||||
const [series, setSeries] = useState<PromqlSeries[]>([])
|
const [series, setSeries] = useState<PromqlSeries[]>([])
|
||||||
const [error, setError] = useState<string | null>(null)
|
const [error, setError] = useState<string | null>(null)
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
|
const [tick, setTick] = useState(0)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const id = setInterval(() => setTick((t) => t + 1), POLL_MS)
|
||||||
|
return () => clearInterval(id)
|
||||||
|
}, [])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!panel.promql.trim()) {
|
if (!panel.promql.trim()) {
|
||||||
@@ -75,7 +87,8 @@ function PanelPreview({ panel, range }: { panel: Panel; range: TimeRange | null
|
|||||||
return () => {
|
return () => {
|
||||||
cancelled = true
|
cancelled = true
|
||||||
}
|
}
|
||||||
}, [panel.promql, panel.chart_type, range?.startMs, range?.endMs])
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [panel.promql, panel.chart_type, range?.startMs, range?.endMs, tick])
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return <div className="rounded border border-danger-border bg-danger-bg/50 px-2 py-1 text-xs text-danger-text">{error}</div>
|
return <div className="rounded border border-danger-border bg-danger-bg/50 px-2 py-1 text-xs text-danger-text">{error}</div>
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
/// Shared polling cadence for the whole app — the log/timeline poll in `App.tsx` and any
|
||||||
|
/// other section that should refresh on the same rhythm (e.g. dashboard panels) import this
|
||||||
|
/// instead of hardcoding their own interval, so they can never drift apart.
|
||||||
|
export const POLL_MS = 2000
|
||||||
Reference in New Issue
Block a user