Rebuild the dashboard around what needs a person

The board was one card per source in a fixed grid: every source got the same
small box whatever it had to say (warranties showed 5 of 50), and a box per
source flattened priority, while the largest type on the page belonged to counts
that need nobody.

Now a severity rollup, one worst-first queue of everything needing a person, and
a row of context beneath. The queue is full width, capped per source so a noisy
check cannot bury a rare one, and names what did not fit with a link to it. Chips
filter the queue by check as well as by severity; both narrow what is already
loaded, so the counts and rows cannot disagree.

Dropped: an 'All assets' tile shown beside its own four addends, a printer strip
carrying the same count a third time, three cards whose job was to display 0, and
a Recent Devices table listing five machines all 'In Use'.

Contract 0.26.0 adds optional 'shortname' on dashboard cards - a title is written
as a heading and ran wider than the hostname it qualified as a row badge.

Also: the supplies card reported 'Low Supplies 0' when Zabbix was unreachable,
which reads as reassurance rather than the truth that nothing could see the
cartridges. It now distinguishes unreachable (says so) from unconfigured (stays
quiet - the site declined the feature) from a real zero.

Full suite 2182 passed, frontend 314 passed.
This commit is contained in:
2026-08-29 21:35:03 -04:00
parent 330fd31524
commit 870e068c44
17 changed files with 1191 additions and 306 deletions

View File

@@ -2124,8 +2124,21 @@ def _get_low_supplies_data():
return cached
service = ZabbixService()
if not service.isconfigured or not service.isreachable:
# "No printer is low" and "we cannot tell whether any printer is low" are
# different answers, and this used to give the same empty one for both. The
# dashboard then showed Low Supplies 0 while the monitoring was dead, which
# is not a missing feature but a wrong statement: it reads as reassurance.
if not service.isconfigured:
# Enabled plus a URL plus a token. Absent any of those the site has not
# asked for supply monitoring, and a permanent "not configured" line
# would be a card reporting nothing wrong every day - which is how a
# board teaches people to stop reading it. Nothing to say: stay hidden.
return {'printers': [], 'summary': {'total_checked': 0, 'low': 0, 'critical': 0}}
if not service.isreachable:
# Configured and unanswerable is a different thing: something meant to
# work is not working, and the cartridges are NOT known to be fine.
return {'printers': [], 'unavailable': 'Supply monitoring is not reachable',
'summary': {'total_checked': 0, 'low': 0, 'critical': 0}}
# active printers with an IP, with vendor and model for waste/part rules
rows = (
@@ -2655,7 +2668,10 @@ def dashboard_supplies():
})
rows.sort(key=lambda r: (not r['iscritical'], r['printername']))
return success_response(rows)
# Envelope rather than a bare list, so the card can say WHY it has no rows.
# An empty card and a blind card must not look the same.
return success_response({'rows': rows,
'unavailable': data.get('unavailable')})
# =============================================================================