Phase 3 PR 27 — Fleet status dashboard page #26

Merged
dohertj2 merged 1 commits from phase-3-pr27-fleet-dashboard into v2 2026-04-18 14:07:18 -04:00
Owner

Adds /fleet Admin UI route that surfaces per-node apply state — joins ClusterNodeGenerationState to ClusterNode for the ClusterId, renders a sortable table plus summary cards (Total / Applied / Stale / Failed).

Behavior

  • StaleLastSeenAt older than 30s → table-warning row class + yellow count card.
  • FailedLastAppliedStatus == Failedtable-danger row + red card.
  • Status badge — Applied=bg-success, Failed=bg-danger, Applying=bg-info, unknown=bg-secondary.
  • Timestamps — relative age (42s ago / 15m ago / 3h ago) and absolute for >24h.
  • Error column — truncated to 320px with full message in a tooltip.

Refresh cadence

  • Initial load on OnInitializedAsync.
  • Auto-refresh every 5s via Timer that calls InvokeAsync(RefreshAsync) — matches FleetStatusPoller's 5s cadence so the dashboard never polls ahead of the broadcaster.
  • Manual Refresh button; _refreshing gate prevents double-runs when the timer fires mid-query.

DI scoping

Uses IServiceScopeFactory to open a fresh scope per refresh (matches FleetStatusPoller.PollOnceAsync). Avoids cross-thread DbContext races between the render thread and the timer. No new DI registrations needed.

Deferred

  • Live SignalR push into this page is deliberately out-of-scope — FleetStatusHub + NodeStateChangedMessage already work for external JS clients; wiring an in-process Blazor consumer with HubConnectionBuilder is worth its own focused PR.
  • Per-host (platform / engine / Modbus PLC) granularity still needs a dedicated page that consumes IHostConnectivityProbe.GetHostStatuses from the Server process — that's the live-SignalR follow-up.

Sidebar

MainLayout.razor gains a Fleet status link between Overview and Clusters.

Closes

LMX follow-up #7 at the fleet level (per-driver dashboard). Per-host still open.

Tests

  • Admin.Tests Unit: 14 pass / 0 fail (unchanged).
  • Full Admin build clean — 0 errors, 0 warnings.
Adds `/fleet` Admin UI route that surfaces per-node apply state — joins `ClusterNodeGenerationState` to `ClusterNode` for the `ClusterId`, renders a sortable table plus summary cards (Total / Applied / Stale / Failed). ## Behavior - **Stale** — `LastSeenAt` older than 30s → `table-warning` row class + yellow count card. - **Failed** — `LastAppliedStatus == Failed` → `table-danger` row + red card. - **Status badge** — Applied=`bg-success`, Failed=`bg-danger`, Applying=`bg-info`, unknown=`bg-secondary`. - **Timestamps** — relative age (`42s ago` / `15m ago` / `3h ago`) and absolute for >24h. - **Error column** — truncated to 320px with full message in a tooltip. ## Refresh cadence - Initial load on `OnInitializedAsync`. - Auto-refresh every 5s via `Timer` that calls `InvokeAsync(RefreshAsync)` — matches `FleetStatusPoller`'s 5s cadence so the dashboard never polls ahead of the broadcaster. - Manual `Refresh` button; `_refreshing` gate prevents double-runs when the timer fires mid-query. ## DI scoping Uses `IServiceScopeFactory` to open a fresh scope per refresh (matches `FleetStatusPoller.PollOnceAsync`). Avoids cross-thread `DbContext` races between the render thread and the timer. No new DI registrations needed. ## Deferred - Live SignalR push into this page is deliberately out-of-scope — `FleetStatusHub` + `NodeStateChangedMessage` already work for external JS clients; wiring an in-process Blazor consumer with `HubConnectionBuilder` is worth its own focused PR. - Per-host (platform / engine / Modbus PLC) granularity still needs a dedicated page that consumes `IHostConnectivityProbe.GetHostStatuses` from the Server process — that's the live-SignalR follow-up. ## Sidebar `MainLayout.razor` gains a `Fleet status` link between `Overview` and `Clusters`. ## Closes LMX follow-up #7 at the fleet level (per-driver dashboard). Per-host still open. ## Tests - Admin.Tests Unit: 14 pass / 0 fail (unchanged). - Full Admin build clean — 0 errors, 0 warnings.
dohertj2 added 1 commit 2026-04-18 14:07:09 -04:00
Phase 3 PR 27 — Fleet status dashboard page. New /fleet route shows per-node apply state (ClusterNodeGenerationState joined with ClusterNode for the ClusterId) in a sortable table with summary cards for Total / Applied / Stale / Failed node counts. Stale detection: LastSeenAt older than 30s triggers a table-warning row class + yellow count card. Failed rows get table-danger + red card. Badge classes per LastAppliedStatus: Applied=bg-success, Failed=bg-danger, Applying=bg-info, unknown=bg-secondary. Timestamps rendered as relative-age strings ('42s ago', '15m ago', '3h ago', then absolute date for >24h). Error column is truncated to 320px with the full message in a tooltip so the table stays readable on wide fleets. Initial data load on OnInitializedAsync; auto-refresh every 5s via a Timer that calls InvokeAsync(RefreshAsync) — matches the FleetStatusPoller's 5s cadence so the dashboard sees the most recent state without polling ahead of the broadcaster. A Refresh button also kicks a manual reload; _refreshing gate prevents double-runs when the timer fires during an in-flight query. IServiceScopeFactory (matches FleetStatusPoller's pattern) creates a fresh DI scope per refresh so the per-page DbContext can't race the timer with the render thread; no new DI registrations needed. Live SignalR hub push is deliberately deferred to a follow-up PR — the existing FleetStatusHub + NodeStateChangedMessage already works for external JavaScript clients; wiring an in-process Blazor Server consumer adds HubConnectionBuilder plumbing that's worth its own focused change. Sidebar link added to MainLayout between Overview and Clusters. Full Admin.Tests Unit suite 14 pass / 0 fail — unchanged, no tests regressed. Full Admin build clean (0 errors, 0 warnings). Closes the 'no per-driver dashboard' gap from lmx-followups item #7 at the fleet level; per-host (platform/engine/Modbus PLC) granularity still needs a dedicated page that consumes IHostConnectivityProbe.GetHostStatuses from the Server process — that's the live-SignalR follow-up. b5f8661e98
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
dohertj2 merged commit 5389d4d22d into v2 2026-04-18 14:07:18 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#26