mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-05-14 16:40:30 +00:00
Extract alerts configuration surface
This commit is contained in:
parent
5f0f6119c8
commit
4ff36e2b90
6 changed files with 1555 additions and 1616 deletions
|
|
@ -152,11 +152,19 @@ The alerts page shell in `frontend-modern/src/pages/Alerts.tsx` must now keep
|
|||
destinations, history, schedule, and thresholds 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
|
||||
so the page owns navigation and cross-surface routing while tab files own their
|
||||
runtime presentation, tab-local interaction logic, and any history-table
|
||||
presentation or thresholds-table adapter logic that does not belong in a shared
|
||||
primitive.
|
||||
|
||||
Alert configuration load/save state, notification config reloads, and threshold
|
||||
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.
|
||||
|
||||
Alert filter metadata and grouped header consumers must also preserve the
|
||||
canonical `agent` and `node` header boundary when reusing shared filter
|
||||
primitives. Frontend alert tables may not drift back to ad hoc host-key
|
||||
|
|
|
|||
|
|
@ -220,8 +220,8 @@ Pulse resource, then layer on additional context.
|
|||
The settings shell is now also a governed frontend primitive boundary.
|
||||
|
||||
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/pages/Alerts.tsx` owns navigation and cross-surface
|
||||
routing, 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` and
|
||||
|
|
@ -231,6 +231,15 @@ continue by extracting page-local tab blocks into feature modules rather than
|
|||
expanding the top-level page file again, and history-table behavior or
|
||||
thresholds-table adapter logic should stay feature-owned unless it graduates
|
||||
into a shared primitive used by more than one alert surface.
|
||||
|
||||
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`
|
||||
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
|
||||
into the top-level page shell.
|
||||
Top-level settings surfaces must route through `Settings.tsx`,
|
||||
`SettingsPageShell.tsx`, and
|
||||
`frontend-modern/src/components/shared/SettingsPanel.tsx` instead of
|
||||
|
|
|
|||
1482
frontend-modern/src/features/alerts/AlertsConfigurationSurface.tsx
Normal file
1482
frontend-modern/src/features/alerts/AlertsConfigurationSurface.tsx
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,5 +1,6 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import alertsPageSource from '@/pages/Alerts.tsx?raw';
|
||||
import alertsConfigurationSurfaceSource from '@/features/alerts/AlertsConfigurationSurface.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';
|
||||
|
|
@ -154,23 +155,32 @@ describe('tab path helpers', () => {
|
|||
expect(tabFromPath('/alerts/summary', custom)).toBe('overview');
|
||||
});
|
||||
|
||||
it('keeps destinations, history, schedule, and thresholds tabs feature-owned', () => {
|
||||
it('keeps alerts configuration owned by a feature surface instead of the page shell', () => {
|
||||
expect(alertsPageSource).toContain(
|
||||
"import { DestinationsTab } from '@/features/alerts/tabs/DestinationsTab';",
|
||||
"import { AlertsConfigurationSurface } from '@/features/alerts/AlertsConfigurationSurface';",
|
||||
);
|
||||
expect(alertsPageSource).toContain(
|
||||
"import { HistoryTab } from '@/features/alerts/tabs/HistoryTab';",
|
||||
);
|
||||
expect(alertsPageSource).toContain(
|
||||
"import { ScheduleTab } from '@/features/alerts/tabs/ScheduleTab';",
|
||||
expect(alertsPageSource).not.toContain('const loadAlertConfiguration = async');
|
||||
expect(alertsPageSource).not.toContain('const FACTORY_GUEST_DEFAULTS =');
|
||||
expect(alertsConfigurationSurfaceSource).toContain(
|
||||
"import { DestinationsTab } from './tabs/DestinationsTab';",
|
||||
);
|
||||
expect(alertsPageSource).toContain(
|
||||
"import { ThresholdsTab } from '@/features/alerts/tabs/ThresholdsTab';",
|
||||
expect(alertsConfigurationSurfaceSource).toContain(
|
||||
"import { ScheduleTab } from './tabs/ScheduleTab';",
|
||||
);
|
||||
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(alertsPageSource).toContain(
|
||||
"import { HistoryTab } from '@/features/alerts/tabs/HistoryTab';",
|
||||
);
|
||||
expect(alertsPageSource).not.toContain('function DestinationsTab(');
|
||||
expect(alertsPageSource).not.toContain('function HistoryTab(');
|
||||
expect(alertsPageSource).not.toContain('function ScheduleTab(');
|
||||
expect(alertsPageSource).not.toContain('function ThresholdsTab(');
|
||||
expect(alertDestinationsTabSource).toContain('NotificationsAPI.getWebhooks');
|
||||
expect(alertHistoryTabSource).toContain('AlertsAPI.getHistory');
|
||||
expect(alertHistoryTabSource).toContain('IncidentTimelinePanel');
|
||||
|
|
|
|||
|
|
@ -223,6 +223,7 @@ import alertOverviewPresentationSource from '@/utils/alertOverviewPresentation.t
|
|||
import alertResourceTablePresentationSource from '@/utils/alertResourceTablePresentation.ts?raw';
|
||||
import alertWebhookPresentationSource from '@/utils/alertWebhookPresentation.ts?raw';
|
||||
import alertOverviewTabSource from '@/features/alerts/OverviewTab.tsx?raw';
|
||||
import alertsConfigurationSurfaceSource from '@/features/alerts/AlertsConfigurationSurface.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';
|
||||
|
|
@ -2188,7 +2189,9 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(deployStatusPresentationSource).toContain('export const getDeployStatusPresentation');
|
||||
expect(alertHistoryTabSource).toContain('getAlertIncidentStatusPresentation');
|
||||
expect(alertHistoryTabSource).toContain('getAlertIncidentLevelBadgeClass');
|
||||
expect(alertsPageSource).toContain('getAlertDestinationsConfigLoadError');
|
||||
expect(alertsPageSource).toContain("import { AlertsConfigurationSurface } from '@/features/alerts/AlertsConfigurationSurface';");
|
||||
expect(alertsPageSource).not.toContain('getAlertDestinationsConfigLoadError');
|
||||
expect(alertsConfigurationSurfaceSource).toContain('getAlertDestinationsConfigLoadError');
|
||||
expect(alertDestinationsTabSource).toContain('getAlertDestinationsWebhookLoadError');
|
||||
expect(alertDestinationsTabSource).toContain('getAlertDestinationsLoadErrorBanner');
|
||||
expect(alertDestinationsTabSource).toContain('getAlertDestinationsAppriseTargetsHelp');
|
||||
|
|
@ -2249,7 +2252,8 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(alertsPageSource).toContain('getAlertsMobileTabClass');
|
||||
expect(alertsPageSource).toContain('getAlertsTabTitle');
|
||||
expect(alertsPageSource).toContain('getAlertsTabGroups');
|
||||
expect(alertsPageSource).toContain("import { ThresholdsTab } from '@/features/alerts/tabs/ThresholdsTab';");
|
||||
expect(alertsConfigurationSurfaceSource).toContain("import { ThresholdsTab } from './tabs/ThresholdsTab';");
|
||||
expect(alertsPageSource).not.toContain("import { ThresholdsTab } from '@/features/alerts/tabs/ThresholdsTab';");
|
||||
expect(alertsPageSource).not.toContain("import { ThresholdsTable } from '@/components/Alerts/ThresholdsTable';");
|
||||
expect(alertsPageSource).not.toContain('function ThresholdsTab(');
|
||||
expect(alertThresholdsTabSource).toContain("import { ThresholdsTable } from '@/components/Alerts/ThresholdsTable';");
|
||||
|
|
@ -2259,11 +2263,12 @@ describe('frontend resource type boundaries', () => {
|
|||
expect(alertScheduleTabSource).toContain('getAlertQuietDayButtonClass');
|
||||
expect(alertScheduleTabSource).toContain('getAlertQuietSuppressCardClass');
|
||||
expect(alertScheduleTabSource).toContain('getAlertQuietSuppressCheckboxClass');
|
||||
expect(alertsPageSource).toContain('getAlertConfigUnsavedChangesLabel');
|
||||
expect(alertsPageSource).toContain('getAlertConfigSaveChangesLabel');
|
||||
expect(alertsPageSource).toContain('getAlertConfigDiscardedSuccess');
|
||||
expect(alertsPageSource).toContain('getAlertConfigReloadFailure');
|
||||
expect(alertsPageSource).toContain('getAlertConfigDiscardLabel');
|
||||
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(alertsPageSource).toContain('getAlertConfigLeaveConfirmation');
|
||||
expect(alertScheduleTabSource).toContain('getAlertConfigResetDefaultsLabel');
|
||||
expect(alertScheduleTabSource).toContain('getAlertConfigResetDefaultsTitle');
|
||||
|
|
@ -3045,10 +3050,13 @@ describe('frontend resource type boundaries', () => {
|
|||
|
||||
it('keeps alerts configuration tabs feature-owned instead of page-local', () => {
|
||||
expect(alertsPageSource).toContain(
|
||||
"import { DestinationsTab } from '@/features/alerts/tabs/DestinationsTab';",
|
||||
"import { AlertsConfigurationSurface } from '@/features/alerts/AlertsConfigurationSurface';",
|
||||
);
|
||||
expect(alertsPageSource).toContain(
|
||||
"import { ScheduleTab } from '@/features/alerts/tabs/ScheduleTab';",
|
||||
expect(alertsConfigurationSurfaceSource).toContain(
|
||||
"import { DestinationsTab } from './tabs/DestinationsTab';",
|
||||
);
|
||||
expect(alertsConfigurationSurfaceSource).toContain(
|
||||
"import { ScheduleTab } from './tabs/ScheduleTab';",
|
||||
);
|
||||
expect(alertsPageSource).not.toContain('function DestinationsTab(');
|
||||
expect(alertsPageSource).not.toContain('function ScheduleTab(');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue