Extract alert history tab

This commit is contained in:
rcourtman 2026-03-20 15:16:28 +00:00
parent 283eb1d341
commit ee178e967c
6 changed files with 1681 additions and 1696 deletions

View file

@ -149,11 +149,12 @@ assume discovery metadata is always present when deriving override IDs or
toggle styling.
The alerts page shell in `frontend-modern/src/pages/Alerts.tsx` must now keep
destinations and schedule rendering feature-owned under
destinations, history, and schedule rendering feature-owned under
`frontend-modern/src/features/alerts/tabs/`. New alert tab surfaces should be
extracted as feature modules instead of remaining page-local function blocks,
so the page owns navigation/save orchestration while tab files own their
runtime presentation and tab-local interaction logic.
runtime presentation, tab-local interaction logic, and any history-table
presentation that does not belong in a shared primitive.
Alert filter metadata and grouped header consumers must also preserve the
canonical `agent` and `node` header boundary when reusing shared filter

View file

@ -223,10 +223,12 @@ The alerts page shell now follows that same page-shell rule for feature tabs:
`frontend-modern/src/pages/Alerts.tsx` owns navigation, load/save
orchestration, and cross-tab state, while feature-owned tab surfaces such as
`frontend-modern/src/features/alerts/tabs/DestinationsTab.tsx` and
`frontend-modern/src/features/alerts/tabs/HistoryTab.tsx` plus
`frontend-modern/src/features/alerts/tabs/ScheduleTab.tsx` own their tab-local
rendering and interaction logic. Future alert tab cleanup should continue by
extracting page-local tab blocks into feature modules rather than expanding the
top-level page file again.
top-level page file again, and history-table behavior should stay feature-owned
unless it graduates into a shared primitive used by more than one alert surface.
Top-level settings surfaces must route through `Settings.tsx`,
`SettingsPageShell.tsx`, and
`frontend-modern/src/components/shared/SettingsPanel.tsx` instead of

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest';
import alertsPageSource from '@/pages/Alerts.tsx?raw';
import alertDestinationsTabSource from '@/features/alerts/tabs/DestinationsTab.tsx?raw';
import alertHistoryTabSource from '@/features/alerts/tabs/HistoryTab.tsx?raw';
import alertScheduleTabSource from '@/features/alerts/tabs/ScheduleTab.tsx?raw';
import {
@ -152,16 +153,22 @@ describe('tab path helpers', () => {
expect(tabFromPath('/alerts/summary', custom)).toBe('overview');
});
it('keeps destinations and schedule tabs feature-owned', () => {
it('keeps destinations, history, and schedule tabs feature-owned', () => {
expect(alertsPageSource).toContain(
"import { DestinationsTab } from '@/features/alerts/tabs/DestinationsTab';",
);
expect(alertsPageSource).toContain(
"import { HistoryTab } from '@/features/alerts/tabs/HistoryTab';",
);
expect(alertsPageSource).toContain(
"import { ScheduleTab } from '@/features/alerts/tabs/ScheduleTab';",
);
expect(alertsPageSource).not.toContain('function DestinationsTab(');
expect(alertsPageSource).not.toContain('function HistoryTab(');
expect(alertsPageSource).not.toContain('function ScheduleTab(');
expect(alertDestinationsTabSource).toContain('NotificationsAPI.getWebhooks');
expect(alertHistoryTabSource).toContain('AlertsAPI.getHistory');
expect(alertHistoryTabSource).toContain('IncidentTimelinePanel');
expect(alertScheduleTabSource).toContain('getAlertConfigQuietHourSuppressOptions');
});
});

View file

@ -224,6 +224,7 @@ import alertResourceTablePresentationSource from '@/utils/alertResourceTablePres
import alertWebhookPresentationSource from '@/utils/alertWebhookPresentation.ts?raw';
import alertOverviewTabSource from '@/features/alerts/OverviewTab.tsx?raw';
import alertDestinationsTabSource from '@/features/alerts/tabs/DestinationsTab.tsx?raw';
import alertHistoryTabSource from '@/features/alerts/tabs/HistoryTab.tsx?raw';
import alertScheduleTabSource from '@/features/alerts/tabs/ScheduleTab.tsx?raw';
import alertIncidentPresentationSource from '@/utils/alertIncidentPresentation.ts?raw';
import alertHistoryPresentationSource from '@/utils/alertHistoryPresentation.ts?raw';
@ -2184,8 +2185,8 @@ describe('frontend resource type boundaries', () => {
'export function getDeployInstallCommandLoadingState',
);
expect(deployStatusPresentationSource).toContain('export const getDeployStatusPresentation');
expect(alertsPageSource).toContain('getAlertIncidentStatusPresentation');
expect(alertsPageSource).toContain('getAlertIncidentLevelBadgeClass');
expect(alertHistoryTabSource).toContain('getAlertIncidentStatusPresentation');
expect(alertHistoryTabSource).toContain('getAlertIncidentLevelBadgeClass');
expect(alertsPageSource).toContain('getAlertDestinationsConfigLoadError');
expect(alertDestinationsTabSource).toContain('getAlertDestinationsWebhookLoadError');
expect(alertDestinationsTabSource).toContain('getAlertDestinationsLoadErrorBanner');
@ -2200,47 +2201,49 @@ describe('frontend resource type boundaries', () => {
expect(alertDestinationsTabSource).toContain('getAlertDestinationsStatusLabel');
expect(alertDestinationsTabSource).toContain('getAlertWebhookTestSuccess');
expect(alertDestinationsTabSource).toContain('getAlertWebhookTestFailure');
expect(alertsPageSource).toContain('getAlertHistoryStatusPresentation');
expect(alertsPageSource).toContain('getAlertHistorySourcePresentation');
expect(alertsPageSource).toContain('getAlertHistoryResourceTypeBadgeClass');
expect(alertsPageSource).toContain('getAlertResourceIncidentPanelTitle');
expect(alertsPageSource).toContain('getAlertResourceIncidentCountLabel');
expect(alertsPageSource).toContain('getAlertResourceIncidentLoadingState');
expect(alertsPageSource).toContain('getAlertResourceIncidentEmptyState');
expect(alertsPageSource).toContain('getAlertResourceIncidentRefreshLabel');
expect(alertsPageSource).toContain('getAlertResourceIncidentAcknowledgedByLabel');
expect(alertsPageSource).toContain('getAlertResourceIncidentToggleLabel');
expect(alertsPageSource).toContain('getAlertResourceIncidentFilteredEventsEmptyState');
expect(alertsPageSource).toContain('IncidentEventFilters');
expect(alertsPageSource).toContain('IncidentTimelinePanel');
expect(alertsPageSource).toContain('getAlertIncidentTimelineMetaRowClass');
expect(alertsPageSource).toContain('getAlertIncidentTimelineHeadingClass');
expect(alertsPageSource).toContain('IncidentTimelineEventCard');
expect(alertsPageSource).not.toContain('getAlertIncidentTimelineEventCardClass');
expect(alertsPageSource).not.toContain('getAlertIncidentTimelineDetailClass');
expect(alertsPageSource).not.toContain('getAlertIncidentTimelineCommandClass');
expect(alertsPageSource).toContain('getAlertResourceIncidentCardClass');
expect(alertsPageSource).toContain('getAlertResourceIncidentSummaryRowClass');
expect(alertsPageSource).toContain('getAlertResourceIncidentToggleButtonClass');
expect(alertsPageSource).toContain('getAlertResourceIncidentTruncatedEventsLabel');
expect(alertsPageSource).toContain('getAlertBucketCountLabel');
expect(alertsPageSource).toContain('getAlertHistorySearchPlaceholder');
expect(alertsPageSource).toContain("import { HistoryTab } from '@/features/alerts/tabs/HistoryTab';");
expect(alertsPageSource).not.toContain('function HistoryTab(');
expect(alertHistoryTabSource).toContain('getAlertHistoryStatusPresentation');
expect(alertHistoryTabSource).toContain('getAlertHistorySourcePresentation');
expect(alertHistoryTabSource).toContain('getAlertHistoryResourceTypeBadgeClass');
expect(alertHistoryTabSource).toContain('getAlertResourceIncidentPanelTitle');
expect(alertHistoryTabSource).toContain('getAlertResourceIncidentCountLabel');
expect(alertHistoryTabSource).toContain('getAlertResourceIncidentLoadingState');
expect(alertHistoryTabSource).toContain('getAlertResourceIncidentEmptyState');
expect(alertHistoryTabSource).toContain('getAlertResourceIncidentRefreshLabel');
expect(alertHistoryTabSource).toContain('getAlertResourceIncidentAcknowledgedByLabel');
expect(alertHistoryTabSource).toContain('getAlertResourceIncidentToggleLabel');
expect(alertHistoryTabSource).toContain('getAlertResourceIncidentFilteredEventsEmptyState');
expect(alertHistoryTabSource).toContain('IncidentEventFilters');
expect(alertHistoryTabSource).toContain('IncidentTimelinePanel');
expect(alertHistoryTabSource).toContain('getAlertIncidentTimelineMetaRowClass');
expect(alertHistoryTabSource).toContain('getAlertIncidentTimelineHeadingClass');
expect(alertHistoryTabSource).toContain('IncidentTimelineEventCard');
expect(alertHistoryTabSource).not.toContain('getAlertIncidentTimelineEventCardClass');
expect(alertHistoryTabSource).not.toContain('getAlertIncidentTimelineDetailClass');
expect(alertHistoryTabSource).not.toContain('getAlertIncidentTimelineCommandClass');
expect(alertHistoryTabSource).toContain('getAlertResourceIncidentCardClass');
expect(alertHistoryTabSource).toContain('getAlertResourceIncidentSummaryRowClass');
expect(alertHistoryTabSource).toContain('getAlertResourceIncidentToggleButtonClass');
expect(alertHistoryTabSource).toContain('getAlertResourceIncidentTruncatedEventsLabel');
expect(alertHistoryTabSource).toContain('getAlertBucketCountLabel');
expect(alertHistoryTabSource).toContain('getAlertHistorySearchPlaceholder');
expect(alertsPageSource).toContain('getAlertsPageHeaderMeta');
expect(alertsPageSource).toContain('getAlertHistoryEmptyState');
expect(alertsPageSource).toContain('getAlertHistoryLoadingState');
expect(alertsPageSource).toContain('getAlertAdministrationSectionTitle');
expect(alertsPageSource).toContain('getAlertAdministrationSectionDescription');
expect(alertsPageSource).toContain('getAlertAdministrationClearHistoryLabel');
expect(alertsPageSource).toContain('getAlertAdministrationClearHistoryError');
expect(alertsPageSource).toContain('getAlertAdministrationClearHistoryConfirmation');
expect(alertHistoryTabSource).toContain('getAlertHistoryEmptyState');
expect(alertHistoryTabSource).toContain('getAlertHistoryLoadingState');
expect(alertHistoryTabSource).toContain('getAlertAdministrationSectionTitle');
expect(alertHistoryTabSource).toContain('getAlertAdministrationSectionDescription');
expect(alertHistoryTabSource).toContain('getAlertAdministrationClearHistoryLabel');
expect(alertHistoryTabSource).toContain('getAlertAdministrationClearHistoryError');
expect(alertHistoryTabSource).toContain('getAlertAdministrationClearHistoryConfirmation');
expect(alertsPageSource).toContain('getAlertActivationPresentation');
expect(alertsPageSource).toContain('getAlertActivationSuccess');
expect(alertsPageSource).toContain('getAlertActivationFailure');
expect(alertsPageSource).toContain('getAlertDeactivationSuccess');
expect(alertsPageSource).toContain('getAlertDeactivationFailure');
expect(alertsPageSource).toContain('getAlertFrequencySelectionPresentation');
expect(alertsPageSource).toContain('getAlertFrequencyClearFilterButtonClass');
expect(alertsPageSource).toContain('getAlertSeverityDotClass');
expect(alertHistoryTabSource).toContain('getAlertFrequencySelectionPresentation');
expect(alertHistoryTabSource).toContain('getAlertFrequencyClearFilterButtonClass');
expect(alertHistoryTabSource).toContain('getAlertSeverityDotClass');
expect(alertsPageSource).toContain('getAlertsSidebarTabClass');
expect(alertsPageSource).toContain('getAlertsMobileTabClass');
expect(alertsPageSource).toContain('getAlertsTabTitle');