feat(central-ui): rail-footer pending-updates indicator

Adds PendingUpdatesIndicator to MainLayout's RailFooter, above .rail-user.
Self-gating on the Deployer/Administrator role claims, it polls the
process-wide single-flight IPendingUpdatesCache every 30s through PollGate
and renders four states: checking / up to date / pending (warning pill plus
an 'N instances · M sites' breakdown) / unavailable. Each half of the
breakdown links only for the role that can act on it (Topology => Deployer,
Sites => Administrator), and a site-scoped operator's totals are narrowed
through SiteScopeService so they never count a site they cannot see.

Claude-Session: https://claude.ai/code/session_01Y5ef34iUUMjmwnMpL9xgmB
This commit is contained in:
Joseph Doherty
2026-09-14 14:00:16 -04:00
parent 5d6bd03868
commit c724ed6ee2
4 changed files with 451 additions and 0 deletions
@@ -191,3 +191,14 @@
overscroll-behavior: contain;
}
}
/* ── Rail pending-updates indicator ──────────────────────────────────────
Sits in .rail-foot just above .rail-user. One line when quiet, two when
something is pending; tokens only so dark mode follows for free. */
.rail-updates { display: flex; flex-direction: column; gap: 0.15rem; padding: 0 0.6rem 0.5rem; font-size: 0.8rem; line-height: 1.3; }
.rail-updates-muted { color: var(--ink-faint); }
.rail-updates-head { display: flex; align-items: center; gap: 0.35rem; font-weight: 600; color: var(--ink-soft); }
.rail-updates-head .badge { font-size: 0.7rem; }
.rail-updates-detail { color: var(--ink-faint); }
.rail-updates a { color: inherit; text-decoration: underline dotted; }
.rail-updates a:hover { color: var(--accent); }