Extract alerts configuration state owner

This commit is contained in:
rcourtman
2026-03-22 12:04:59 +00:00
parent 4882508929
commit d5dc1752a1
6 changed files with 1576 additions and 1401 deletions
@@ -196,8 +196,11 @@ override normalization now route through
`frontend-modern/src/features/alerts/AlertsConfigurationSurface.tsx` instead of
living inline in `frontend-modern/src/pages/Alerts.tsx`. The page shell owns
navigation, activation chrome, and cross-surface routing; the configuration
surface owns the alert config controller and composes the destinations,
schedule, and thresholds tabs beneath that feature boundary.
surface is now a shell that composes the destinations, schedule, and thresholds
tabs, while `frontend-modern/src/features/alerts/useAlertsConfigurationState.ts`
owns alert config load/save behavior, notification-config reloads, defaults,
override normalization, and derived resource mapping beneath that feature
boundary.
Alert filter metadata and grouped header consumers must also preserve the
canonical `agent` and `node` header boundary when reusing shared filter
@@ -416,10 +416,12 @@ component.
The alerts page now also applies the same shell-versus-feature rule to
configuration orchestration. `frontend-modern/src/pages/Alerts.tsx` is the page
shell, while `frontend-modern/src/features/alerts/AlertsConfigurationSurface.tsx`
is the feature shell. The canonical runtime owner is now
`frontend-modern/src/features/alerts/useAlertsConfigurationState.ts`, which
owns alert config load/save behavior, notification-config reloads, defaults,
and threshold-override normalization for the destinations, schedule, and
thresholds tabs. Future cleanup should continue by moving page-local config
control flow into that feature surface or a narrower shared primitive, not back
derived resource mapping, and threshold-override normalization for the
destinations, schedule, and thresholds tabs. Future cleanup should extend that
feature hook or a narrower shared primitive, not move config control flow back
into the top-level page shell.
Top-level settings surfaces must route through `Settings.tsx`,
`SettingsPageShell.tsx`, and
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest';
import alertsPageSource from '@/pages/Alerts.tsx?raw';
import alertsConfigurationSurfaceSource from '@/features/alerts/AlertsConfigurationSurface.tsx?raw';
import alertsConfigurationStateSource from '@/features/alerts/useAlertsConfigurationState.ts?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';
@@ -176,10 +177,16 @@ describe('tab path helpers', () => {
expect(alertsConfigurationSurfaceSource).toContain(
"import { ThresholdsTab } from './tabs/ThresholdsTab';",
);
expect(alertsConfigurationSurfaceSource).toContain('AlertsAPI.getConfig');
expect(alertsConfigurationSurfaceSource).toContain('NotificationsAPI.getEmailConfig');
expect(alertsConfigurationSurfaceSource).toContain('NotificationsAPI.updateEmailConfig');
expect(alertsConfigurationSurfaceSource).toContain("eventBus.on('org_switched'");
expect(alertsConfigurationSurfaceSource).toContain('useAlertsConfigurationState');
expect(alertsConfigurationSurfaceSource).not.toContain('AlertsAPI.getConfig');
expect(alertsConfigurationSurfaceSource).not.toContain('NotificationsAPI.getEmailConfig');
expect(alertsConfigurationSurfaceSource).not.toContain('NotificationsAPI.updateEmailConfig');
expect(alertsConfigurationSurfaceSource).not.toContain("eventBus.on('org_switched'");
expect(alertsConfigurationStateSource).toContain('export function useAlertsConfigurationState');
expect(alertsConfigurationStateSource).toContain('AlertsAPI.getConfig');
expect(alertsConfigurationStateSource).toContain('NotificationsAPI.getEmailConfig');
expect(alertsConfigurationStateSource).toContain('NotificationsAPI.updateEmailConfig');
expect(alertsConfigurationStateSource).toContain("eventBus.on('org_switched'");
expect(alertsPageSource).toContain(
"import { HistoryTab } from '@/features/alerts/tabs/HistoryTab';",
);
@@ -325,6 +325,7 @@ import alertResourceTablePresentationSource from '@/utils/alertResourceTablePres
import alertWebhookPresentationSource from '@/utils/alertWebhookPresentation.ts?raw';
import alertOverviewTabSource from '@/features/alerts/OverviewTab.tsx?raw';
import alertsConfigurationSurfaceSource from '@/features/alerts/AlertsConfigurationSurface.tsx?raw';
import alertsConfigurationStateSource from '@/features/alerts/useAlertsConfigurationState.ts?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';
@@ -2760,7 +2761,13 @@ describe('frontend resource type boundaries', () => {
"import { AlertsConfigurationSurface } from '@/features/alerts/AlertsConfigurationSurface';",
);
expect(alertsPageSource).not.toContain('getAlertDestinationsConfigLoadError');
expect(alertsConfigurationSurfaceSource).toContain('getAlertDestinationsConfigLoadError');
expect(alertsConfigurationSurfaceSource).toContain('useAlertsConfigurationState');
expect(alertsConfigurationSurfaceSource).not.toContain('AlertsAPI.getConfig');
expect(alertsConfigurationStateSource).toContain('getAlertDestinationsConfigLoadError');
expect(alertsConfigurationStateSource).toContain('AlertsAPI.getConfig');
expect(alertsConfigurationStateSource).toContain('NotificationsAPI.getEmailConfig');
expect(alertsConfigurationStateSource).toContain('NotificationsAPI.updateEmailConfig');
expect(alertsConfigurationStateSource).toContain("eventBus.on('org_switched'");
expect(alertDestinationsTabSource).toContain('getAlertDestinationsWebhookLoadError');
expect(alertDestinationsTabSource).toContain('getAlertDestinationsLoadErrorBanner');
expect(alertDestinationsTabSource).toContain('getAlertDestinationsAppriseTargetsHelp');
@@ -2854,9 +2861,9 @@ describe('frontend resource type boundaries', () => {
expect(alertsPageSource).not.toContain('getAlertConfigUnsavedChangesLabel');
expect(alertsConfigurationSurfaceSource).toContain('getAlertConfigUnsavedChangesLabel');
expect(alertsConfigurationSurfaceSource).toContain('getAlertConfigSaveChangesLabel');
expect(alertsConfigurationSurfaceSource).toContain('getAlertConfigDiscardedSuccess');
expect(alertsConfigurationSurfaceSource).toContain('getAlertConfigReloadFailure');
expect(alertsConfigurationSurfaceSource).toContain('getAlertConfigDiscardLabel');
expect(alertsConfigurationStateSource).toContain('getAlertConfigDiscardedSuccess');
expect(alertsConfigurationStateSource).toContain('getAlertConfigReloadFailure');
expect(alertsPageSource).toContain('getAlertConfigLeaveConfirmation');
expect(alertScheduleTabSource).toContain('getAlertConfigResetDefaultsLabel');
expect(alertScheduleTabSource).toContain('getAlertConfigResetDefaultsTitle');
@@ -3757,6 +3764,7 @@ describe('frontend resource type boundaries', () => {
expect(alertsConfigurationSurfaceSource).toContain(
"import { ScheduleTab } from './tabs/ScheduleTab';",
);
expect(alertsConfigurationSurfaceSource).toContain('useAlertsConfigurationState');
expect(alertsPageSource).not.toContain('function DestinationsTab(');
expect(alertsPageSource).not.toContain('function ScheduleTab(');
expect(alertDestinationsTabSource).toContain('NotificationsAPI.getWebhooks');