Commit Graph

24 Commits

Author SHA1 Message Date
Joseph Doherty
b3b02a8cb6 fix(centralui): apply status/stuck query-string filters on the Site Calls page 2026-05-21 05:08:50 -04:00
Joseph Doherty
44f1ee372a feat(centralui): Site Call KPI tiles on the Health dashboard 2026-05-21 05:04:16 -04:00
Joseph Doherty
d73b459057 fix(centralui): single relay toast, paging/skip polish, extra Site Calls tests 2026-05-21 04:59:12 -04:00
Joseph Doherty
7e9d74697b feat(centralui): Site Calls page with Retry/Discard and Audit drill-in 2026-05-21 04:51:14 -04:00
Joseph Doherty
babf5b99e7 feat(ui): notification detail modal shows message body + recipients 2026-05-21 02:49:17 -04:00
Joseph Doherty
ef5cf76026 feat(ui): notification report row double-click opens detail modal 2026-05-21 02:39:41 -04:00
Joseph Doherty
c66ef71017 feat(ui): SMTP config form TlsMode field
Add a TlsMode read-only row and a None/StartTLS/SSL select to the SMTP
Configuration page edit form. New configs default to None; edits load
and persist the chosen mode through the repository.
2026-05-21 02:13:02 -04:00
Joseph Doherty
6dea84cd28 feat(security): OperationalAudit + AuditExport permissions for Audit Log surface (#23 M7)
Bundle G (#23 M7-T15): replace the temporary Admin-only gate on the Audit
Log surface with two new permission policies — OperationalAudit (read) and
AuditExport (bulk-export) — so the read path and the forensic-export path
can be delegated independently.

ScadaLink.Security
- AuthorizationPolicies: add OperationalAudit + AuditExport policy
  constants; register them via RequireClaim with an explicit role allow-list
  (OperationalAuditRoles, AuditExportRoles) so the role-to-permission
  mapping is documented in one place.
- Default mapping: Admin and Audit roles grant both policies; AuditReadOnly
  grants OperationalAudit only (read access without bulk export); Design
  and Deployment grant neither.

ScadaLink.CentralUI
- AuditLogPage: switch the page-level [Authorize] to the OperationalAudit
  policy and wrap the Export-CSV button in an AuthorizeView gated on
  AuditExport so an OperationalAudit-only operator still sees the page +
  filters but cannot trigger the CSV pull.
- ConfigurationAuditLog: switch from RequireAdmin to OperationalAudit so
  both pages under the Audit nav group share the same gate.
- NavMenu: the Audit nav group now gates on OperationalAudit so the
  section header + both child links match the per-page policies.
- AuditExportEndpoints: switch RequireAuthorization from RequireAdmin to
  AuditExport — this is the authoritative gate; the AuthorizeView on the
  button is just a UX affordance.

Tests
- New AuditLogPagePermissionTests covers the 5 brief-mandated cases plus
  defence-in-depth for Admin-alone and AuditReadOnly users on the endpoint.
- SecurityTests: add policy-level coverage for the new role→permission
  matrix (Theory rows pin every role/policy combination).
- AuditExportEndpointsTests: switch to AddScadaLinkAuthorization() so the
  test host exercises the real production wiring under the new gate.
- AuditLogPageScaffoldTests: wrap the page render in a
  CascadingAuthenticationState so the new in-page AuthorizeView resolves
  the principal.
2026-05-20 21:09:42 -04:00
Joseph Doherty
8744630adb feat(ui): server-side streaming CSV export of Audit Log (#23 M7) 2026-05-20 20:57:01 -04:00
Joseph Doherty
943c2ced39 feat(ui): Audit KPI tiles on Health dashboard (#23 M7)
Adds three KPI tiles to the central Health dashboard for the Audit channel:
volume (rows in the last hour), error rate (Failed/Parked/Discarded over
total), and backlog (sum of SiteAuditBacklog.PendingCount across all sites).

Repo + service:
- IAuditLogRepository.GetKpiSnapshotAsync(window, nowUtc) — single aggregate
  SELECT over the trailing window returning total + error counts; nowUtc is
  optional for production callers and pinned by integration tests against the
  shared MSSQL fixture so the global counts are deterministic.
- AuditLogQueryService.GetKpiSnapshotAsync() — composes the repo aggregate
  with a sum of SiteAuditBacklog.PendingCount read from ICentralHealthAggregator.
- AuditLogKpiSnapshot record in Commons/Types/.

UI:
- New AuditKpiTiles Blazor component (Components/Health/) — three Bootstrap
  card-tiles, click navigates to /audit/log with the matching pre-filter.
- Health.razor wires the tiles in alongside the existing Notification Outbox
  KPIs; LoadAuditKpis() runs on every 10s refresh tick and degrades to em
  dashes + inline error if the query fails.
- AuditLogPage extended to parse ?status= so the error-rate tile drill-in
  (?status=Failed) auto-loads the grid.

Tests:
- AuditLogRepositoryTests: GetKpiSnapshotAsync mixed-status + empty-window
  cases against the MSSQL migration fixture.
- AuditLogQueryServiceTests: forwarding + backlog composition; sites with
  null SiteAuditBacklog contribute zero.
- AuditKpiTilesTests: 9 bUnit tests covering tile render, error-rate maths
  with safe zero-events handling, em-dash unavailable path, click-through
  navigation, and warning/danger border thresholds.
- HealthPageTests: new Renders_AuditKpiTiles_WithValues plus IAuditLogQueryService
  stub registration in the constructor so existing outbox tests still pass.
- AuditLogPageScaffoldTests: ?status=Failed auto-load + unknown status drop.
2026-05-20 20:43:57 -04:00
Joseph Doherty
1c20e81d77 feat(ui): drill-in from Notifications to Audit Log (#23 M7) 2026-05-20 20:20:54 -04:00
Joseph Doherty
450f8bca28 feat(ui): AuditLogPage parses query-string filters for drill-ins (#23 M7) 2026-05-20 20:19:47 -04:00
Joseph Doherty
e052aa4ff8 feat(ui): AuditResultsGrid + AuditLogQueryService with keyset paging (#23 M7)
Adds the results grid + query facade for the central Audit Log page
(#23 M7-T3):

* IAuditLogQueryService / AuditLogQueryService — CentralUI facade over
  IAuditLogRepository.QueryAsync so the grid can be tested with a stubbed
  query source. Default page size is 100; callers can override per call.

* AuditResultsGrid.razor + .razor.cs — Blazor Server component (Bootstrap
  only, no third-party UI libs). Renders the 10 columns from
  Component-AuditLog.md §10 (OccurredAtUtc, Site, Channel, Kind, Status,
  Target, Actor, DurationMs, HttpStatus, ErrorMessage). Keyset-paged via
  the last visible row's (OccurredAtUtc, EventId) as the cursor; Next-page
  button disabled when the current page is short (no count query). Row
  clicks emit OnRowSelected(AuditEvent) for Bundle C's drilldown drawer.
  Status badges are colour-coded (Delivered=green; Failed/Parked/Discarded
  =red; other=gray). Error messages truncated to 80 chars with full text
  on hover.

* Column model framework: a ColumnOrder [Parameter] reorders columns by
  stable string keys; unknown keys are dropped. M7 scope decision (in the
  class doc): the framework is in place but drag-reorder / resize UX is
  not implemented — M7.x can add persisted-per-user reordering without
  rewriting the column model.

* AuditLogPage wired: hosts AuditFilterBar + AuditResultsGrid, threads
  the filter through and stubs OnRowSelected for Bundle C.

* AuditLogQueryService registered as scoped in AddCentralUI.

* Tests: 4 grid bUnit tests (10 columns rendered, next-page cursor
  carries last row, row click raises callback, badge classes for
  Failed vs Delivered), 2 service tests (filter+paging pass-through,
  default page size of 100). AuditLogPageScaffoldTests updated to
  provide the new ISiteRepository + IAuditLogQueryService stubs the
  page now resolves.
2026-05-20 20:02:46 -04:00
Joseph Doherty
12b86bea7a feat(ui): scaffold Audit Log page + Audit nav group (#23 M7)
Adds the central-side Audit Log page scaffold at /audit/log (M7-T1) and
introduces a new Audit nav group (M7-T9) that hosts both the new page and
the renamed Configuration Audit Log. The page body is intentionally a
heading + two placeholders — Bundle B will land the AuditFilterBar and
AuditResultsGrid behind them.

The Audit nav group sits between Monitoring and the per-user footer; both
items inside are Admin-only, so the section header lives inside the
RequireAdmin AuthorizeView (non-admins see no orphan header).

bUnit scaffold tests pin the page heading, the section header order, and
the two child links; the existing 338 CentralUI tests continue to pass.
2026-05-20 19:49:11 -04:00
Joseph Doherty
562a1d1678 test(central-ui): assert the Health KPIs link is a real anchor element 2026-05-19 06:24:39 -04:00
Joseph Doherty
82745ef916 feat(central-ui): link Health outbox tiles to the Notification KPIs page 2026-05-19 06:22:02 -04:00
Joseph Doherty
8bb860ad5f test(central-ui): cover the per-site KPI error path on the Notification KPIs page 2026-05-19 06:14:19 -04:00
Joseph Doherty
22bac058dd feat(central-ui): Notification KPIs page with per-site breakdown 2026-05-19 06:09:43 -04:00
Joseph Doherty
34e464edab refactor(central-ui): split Notification Report out of the Outbox page 2026-05-19 06:03:15 -04:00
Joseph Doherty
0fa4ac5525 refactor(central-ui): contextual errors, parallel recipient load, delete-path test for Notification Lists 2026-05-19 05:54:48 -04:00
Joseph Doherty
0f90c0ad9c feat(central-ui): standalone Notification Lists page 2026-05-19 05:49:45 -04:00
Joseph Doherty
9e7bc7b541 feat(notification-outbox): add outbox KPI tiles to Health dashboard 2026-05-19 03:05:41 -04:00
Joseph Doherty
9b05e48ea6 test(notification-outbox): cover Discard and query-failure paths on the Outbox page 2026-05-19 03:02:48 -04:00
Joseph Doherty
ad9872705d feat(notification-outbox): add Notification Outbox UI page 2026-05-19 02:58:49 -04:00