feat(status): what is wrong first — down monitors and active alerts, and since when (#5120)
CI / lint (push) Successful in 3s
CI / integration (push) Successful in 56s
CI / unit (push) Successful in 1m11s
CI / publish (push) Successful in 1m15s

#5120 "Status page: what is wrong first".

- A "Needs attention" card heads the Status board: each down monitor with
  how long it has been down, and each active alert (firing, acknowledged,
  pending, no data) with its condition and how long, linking to its history.
- StatusEntry.since / since_at_least: when the current up/down run began,
  from the recent checks (streak_start); "for at least" when every check we
  have agrees.
- Counts as stat tiles (Down, Active alerts, Pending, Up); names wrap
  instead of being cut off; dots carry their label.
- Uptime and TLS are coloured by status_class against Settings → Thresholds
  instead of literal cutoffs; heartbeat pills and DOWN/UP use tokens and
  status_badge rather than hex and bold.
- tests/core/test_status_page.py: streak_start cases, and the rows render
  with the problem card before the list, each since-time, threshold tones
  and no hex colour.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-06 14:40:45 -04:00
co-authored by Claude Opus 5.5
parent e864cd4b6c
commit fda54a3c31
5 changed files with 162 additions and 43 deletions
+16
View File
@@ -228,6 +228,13 @@ textarea { resize: vertical; }
.dot-crit { background: var(--red); }
.dot-info { background: var(--blue); }
.dot-label { margin-left: 0.35rem; font-size: 0.85rem; }
/* Text in a tone: status_class() results ('ok' / 'warn' / 'crit' / 'unknown')
and the STATES tones. */
.tone-ok { color: var(--green); }
.tone-warn { color: var(--yellow); }
.tone-crit { color: var(--red); }
.tone-info { color: var(--blue); }
.tone-dim, .tone-unknown { color: var(--text-dim); }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0; }
/* Empty state */
@@ -251,6 +258,13 @@ a.stat-tile:hover { border-color: var(--border-mid); background: var(--bg-elevat
.stat-crit .stat-tile-value { color: var(--red); }
.stat-info .stat-tile-value { color: var(--blue); }
.stat-dim .stat-tile-value { color: var(--text-dim); }
.stat-strip { display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: 0.75rem; margin-bottom: 1rem; }
/* "Needs attention": what is wrong, first on a page. */
.problem-card { border-color: color-mix(in srgb, var(--fs-error) 45%, var(--border)); }
.problem-list { list-style: none; display: flex; flex-direction: column; gap: 0.45rem; }
.problem-list li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.6rem; overflow-wrap: anywhere; }
.problem-detail { color: var(--text-muted); font-size: 0.82rem; }
/* Breadcrumbs */
/* Breadcrumb renders as a kicker line tucked directly above the page header
@@ -307,6 +321,8 @@ body.dash-editing .widget-drawer { transform:translateY(0); }
.pill { display:inline-block; width:7px; height:22px; border-radius:2px; cursor:default; transition:opacity .1s; }
.pill:hover { opacity:.75; }
.pill-empty { background:var(--bg-elevated); }
.pill-up { background:var(--green); }
.pill-down { background:var(--red); }
.ping-cur { min-width:72px; text-align:right; font-size:0.85rem; font-variant-numeric:tabular-nums; }
.ping-cur-good { color:var(--green); }
.ping-cur-warn { color:var(--yellow); }