# 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.razor` routes 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 in `ExternalSystems.razor` are 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:

1. **Global tiles** — the existing 5: Queue Depth, Stuck, Parked, Delivered Last
   Interval, Oldest Pending Age.
2. **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 new `SiteNotificationKpiSnapshot` record carrying the
  source site id plus the five metrics). Implemented in
  `NotificationOutboxRepository`.
- New message pair in `Messages/Notification/NotificationOutboxQueries.cs`:
  `PerSiteNotificationKpiRequest` / `PerSiteNotificationKpiResponse` (additive —
  honors message-evolution rules).
- A handler in `NotificationOutboxActor` for the new request, mirroring the
  existing `NotificationKpiRequest` handler.
- A `CommunicationService.GetPerSiteNotificationKpisAsync(...)` method mirroring
  `GetNotificationKpisAsync`.

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 moved `SmtpConfiguration` page.
- A `NavMenu` test asserting the Notifications section renders and that per-item
  visibility honors Admin/Design/Deployment roles.
- Repository tests for `ComputePerSiteKpisAsync`.
- Actor test for the `PerSiteNotificationKpiRequest` handler.
- `CommunicationService` test for `GetPerSiteNotificationKpisAsync`.

## Out of scope

- Historical/trend KPI charts (no time-series store).
- Any change to notification delivery, store-and-forward, or the `Notifications`
  table schema.
- Renaming the Notification Outbox **component** (#21) — only the UI page names change.
