feat(dashboard): an Alerts widget beside the Overview; the Overview's overflow links to Alerts and Monitors (#5510)
CI / lint (push) Successful in 3s
CI / integration (push) Successful in 1m16s
CI / unit (push) Failing after 1m28s
CI / publish (push) Skipped

The Alerts widget (/alerts/widget) is shaped like every widget: a verdict
("2 firing, 1 silent." / "Nothing firing: all 12 rules are quiet."), then
what is firing or silent with its reading now, what the rule fires on and
since when, then what someone acknowledged and who; a line when alert
email failed in the last 7 days or is retrying (outbox.alert_trouble),
linking admins to Settings -> System; the quiet rules counted, a covering
rule once. It sits beside the Overview in the recommended layout (6x4
each).

The Overview's "+N more" line pointed at the removed /status/; alerts now
link to /alerts/ and monitors to /monitors/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-09 16:27:17 -04:00
co-authored by Claude Opus 5.5
parent defad2e98b
commit 57e1960dc1
10 changed files with 279 additions and 13 deletions
+13
View File
@@ -178,6 +178,19 @@ async def active_fragment():
rules_off=sum(1 for r in rules if not r.enabled))
@alerts_bp.get("/widget")
@require_role(UserRole.viewer)
async def widget():
"""The dashboard Alerts widget (#5510): a verdict, what is firing or
silent, then what someone has taken, a line when alert email is failing,
and the quiet rules counted."""
async with current_app.db_sessionmaker() as db:
active = await alert_svc.active_rows(db, datetime.now(timezone.utc))
view = alert_svc.widget_view(active, await alert_svc.list_rules(db))
email = await outbox.alert_trouble(db)
return await render_template("alerts/widget.html", **view, email=email)
def _delivery(label: str, sent: bool | None, error: str | None) -> dict | None:
"""A channel's outcome as recorded on the event; None when not tried."""
if sent is None: