diff --git a/frontend-modern/src/features/alerts/AlertDeliveryHealthCard.test.tsx b/frontend-modern/src/features/alerts/AlertDeliveryHealthCard.test.tsx index 6240142f7..9f3828c05 100644 --- a/frontend-modern/src/features/alerts/AlertDeliveryHealthCard.test.tsx +++ b/frontend-modern/src/features/alerts/AlertDeliveryHealthCard.test.tsx @@ -102,4 +102,39 @@ describe('AlertDeliveryHealthCard', () => { expect(screen.getByRole('alert')).toHaveTextContent('send a test before relying on delivery'); expect(screen.getByRole('button', { name: 'Refresh delivery status' })).toBeDisabled(); }); + + it('keeps the overview treatment concise and points directly to delivery evidence', () => { + render(() => ( + + ( + + )} + /> + + )); + + const alert = screen.getByRole('alert'); + expect(alert).toHaveTextContent('Most recent failures: authentication (2)'); + expect(alert).toHaveTextContent( + 'Review delivery activity for timestamps, destinations, alerts, and errors', + ); + expect(alert).not.toHaveTextContent('Otherwise Pulse removes expired records hourly'); + expect(screen.queryByRole('button', { name: 'Refresh delivery status' })).toBeNull(); + expect(screen.getByRole('link', { name: 'Review delivery activity' })).toHaveAttribute( + 'href', + '/alerts/notifications#notification-delivery-activity', + ); + }); }); diff --git a/frontend-modern/src/features/alerts/AlertDeliveryHealthCard.tsx b/frontend-modern/src/features/alerts/AlertDeliveryHealthCard.tsx index b470dfb50..6519ec23c 100644 --- a/frontend-modern/src/features/alerts/AlertDeliveryHealthCard.tsx +++ b/frontend-modern/src/features/alerts/AlertDeliveryHealthCard.tsx @@ -7,6 +7,7 @@ import { Card } from '@/components/shared/Card'; import { getAlertDestinationsDeliveryDismissLabel, getAlertDestinationsDeliveryHealthDescription, + getAlertDestinationsDeliveryHealthSummary, getAlertDestinationsDeliveryHealthTitle, getAlertDestinationsDeliveryRefreshLabel, getAlertDestinationsDeliveryReviewLabel, @@ -23,6 +24,8 @@ interface AlertDeliveryHealthCardProps { onRetryFailures?: () => void; onDismissFailures?: () => void; detailsHref?: string; + detailLevel?: 'summary' | 'full'; + showRefresh?: boolean; } export function AlertDeliveryHealthCard(props: AlertDeliveryHealthCardProps) { @@ -32,27 +35,34 @@ export function AlertDeliveryHealthCard(props: AlertDeliveryHealthCardProps) { const deadLetter = () => props.health?.deadLetter ?? 0; const completedRetentionDays = () => props.health?.completedRetentionDays ?? 7; const deadLetterRetentionDays = () => props.health?.deadLetterRetentionDays ?? 30; + const description = () => { + const input = { + status: status(), + failed: failed(), + deadLetter: deadLetter(), + completedRetentionDays: completedRetentionDays(), + deadLetterRetentionDays: deadLetterRetentionDays(), + failureClasses7d: props.health?.failureClasses7d, + failureClassesAvailable: props.health?.failureClassesAvailable ?? false, + }; + return props.detailLevel === 'summary' + ? getAlertDestinationsDeliveryHealthSummary(input) + : getAlertDestinationsDeliveryHealthDescription(input); + }; return (
- +
@@ -83,15 +93,20 @@ export function AlertDeliveryHealthCard(props: AlertDeliveryHealthCardProps) { {getAlertDestinationsDeliveryDismissLabel()} ) : null} - + {props.showRefresh !== false ? ( + + ) : null}
diff --git a/frontend-modern/src/features/alerts/OverviewTab.tsx b/frontend-modern/src/features/alerts/OverviewTab.tsx index debd2dfaf..219aeb25e 100644 --- a/frontend-modern/src/features/alerts/OverviewTab.tsx +++ b/frontend-modern/src/features/alerts/OverviewTab.tsx @@ -88,7 +88,9 @@ export function OverviewTab(props: { dismissingFailures={deliveryHealthState.dismissingTerminalFailures()} onRetryFailures={() => void deliveryHealthState.retryTerminalFailures()} onDismissFailures={() => void deliveryHealthState.dismissTerminalFailures()} - detailsHref="/alerts/notifications" + detailsHref="/alerts/notifications#notification-delivery-activity" + detailLevel="summary" + showRefresh={false} /> diff --git a/frontend-modern/src/features/alerts/__tests__/OverviewTab.deliveryactions.test.tsx b/frontend-modern/src/features/alerts/__tests__/OverviewTab.deliveryactions.test.tsx index 827602039..c177bc613 100644 --- a/frontend-modern/src/features/alerts/__tests__/OverviewTab.deliveryactions.test.tsx +++ b/frontend-modern/src/features/alerts/__tests__/OverviewTab.deliveryactions.test.tsx @@ -124,5 +124,7 @@ describe('OverviewTab delivery health actions', () => { }); expect(screen.getByRole('button', { name: 'Retry retained deliveries' })).toBeTruthy(); expect(screen.getByRole('button', { name: 'Dismiss retained failures' })).toBeTruthy(); + expect(screen.queryByRole('button', { name: 'Refresh delivery status' })).toBeNull(); + expect(screen.getByRole('alert')).toHaveTextContent('Most recent failures: connectivity (1).'); }); }); diff --git a/frontend-modern/src/utils/__tests__/alertDestinationsPresentation.test.ts b/frontend-modern/src/utils/__tests__/alertDestinationsPresentation.test.ts index 61c556657..59c25c312 100644 --- a/frontend-modern/src/utils/__tests__/alertDestinationsPresentation.test.ts +++ b/frontend-modern/src/utils/__tests__/alertDestinationsPresentation.test.ts @@ -46,6 +46,7 @@ import { getAlertDestinationsAppriseValidationError, getAlertDestinationsConfigLoadError, getAlertDestinationsDeliveryHealthDescription, + getAlertDestinationsDeliveryHealthSummary, getAlertDestinationsDeliveryHealthTitle, getAlertDestinationsDeliveryDismissConfirmation, getAlertDestinationsDeliveryDismissLabel, @@ -204,6 +205,22 @@ describe('alertDestinationsPresentation', () => { 'Delivery history remains available', ); }); + + it('summarizes degraded delivery health without configuration guidance', () => { + expect( + getAlertDestinationsDeliveryHealthSummary({ + status: 'degraded', + failed: 1, + deadLetter: 2, + completedRetentionDays: 7, + deadLetterRetentionDays: 30, + failureClasses7d: { connectivity: 3 }, + failureClassesAvailable: true, + }), + ).toBe( + '1 failed delivery retained for 7 days and 2 dead-lettered deliveries retained for 30 days. These notifications were not delivered. Most recent failures: connectivity (3). Review delivery activity for timestamps, destinations, alerts, and errors.', + ); + }); }); describe('alert destinations delivery log copy', () => { diff --git a/frontend-modern/src/utils/alertDestinationsPresentation.ts b/frontend-modern/src/utils/alertDestinationsPresentation.ts index c988f743c..e4721617e 100644 --- a/frontend-modern/src/utils/alertDestinationsPresentation.ts +++ b/frontend-modern/src/utils/alertDestinationsPresentation.ts @@ -168,7 +168,7 @@ export function getAlertDestinationsDeliveryHealthTitle(status: 'degraded' | 'un : ALERT_DESTINATIONS_DELIVERY_UNAVAILABLE_TITLE; } -export function getAlertDestinationsDeliveryHealthDescription(input: { +type AlertDestinationsDeliveryHealthInput = { status: 'degraded' | 'unavailable'; failed: number; deadLetter: number; @@ -176,11 +176,9 @@ export function getAlertDestinationsDeliveryHealthDescription(input: { deadLetterRetentionDays: number; failureClasses7d?: Record; failureClassesAvailable?: boolean; -}) { - if (input.status === 'unavailable') { - return 'Pulse could not verify the notification queue. Review the destination settings below and send a test before relying on delivery.'; - } +}; +function getRetainedDeliveryOutcomes(input: AlertDestinationsDeliveryHealthInput) { const outcomes: string[] = []; if (input.failed > 0) { outcomes.push( @@ -192,7 +190,38 @@ export function getAlertDestinationsDeliveryHealthDescription(input: { `${input.deadLetter} dead-lettered ${input.deadLetter === 1 ? 'delivery' : 'deliveries'} retained for ${input.deadLetterRetentionDays} days`, ); } - const summary = outcomes.join(' and ') || 'A retained terminal delivery failure'; + return outcomes.join(' and ') || 'A retained terminal delivery failure'; +} + +function getDominantDeliveryFailure(input: AlertDestinationsDeliveryHealthInput) { + if (!input.failureClassesAvailable || !input.failureClasses7d) return undefined; + return Object.entries(input.failureClasses7d) + .filter(([, count]) => count > 0) + .sort((left, right) => right[1] - left[1])[0]; +} + +export function getAlertDestinationsDeliveryHealthSummary( + input: AlertDestinationsDeliveryHealthInput, +) { + if (input.status === 'unavailable') { + return 'Pulse could not verify the notification queue. Review notification delivery before relying on it.'; + } + + const dominant = getDominantDeliveryFailure(input); + const diagnosis = dominant + ? ` Most recent failures: ${dominant[0].replace('_', ' ')} (${dominant[1]}).` + : ''; + return `${getRetainedDeliveryOutcomes(input)}. These notifications were not delivered.${diagnosis} Review delivery activity for timestamps, destinations, alerts, and errors.`; +} + +export function getAlertDestinationsDeliveryHealthDescription( + input: AlertDestinationsDeliveryHealthInput, +) { + if (input.status === 'unavailable') { + return 'Pulse could not verify the notification queue. Review the destination settings below and send a test before relying on delivery.'; + } + + const summary = getRetainedDeliveryOutcomes(input); const guidanceByClass: Record = { authentication: 'Check destination credentials, tokens, and account permissions.', rate_limited: 'Check provider rate limits and reduce delivery volume before retrying.', @@ -203,13 +232,9 @@ export function getAlertDestinationsDeliveryHealthDescription(input: { unknown: 'Review the local notification audit details for the terminal error.', }; let diagnostic = 'Check each enabled destination and send a test.'; - if (input.failureClassesAvailable && input.failureClasses7d) { - const dominant = Object.entries(input.failureClasses7d) - .filter(([, count]) => count > 0) - .sort((left, right) => right[1] - left[1])[0]; - if (dominant) { - diagnostic = `Most recent terminal failures were classified as ${dominant[0].replace('_', ' ')} (${dominant[1]}). ${guidanceByClass[dominant[0]] ?? guidanceByClass.unknown}`; - } + const dominant = getDominantDeliveryFailure(input); + if (dominant) { + diagnostic = `Most recent terminal failures were classified as ${dominant[0].replace('_', ' ')} (${dominant[1]}). ${guidanceByClass[dominant[0]] ?? guidanceByClass.unknown}`; } return `${summary}. These notifications were not delivered. ${diagnostic} Review delivery activity in Notifications for timestamps, destinations, alerts, and safely redacted errors. After correcting the destination, retry them. Dismiss retained failures to clear this warning without deleting delivery history. Otherwise Pulse removes expired records hourly after their retention limit. Recoverable retry attempts do not trigger this warning.`; }