Say what a service state actually is, and make the badges readable
All checks were successful
CI / naming (push) Successful in 4s
CI / frontend (push) Successful in 14s
CI / migrations-mysql (push) Successful in 43s
CI / backend (push) Successful in 4m18s

Three colour faults on the PC page, each hiding the next.

ACTIVITY BADGES WERE ALL GREY. The list renderer reads a badge colour from a
ROW FIELD, and the activity panel declared none, so every severity rendered
identically. The feed's whole job is to make a critical event visible and it
was painting it the same as a logon. The route now emits severitycolor - info
grey, warning yellow, critical red - and the panel declares it.

PAUSED AND THE TRANSITIONAL STATES WERE RED. statecolor was green-or-red-or-
grey, so a paused service looked like a stopped one. They are different: a
paused service was paused deliberately, and one stuck in Start Pending is
failing to start on a retry cycle - which is exactly what raises event 7011,
the thing behind the critical counts on the Fleet page. Both are amber now.

NAMED COLOURS ALWAYS GOT WHITE TEXT. readableText only parsed hex and returned
white for anything else, but panels emit NAMES. Green, red and grey are dark
enough that white worked by luck; amber is not, so the fix above shipped as
white-on-yellow and could not be read. Names resolve to hex before the contrast
test, so this holds for any named colour anywhere in the app rather than these
two panels.

colorStyle had no test file at all. It has one now.

Every one of these was found by looking at the rendered page. None of them
would fail a test that checked markup.

Full suite 2131 passed, vitest 273 passed.
This commit is contained in:
cproudlock
2026-08-29 17:00:18 -04:00
parent 828a62063b
commit d4105c2289
5 changed files with 141 additions and 3 deletions

View File

@@ -244,6 +244,18 @@ class EndpointsPlugin(BasePlugin):
'render': 'list',
'collapsible': True,
'filter': {'key': 'kind', 'equals': 'service'},
# Facet on the STATE, not the status colour. statecolor is only
# ever green, red or grey here, so chips derived from it would
# read "OK" and "Critical" - not how anybody talks about a
# service. statelabel carries the real vocabulary: Running,
# Stopped, Paused, and the Windows transitional states. Any
# value not listed still gets a chip, appended after these.
'facets': {
'key': 'statelabel',
'order': ['Running', 'Stopped', 'Paused', 'Start Pending',
'Stop Pending', 'Continue Pending', 'Pause Pending',
'Unknown'],
},
'status': {
'key': 'statecolor',
'levels': {
@@ -270,6 +282,18 @@ class EndpointsPlugin(BasePlugin):
'render': 'list',
'collapsible': True,
'filter': {'key': 'kind', 'equals': 'process'},
# Facet on the STATE, not the status colour. statecolor is only
# ever green, red or grey here, so chips derived from it would
# read "OK" and "Critical" - not how anybody talks about a
# service. statelabel carries the real vocabulary: Running,
# Stopped, Paused, and the Windows transitional states. Any
# value not listed still gets a chip, appended after these.
'facets': {
'key': 'statelabel',
'order': ['Running', 'Stopped', 'Paused', 'Start Pending',
'Stop Pending', 'Continue Pending', 'Pause Pending',
'Unknown'],
},
'status': {
'key': 'statecolor',
'levels': {
@@ -313,6 +337,10 @@ class EndpointsPlugin(BasePlugin):
'title': 'title',
'badge': {
'label': 'severity',
# The renderer reads the colour from a row field; with
# no colour declared every badge renders grey, which is
# what the feed spent its first version doing.
'color': 'severitycolor',
'labelmap': {
'critical': 'Critical',
'warning': 'Warning',