feat(dashboard): an Alerts widget beside the Overview; the Overview's overflow links to Alerts and Monitors (#5510)
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:
@@ -28,8 +28,11 @@ def test_retired_widgets_point_at_the_one_that_replaced_them():
|
||||
def test_the_recommended_layout_fills_rows_without_overlap():
|
||||
items = recommended_layout(ALL_ON)
|
||||
assert [i["key"] for i in items] == [key for key, *_ in RECOMMENDED]
|
||||
assert items[0] == {"key": "status_overview", "x": 0, "y": 0, "w": 12, "h": 3, "config": {}}
|
||||
assert (items[1]["x"], items[1]["y"], items[2]["x"], items[2]["y"]) == (0, 3, 6, 3)
|
||||
# The Overview and the Alerts side by side first (#5510), then the fleet.
|
||||
assert items[0] == {"key": "status_overview", "x": 0, "y": 0, "w": 6, "h": 4, "config": {}}
|
||||
assert items[1] == {"key": "alerts", "x": 6, "y": 0, "w": 6, "h": 4, "config": {}}
|
||||
assert [(i["key"], i["x"], i["y"]) for i in items[2:4]] == [
|
||||
("hosts_overview", 0, 4), ("monitors", 6, 4)]
|
||||
for i, a in enumerate(items):
|
||||
assert a["x"] + a["w"] <= 12
|
||||
assert not any(_overlap(a, b) for b in items[i + 1:]), a
|
||||
@@ -37,8 +40,8 @@ def test_the_recommended_layout_fills_rows_without_overlap():
|
||||
|
||||
def test_the_recommended_layout_skips_what_is_not_enabled():
|
||||
items = recommended_layout({})
|
||||
assert [i["key"] for i in items] == ["status_overview", "hosts_overview", "monitors"]
|
||||
assert [(i["x"], i["y"]) for i in items] == [(0, 0), (0, 3), (6, 3)]
|
||||
assert [i["key"] for i in items] == ["status_overview", "alerts", "hosts_overview", "monitors"]
|
||||
assert [(i["x"], i["y"]) for i in items] == [(0, 0), (6, 0), (0, 4), (6, 4)]
|
||||
|
||||
|
||||
def test_overview_alerts_put_the_breaching_first():
|
||||
@@ -77,6 +80,26 @@ async def test_the_overview_labels_its_counts_and_lists_what_is_wrong(app):
|
||||
assert not hex_colours(page), hex_colours(page)
|
||||
|
||||
|
||||
async def test_the_overviews_overflow_links_to_alerts_and_to_monitors(app):
|
||||
"""#5510: what doesn't fit goes to its own list; /status/ is gone."""
|
||||
now = datetime.now(timezone.utc)
|
||||
alerts = [{"rule": SimpleNamespace(id=f"r{i}", name=f"rule {i}"), "label": "firing", "since": now}
|
||||
for i in range(7)]
|
||||
entries = [StatusEntry(kind="tcp", key=str(i), name=f"m{i}", status="down", detail_url=f"/monitors/{i}")
|
||||
for i in range(10)]
|
||||
async with app.test_request_context("/status/widget"):
|
||||
session["user_id"] = "u1"
|
||||
page = await render_template("status/widget.html", entries=entries, up=0, down=10, pending=0,
|
||||
total_hosts=1, alerts=alerts, widget_id="1")
|
||||
assert '<a href="/alerts/">+1 more alerts</a> · <a href="/monitors/">+2 more monitors</a>' in page
|
||||
assert "/status/" not in page
|
||||
async with app.test_request_context("/status/widget"):
|
||||
page = await render_template("status/widget.html", entries=entries[:3], up=0, down=3, pending=0,
|
||||
total_hosts=1, alerts=alerts, widget_id="1")
|
||||
assert "+1 more alerts" in page and "more monitors" not in page
|
||||
assert 'href="/alerts/"' not in page # the share view has no links
|
||||
|
||||
|
||||
async def test_a_quiet_overview_says_so(app):
|
||||
async with app.test_request_context("/status/widget"):
|
||||
page = await render_template("status/widget.html", entries=[], up=0, down=0, pending=0,
|
||||
|
||||
Reference in New Issue
Block a user