You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Once notifications are dispatched in the background, the only evidence a user has that something was sent is the receiving system (webhook endpoint, ntfy topic). When a delivery dead-letters, the reason lives in NotificationRecord.LastError and is invisible without database access. A minimal read-only history page closes that gap and makes the subsystem debuggable from the UI.
Scope
New view data
src/DockerUpdateGuard/UI/NotificationRecordViewData.cs following the existing *ViewData conventions (compare ScanHistoryItemData.cs, VulnerabilityFindingViewData.cs):
Property
Purpose
Id
Record identifier
Kind
NotificationKind for icon/label selection
ChannelName
Which channel the record belongs to
Status
NotificationRecordStatus for the status chip
Subject
Denormalized subject text
Details
Optional detail line
AttemptCount
Delivery attempts made
LastError
Error text for dead-lettered records
CreatedAtUtc / SentAtUtc / NextAttemptAtUtc
Timestamps
View service
Extend src/DockerUpdateGuard/UI/IApplicationViewService.cs and ApplicationViewService.cs with
Ordered by CreatedAtUtc descending, capped (default 200), AsNoTracking, projected directly into the view data (no entity leakage into the UI layer — the established pattern for every page).
Suppressed baseline records are excluded by default so the first-run baseline does not bury real history; expose them behind a filter toggle only if it stays trivial.
MudBlazor table listing subject, kind, channel, status chip, attempt count, created/sent timestamps; LastError shown in an expandable detail area for dead-lettered rows (compare the collapsible detail pattern introduced in Collapse severity and delta chips behind an expandable detail area #219).
Uses PersistentComponentState and a skeleton loader (TableSkeleton) exactly like the existing pages.
Empty state distinguishes "notifications disabled" from "no notifications yet", so the page explains itself when the feature is off.
Follows the docs/ui-concept design tokens (--dug-*) and mobile breakpoints.
Navigation
src/DockerUpdateGuard/Components/Layout/NavMenu.razor: new MudNavLink Href="notification-history" with a bell icon (Icons.Material.Filled.Notifications), rendered only whenNotifications:Enabled is true — the same conditional pattern already used for the "My Images" and "Base Images" entries.
Out of scope
A settings UI for the notification configuration — configuration stays file/environment based like every other option section.
Manual re-send or dead-letter requeue actions.
A dashboard tile for notifications.
Testing
src/Tests/DockerUpdateGuard.Tests/NotificationHistoryPageTests.cs — bunit render tests via BlazorTestContextFactory: rows rendered per record, status chips, dead-letter error visible, both empty states.
NotificationHistoryPersistentStateTests.cs mirroring the existing *PersistentStateTests files.
Final, optional stage of the notification feature set. Depends on #220 (ledger entity) and benefits from #224 (dead-letter state).
Full design: notification concept
Context
Once notifications are dispatched in the background, the only evidence a user has that something was sent is the receiving system (webhook endpoint, ntfy topic). When a delivery dead-letters, the reason lives in
NotificationRecord.LastErrorand is invisible without database access. A minimal read-only history page closes that gap and makes the subsystem debuggable from the UI.Scope
New view data
src/DockerUpdateGuard/UI/NotificationRecordViewData.csfollowing the existing*ViewDataconventions (compareScanHistoryItemData.cs,VulnerabilityFindingViewData.cs):IdKindNotificationKindfor icon/label selectionChannelNameStatusNotificationRecordStatusfor the status chipSubjectDetailsAttemptCountLastErrorCreatedAtUtc/SentAtUtc/NextAttemptAtUtcView service
Extend
src/DockerUpdateGuard/UI/IApplicationViewService.csandApplicationViewService.cswithCreatedAtUtcdescending, capped (default 200),AsNoTracking, projected directly into the view data (no entity leakage into the UI layer — the established pattern for every page).Suppressedbaseline records are excluded by default so the first-run baseline does not bury real history; expose them behind a filter toggle only if it stays trivial.Page
src/DockerUpdateGuard/Components/Pages/NotificationHistory.razor+.razor.cscode-behind, route/notification-history:LastErrorshown in an expandable detail area for dead-lettered rows (compare the collapsible detail pattern introduced in Collapse severity and delta chips behind an expandable detail area #219).PersistentComponentStateand a skeleton loader (TableSkeleton) exactly like the existing pages.docs/ui-conceptdesign tokens (--dug-*) and mobile breakpoints.Navigation
src/DockerUpdateGuard/Components/Layout/NavMenu.razor: newMudNavLink Href="notification-history"with a bell icon (Icons.Material.Filled.Notifications), rendered only whenNotifications:Enabledis true — the same conditional pattern already used for the "My Images" and "Base Images" entries.Out of scope
Testing
src/Tests/DockerUpdateGuard.Tests/NotificationHistoryPageTests.cs— bunit render tests viaBlazorTestContextFactory: rows rendered per record, status chips, dead-letter error visible, both empty states.NotificationHistoryPersistentStateTests.csmirroring the existing*PersistentStateTestsfiles.ApplicationViewServiceTestsextension: ordering, cap,Suppressedexclusion.Acceptance criteria
/notification-historylists notification records with status, channel, attempt count, and timestampsLastErrorin the UIDockerUpdateGuardDbContextdirectly — all data flows throughIApplicationViewServicePersistentComponentStatebehave like on the existing pages