feat(alerts): live refresh table with toggle + severity colour cues #11

Merged
steve merged 2 commits from alerts-live-refresh into main 2026-05-05 07:42:22 +01:00
Owner

Adds the auto-refresh feature on /alerts (5s poll, paused when tab is hidden or toggle is off), plus tints the severity-filter dropdown options to match the row colours.

Why

Alerts is the one screen where staleness is genuinely harmful — an operator can be looking at an Open tab that's already been resolved by another admin or auto-resolved by the engine, and act on a row that no longer exists.

Severity dropdown previously named a concept that the table itself didn't render (kind column shows kind only, severity is encoded as colour). Tinting the dropdown options bridges the gap.

Changes

Live refresh (web/templates/pages/alerts.html, internal/server/http/ui_alerts.go):

  • htmx hx-get polls the same URL every 5s, hx-select="#alerts-table" swaps just the table panel — filter strip + page header don't flash
  • Polling lives on the table div, not the page root
  • Trigger predicate: document.visibilityState==='visible' && localStorage.getItem('rm-alerts-live')!=='off' — pauses on hidden tabs and when the operator turns it off
  • Checkbox in the table header writes rm-alerts-live=on|off to localStorage; choice survives full-page navigation
  • Live dot dims to 0.3 opacity when paused
  • alertsPage.RefreshURL = r.URL.RequestURI() keeps any status/severity/host_id/q params intact across refreshes

Severity colour cues (web/templates/pages/alerts.html):

  • Each <option> carries an inline color: matching the row's oklch tint (info=neutral, warning=orange, critical=red), plus a leading
  • Chrome + Firefox honour the styling; Safari falls back to plain text (acceptable degradation)

Other screens (dashboard, hosts, jobs) deliberately stay manual-refresh per the project's anti-flicker stance.

Test plan

  • /alerts loads with live ON by default; raise an alert via cmd/_fake_alert from another shell, watch it appear within ~5s
  • Toggle off, raise another alert, confirm the table doesn't move
  • Reload the page, toggle stays off
  • Background the tab, raise an alert, foreground — refresh fires once on visibility change
  • Apply a severity=warning filter, confirm the option text in the dropdown is orange
  • All existing tests pass (go test ./...)
Adds the auto-refresh feature on /alerts (5s poll, paused when tab is hidden or toggle is off), plus tints the severity-filter dropdown options to match the row colours. ## Why Alerts is the one screen where staleness is genuinely harmful — an operator can be looking at an Open tab that's already been resolved by another admin or auto-resolved by the engine, and act on a row that no longer exists. Severity dropdown previously named a concept that the table itself didn't render (kind column shows kind only, severity is encoded as colour). Tinting the dropdown options bridges the gap. ## Changes **Live refresh** (`web/templates/pages/alerts.html`, `internal/server/http/ui_alerts.go`): - htmx `hx-get` polls the same URL every 5s, `hx-select="#alerts-table"` swaps just the table panel — filter strip + page header don't flash - Polling lives on the table div, not the page root - Trigger predicate: `document.visibilityState==='visible' && localStorage.getItem('rm-alerts-live')!=='off'` — pauses on hidden tabs and when the operator turns it off - Checkbox in the table header writes `rm-alerts-live=on|off` to localStorage; choice survives full-page navigation - Live dot dims to 0.3 opacity when paused - `alertsPage.RefreshURL = r.URL.RequestURI()` keeps any status/severity/host_id/q params intact across refreshes **Severity colour cues** (`web/templates/pages/alerts.html`): - Each `<option>` carries an inline `color:` matching the row's oklch tint (info=neutral, warning=orange, critical=red), plus a leading `●` - Chrome + Firefox honour the styling; Safari falls back to plain text (acceptable degradation) Other screens (dashboard, hosts, jobs) deliberately stay manual-refresh per the project's anti-flicker stance. ## Test plan - [ ] /alerts loads with live ON by default; raise an alert via cmd/_fake_alert from another shell, watch it appear within ~5s - [ ] Toggle off, raise another alert, confirm the table doesn't move - [ ] Reload the page, toggle stays off - [ ] Background the tab, raise an alert, foreground — refresh fires once on visibility change - [ ] Apply a severity=warning filter, confirm the option text in the dropdown is orange - [ ] All existing tests pass (`go test ./...`)
steve added 2 commits 2026-05-05 07:37:47 +01:00
The alerts list is the one screen where staleness is genuinely
harmful — an operator can be looking at an Open tab that's already
been resolved by another admin or auto-resolved by the engine, and
take action on a row that no longer exists.

Add an htmx poll on just the table panel:

  hx-get        same URL with current querystring (filters preserved)
  hx-trigger    every 15s, only when document is visible (no idle CPU)
  hx-select     #alerts-table — pull this element out of the response
  hx-swap       outerHTML

Polling lives on the table div, not the page root, so the filter
strip and header don't flash on each tick. Header gains a small
'live ●' label so the polling is discoverable.

RefreshURL is r.URL.RequestURI() on the server side — keeps any
status/severity/host_id/q params intact across refreshes.

Other screens (dashboard, hosts, jobs) deliberately stay manual-
refresh per the project's anti-flicker stance.
alerts: 5s polling cadence + live toggle + severity colour cues
CI / Build (windows/amd64) (pull_request) Successful in 23s
CI / Lint (pull_request) Successful in 38s
CI / Build (linux/amd64) (pull_request) Successful in 21s
CI / Build (linux/arm64) (pull_request) Successful in 23s
CI / Test (linux/amd64) (pull_request) Successful in 2m57s
8813e93317
Two operator-visible changes on /alerts:

1. Polling drops from 15s to 5s and gains a checkbox in the table
   header to turn live monitoring on/off. Choice is persisted in
   localStorage so it survives full-page navigations. The toggle
   state is woven into the htmx hx-trigger predicate, so flipping
   the checkbox just sets the flag and the next tick (or the
   absence of one) honours it — no attribute juggling, no
   htmx.process re-init. The dot dims to 0.3 opacity when paused
   so operators can see at a glance that they're looking at a
   stale view.

2. Severity dropdown options pick up the same oklch tints used by
   the row dots / left borders / kind chips. The kind column shows
   only the kind text, so without a colour cue the dropdown
   mentioned a concept (severity) that the table itself didn't
   render. Now the colours bridge the gap.

Note on <option> styling: Chrome and Firefox honour inline color:
on options; Safari ignores it. Acceptable degradation — falls back
to plain text, which is what we had.
steve merged commit cb3260b89c into main 2026-05-05 07:42:22 +01:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: steve/restic-manager#11