6.4 KiB
Notifications Nav Group — Design
Date: 2026-05-19
Goal: Consolidate all notification-related Central UI pages into a dedicated Notifications left-menu section, split the combined Outbox page into a report and a KPIs page, give Notification Lists a proper home, and add a per-source-site KPI breakdown.
Background
Notification-related UI is currently scattered:
| Page | Route | Nav section | Policy |
|---|---|---|---|
| SMTP Configuration | /admin/smtp |
Admin | RequireAdmin |
| Notification Outbox (KPI tiles + filterable table) | /monitoring/notification-outbox |
Monitoring | RequireDeployment |
| Notification Lists | /design/notification-lists/... (form only) |
none — table embedded in the External Systems page | RequireDesign |
The Outbox page mixes KPI tiles and the filterable Notifications-table report on
one page. Notification Lists has no list page of its own — its table is bolted
onto ExternalSystems.razor. KPI infrastructure
(NotificationKpiRequest/Response, INotificationOutboxRepository.ComputeKpisAsync)
is global-only, despite CLAUDE.md stating KPIs are "global + per-source-site".
Architecture
A new Notifications left-menu section consolidates these pages. Routes move
to a consistent /notifications/* prefix. The combined Outbox page is split into
two. Notification Lists gets a dedicated page. A bounded backend addition supplies
per-source-site KPIs. No actor topology, persistence, or message-evolution rules
change beyond the additive KPI contracts.
1. Nav menu
New Notifications section in NavMenu.razor, placed between Deployment and
Monitoring. Final section order: Dashboard, Admin, Design, Deployment,
Notifications, Monitoring, Audit Log.
| Menu item | Route | Policy |
|---|---|---|
| SMTP Configuration | /notifications/smtp |
RequireAdmin |
| Notification Lists | /notifications/lists |
RequireDesign |
| Notification Report | /notifications/report |
RequireDeployment |
| Notification KPIs | /notifications/kpis |
RequireDeployment |
Each item is wrapped in its own per-item AuthorizeView policy (same pattern the
Monitoring section already uses for its mixed-role items). The section header is a
plain div — every authenticated user holds at least one of Admin/Design/Deployment,
so the header always has ≥1 visible child and cannot be orphaned.
SMTP Configuration is removed from the Admin section; Notification Outbox is removed from the Monitoring section.
2. SMTP Configuration
Move Components/Pages/Admin/SmtpConfiguration.razor →
Components/Pages/Notifications/SmtpConfiguration.razor. Route /admin/smtp →
/notifications/smtp. Page content, RequireAdmin policy, and the
SmtpConfiguration namespace alias are unchanged.
3. Notification Lists (new page)
New Components/Pages/Notifications/NotificationLists.razor
(/notifications/lists, RequireDesign): a DataTable of notification lists with
Add and per-row Edit actions, plus an empty state — extracted verbatim from the
notification-lists block currently in ExternalSystems.razor.
NotificationListForm.razorroutes move:/design/notification-lists/create→/notifications/lists/create,/design/notification-lists/{Id:int}/edit→/notifications/lists/{Id:int}/edit. Its "Back" navigation targets/notifications/lists.- The notification-lists section is removed from
ExternalSystems.razor, leaving that page purely external systems. The three/design/notification-lists/...navigate-links inExternalSystems.razorare removed with it.
4. Notification Report
New Components/Pages/Notifications/NotificationReport.razor
(/notifications/report, RequireDeployment), split from the existing
Monitoring/NotificationOutbox.razor. Retains the full filter bar, the paginated
Notifications-table query (NotificationOutboxQueryRequest), and the per-row
Retry/Discard actions. The KPI tile row is removed from this page.
Components/Pages/Monitoring/NotificationOutbox.razor and its Monitoring nav entry
are deleted.
5. Notification KPIs
New Components/Pages/Notifications/NotificationKpis.razor
(/notifications/kpis, RequireDeployment) with a manual Refresh button. Two parts:
- Global tiles — the existing 5: Queue Depth, Stuck, Parked, Delivered Last Interval, Oldest Pending Age.
- Per-source-site breakdown table — one row per site with the same five metrics, so operators can see which site is backing up.
Backend addition for per-site KPIs
Bounded, additive, follows the existing global-KPI pattern:
INotificationOutboxRepository.ComputePerSiteKpisAsync(...)→ returns a per-site collection (a newSiteNotificationKpiSnapshotrecord carrying the source site id plus the five metrics). Implemented inNotificationOutboxRepository.- New message pair in
Messages/Notification/NotificationOutboxQueries.cs:PerSiteNotificationKpiRequest/PerSiteNotificationKpiResponse(additive — honors message-evolution rules). - A handler in
NotificationOutboxActorfor the new request, mirroring the existingNotificationKpiRequesthandler. - A
CommunicationService.GetPerSiteNotificationKpisAsync(...)method mirroringGetNotificationKpisAsync.
Per CLAUDE.md, KPIs remain point-in-time computed from the Notifications table —
no time-series store, no historical charts (YAGNI).
6. Health dashboard
Monitoring/Health.razor keeps its KPI tile row unchanged. A "View details →"
link is added from that tile row to /notifications/kpis.
Error handling
Unchanged from the current Outbox page: KPI/query faults surface as an inline
warning alert (Success == false → ErrorMessage); the site-name lookup degrades
gracefully to raw site ids. Per-site KPI faults are reported the same way.
Testing
- bUnit component tests for
NotificationLists,NotificationReport,NotificationKpis, and the movedSmtpConfigurationpage. - A
NavMenutest asserting the Notifications section renders and that per-item visibility honors Admin/Design/Deployment roles. - Repository tests for
ComputePerSiteKpisAsync. - Actor test for the
PerSiteNotificationKpiRequesthandler. CommunicationServicetest forGetPerSiteNotificationKpisAsync.
Out of scope
- Historical/trend KPI charts (no time-series store).
- Any change to notification delivery, store-and-forward, or the
Notificationstable schema. - Renaming the Notification Outbox component (#21) — only the UI page names change.