feat: error boundaries, confirmation dialog, delivery metrics API, channel name validation - #799
Merged
Abd-Standard merged 5 commits intoSep 25, 2026
Conversation
- Export isBlankChannelName() helper from subscription-service - subscribe() returns INVALID_INPUT error for whitespace-only channel names - Add edge-case unit tests: single space, tabs, newline, mixed whitespace - Add isBlankChannelName() unit test suite (7 cases) Closes Core-Foundry#479
- New module: listener/src/api/delivery-metrics-api.ts
- GET /api/notifications/delivery-metrics — live snapshot from aggregator
- GET /api/notifications/delivery-metrics/history — persisted history from store
- ?reset=true resets the in-memory aggregator window after read
- ?limit and ?since query params for history pagination
- 503 when aggregator/store not configured, 400 on invalid date
- Wire handleDeliveryMetricsRequest() into events-server.ts
- Tests: delivery-metrics-api.test.ts (12 test cases)
- snapshot content, 503 fallback, reset behaviour, history pagination,
invalid-since validation, empty store, versioned path (/api/v1/...)
Closes Core-Foundry#482
- ConfirmationDialog.tsx wraps alertdialog pattern with full a11y support - role=alertdialog, aria-modal, aria-labelledby, aria-describedby - Confirm button auto-focused on open (primary action intent) - Focus trap: Tab/Shift+Tab cycles within dialog only - Escape key and backdrop click both fire onCancel - Focus restored to trigger element on close - danger/primary variant prop for confirm button styling - Custom confirmLabel / cancelLabel props - ConfirmationDialog.test.tsx: 20 test cases covering - rendering (open/closed, labels, variants) - interactions (confirm, cancel, backdrop, escape) - keyboard focus trap (Tab wrap, Shift+Tab wrap) - focus restoration on close - axe accessibility audit (alertdialog, aria attributes) Closes Core-Foundry#653
…oard
- New ErrorBoundary class component (ErrorBoundary.tsx)
- getDerivedStateFromError catches render-time errors in subtree
- componentDidCatch logs error.name + error.message + componentStack
(no raw JS stack frames — avoids leaking sensitive variable values)
- optional onError() callback for external error tracking
- optional custom fallback prop; defaults to branded fallback UI
- 'Try again' button resets boundary state for retry
- role=alert + aria-live=assertive on fallback for screen readers
- section prop names the boundary in logs and fallback heading
- App.tsx: wrap all 11 dashboard panels in ErrorBoundary
- Each panel has its own named boundary (Event Explorer, Delivery
Timeline, Activity Feed, Webhook Performance, etc.)
- A failure in one panel leaves all other panels fully functional
- ErrorBoundary.test.tsx: 19 test cases covering
- pass-through rendering when no error
- fallback UI content and section name
- custom fallback prop
- retry button resets and re-renders children
- onError callback receives error + componentStack
- onError exceptions are swallowed
- console.error logs name+message but not raw stack frames
- sibling boundary isolation (one error does not affect sibling)
- nested boundary isolation (inner caught, outer unaffected)
- role=alert accessibility
Closes Core-Foundry#654
|
@abdoolbasit374-web Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
5 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR implements four issues in a single branch so they can be reviewed
and merged together.
Changes
fix(#479) — Prevent Empty Channel Names
SubscriptionService.subscribe()now explicitly rejects whitespace-onlychannel names (single space, tabs, newlines, mixed whitespace) with an
INVALID_INPUTerror and a clear message.isBlankChannelName()helper for reuse andindependent testing.
feat(#482) — Notification Delivery Metrics API
listener/src/api/delivery-metrics-api.tsexposes two endpoints:GET /api/notifications/delivery-metrics— live snapshot of deliverystatistics (total, success, failure, retry, success rate, average duration,
breakdown by type/contract/hour, error breakdown).
Supports
?reset=trueto reset the in-memory window after reading.GET /api/notifications/delivery-metrics/history— paginated persistedsnapshots from the metrics store (
?limit,?sinceISO-8601 date).503when the aggregator or store is not configured.400on an invalidsincedate./api/v1/notifications/delivery-metricsworks via theexisting rewrite middleware.
events-server.tsbefore the 404 fallback.reset behaviour, history pagination, invalid-date validation, empty store,
and the versioned path.
feat(#653) — Reusable Confirmation Dialog Component
ConfirmationDialogcomponent built on thealertdialogARIA pattern:role="alertdialog",aria-modal,aria-labelledby,aria-describedbyintentionally — one Enter press confirms).
onCancel.danger/primaryvariant prop for the confirm button.confirmLabel/cancelLabelprops.restoration, and a full
axeaccessibility audit.feat(#654) — Component-Level Error Boundaries
ErrorBoundaryReact class component:getDerivedStateFromError+componentDidCatch.error.name,error.message, and the ReactcomponentStack—deliberately omits raw JS stack frames to avoid leaking sensitive
variable values.
onErrorcallback for external error-tracking services(exceptions thrown by the callback are swallowed).
fallbackprop; defaults to a branded fallback UIwith section name and a "Try again" reset button.
role="alert"+aria-live="assertive"on the fallback so screenreaders announce the error.
App.tsx: all 11 dashboard panels (Event Explorer, Delivery Timeline,Activity Feed, Webhook Performance, Export History, Notification Search,
Preferences, Templates, Channel Details, User Activity, Retry Statistics)
are individually wrapped in named
ErrorBoundaryinstances — a failurein one panel leaves every other panel fully functional.
retry/reset,
onErrorcallback, safe logging, sibling isolation, nestedboundary isolation, and
role="alert"accessibility.Testing
All new behaviour is covered by tests co-located with the source files:
subscription-service.test.tsdelivery-metrics-api.test.tsConfirmationDialog.test.tsxErrorBoundary.test.tsxCI runs lint → typecheck → tests for both
dashboardandlisteneronevery PR — all checks are expected to pass.
Closes
Closes #479
Closes #482
Closes #653
Closes #654