diff --git a/docs/release-control/v6/internal/subsystems/alerts.md b/docs/release-control/v6/internal/subsystems/alerts.md
index 7d25151ed..7f535a23c 100644
--- a/docs/release-control/v6/internal/subsystems/alerts.md
+++ b/docs/release-control/v6/internal/subsystems/alerts.md
@@ -271,6 +271,12 @@ provider-catalog loading and provider-default application, while
`frontend-modern/src/components/Alerts/EmailProviderSelect.tsx` stays the
render shell and consumes the canonical `UIEmailConfig` feature type instead of
keeping a second local email-config interface.
+The alert scheduling surface now follows the same shell/section split:
+`frontend-modern/src/features/alerts/useAlertScheduleState.ts` owns schedule
+runtime and default/reset policy, while
+`frontend-modern/src/features/alerts/tabs/ScheduleTab.tsx` stays the shell and
+composes the dedicated quiet-hours, cooldown, grouping, recovery, escalation,
+and summary section owners instead of carrying those panels inline.
Alert filter metadata and grouped header consumers must also preserve the
canonical `agent` and `node` header boundary when reusing shared filter
diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md
index 6f91f69aa..528febdab 100644
--- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md
+++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md
@@ -467,6 +467,11 @@ provider-catalog loading and provider-default application, while
`frontend-modern/src/components/Alerts/EmailProviderSelect.tsx` stays the
render shell and should not re-accumulate `NotificationsAPI.getEmailProviders`
or a second local email-config contract inline.
+The alert scheduling surface now follows the same shell-versus-section split:
+`frontend-modern/src/features/alerts/tabs/ScheduleTab.tsx` should compose the
+dedicated quiet-hours, cooldown, grouping, recovery, escalation, and summary
+section owners while `frontend-modern/src/features/alerts/useAlertScheduleState.ts`
+remains the canonical runtime owner.
The same rule now also covers cross-tab incident timelines: the shared runtime
owner is `frontend-modern/src/features/alerts/useAlertIncidentTimelineState.ts`,
while `frontend-modern/src/features/alerts/OverviewTab.tsx` and
diff --git a/frontend-modern/src/features/alerts/AlertCooldownSection.tsx b/frontend-modern/src/features/alerts/AlertCooldownSection.tsx
new file mode 100644
index 000000000..8a9d2f1c5
--- /dev/null
+++ b/frontend-modern/src/features/alerts/AlertCooldownSection.tsx
@@ -0,0 +1,98 @@
+import { Show } from 'solid-js';
+
+import {
+ controlClass,
+ formHelpText,
+ formField,
+ labelClass,
+} from '@/components/shared/Form';
+import { SettingsPanel } from '@/components/shared/SettingsPanel';
+import { Toggle } from '@/components/shared/Toggle';
+import {
+ ALERT_CONFIG_COOLDOWN_DESCRIPTION,
+ ALERT_CONFIG_COOLDOWN_MAX_ALERTS_HELP,
+ ALERT_CONFIG_COOLDOWN_MAX_ALERTS_LABEL,
+ ALERT_CONFIG_COOLDOWN_MAX_ALERTS_SUFFIX,
+ ALERT_CONFIG_COOLDOWN_PERIOD_HELP,
+ ALERT_CONFIG_COOLDOWN_PERIOD_LABEL,
+ ALERT_CONFIG_COOLDOWN_PERIOD_SUFFIX,
+ ALERT_CONFIG_COOLDOWN_TITLE,
+ getAlertConfigToggleStatusLabel,
+} from '@/utils/alertConfigPresentation';
+
+import type { CooldownConfig } from './types';
+
+interface AlertCooldownSectionProps {
+ cooldown: CooldownConfig;
+ setCooldownEnabled: (value: boolean) => void;
+ setCooldownMinutes: (value: string) => void;
+ setCooldownMaxAlerts: (value: string) => void;
+}
+
+export function AlertCooldownSection(props: AlertCooldownSectionProps) {
+ return (
+ props.setCooldownEnabled(event.currentTarget.checked)}
+ containerClass="sm:self-start"
+ label={
+
+ {getAlertConfigToggleStatusLabel(props.cooldown.enabled)}
+
+ }
+ />
+ }
+ class="space-y-4"
+ >
+
+
+
+
+ );
+}
diff --git a/frontend-modern/src/features/alerts/AlertEscalationSection.tsx b/frontend-modern/src/features/alerts/AlertEscalationSection.tsx
new file mode 100644
index 000000000..0a7aa4e93
--- /dev/null
+++ b/frontend-modern/src/features/alerts/AlertEscalationSection.tsx
@@ -0,0 +1,132 @@
+import { For, Show } from 'solid-js';
+
+import { controlClass, formHelpText } from '@/components/shared/Form';
+import { SettingsPanel } from '@/components/shared/SettingsPanel';
+import { Toggle } from '@/components/shared/Toggle';
+import {
+ ALERT_CONFIG_ESCALATION_ADD_LABEL,
+ ALERT_CONFIG_ESCALATION_AFTER_LABEL,
+ ALERT_CONFIG_ESCALATION_DESCRIPTION,
+ ALERT_CONFIG_ESCALATION_MINUTES_SUFFIX,
+ ALERT_CONFIG_ESCALATION_NOTIFY_LABEL,
+ ALERT_CONFIG_ESCALATION_REMOVE_TITLE,
+ ALERT_CONFIG_ESCALATION_TITLE,
+ getAlertConfigEscalationHelp,
+ getAlertConfigEscalationNotifyLabel,
+ getAlertConfigToggleStatusLabel,
+} from '@/utils/alertConfigPresentation';
+
+import type { EscalationConfig, EscalationNotifyTarget } from './types';
+
+interface AlertEscalationSectionProps {
+ escalation: EscalationConfig;
+ setEscalationEnabled: (value: boolean) => void;
+ setEscalationAfter: (index: number, value: string) => void;
+ setEscalationNotify: (index: number, value: EscalationNotifyTarget) => void;
+ removeEscalationLevel: (index: number) => void;
+ addEscalationLevel: () => void;
+}
+
+export function AlertEscalationSection(props: AlertEscalationSectionProps) {
+ return (
+ props.setEscalationEnabled(event.currentTarget.checked)}
+ containerClass="sm:self-start"
+ label={
+
+ {getAlertConfigToggleStatusLabel(props.escalation.enabled)}
+
+ }
+ />
+ }
+ class="space-y-4"
+ >
+
+
+
{getAlertConfigEscalationHelp()}
+
+ {(level, index) => (
+
+
+
+
+ {ALERT_CONFIG_ESCALATION_AFTER_LABEL}
+
+ props.setEscalationAfter(index(), event.currentTarget.value)}
+ class={`${controlClass('px-2 py-1 text-sm')} w-20`}
+ />
+
+ {ALERT_CONFIG_ESCALATION_MINUTES_SUFFIX}
+
+
+
+
+ {ALERT_CONFIG_ESCALATION_NOTIFY_LABEL}
+
+
+
+
+
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/frontend-modern/src/features/alerts/AlertGroupingSection.tsx b/frontend-modern/src/features/alerts/AlertGroupingSection.tsx
new file mode 100644
index 000000000..0aef0697e
--- /dev/null
+++ b/frontend-modern/src/features/alerts/AlertGroupingSection.tsx
@@ -0,0 +1,136 @@
+import { Show } from 'solid-js';
+
+import {
+ formHelpText,
+ formField,
+ labelClass,
+} from '@/components/shared/Form';
+import { SettingsPanel } from '@/components/shared/SettingsPanel';
+import { Toggle } from '@/components/shared/Toggle';
+import {
+ ALERT_CONFIG_GROUPING_BY_GUEST,
+ ALERT_CONFIG_GROUPING_BY_NODE,
+ ALERT_CONFIG_GROUPING_DESCRIPTION,
+ ALERT_CONFIG_GROUPING_STRATEGY_LABEL,
+ ALERT_CONFIG_GROUPING_TITLE,
+ ALERT_CONFIG_GROUPING_WINDOW_HELP,
+ ALERT_CONFIG_GROUPING_WINDOW_LABEL,
+ getAlertConfigToggleStatusLabel,
+} from '@/utils/alertConfigPresentation';
+import {
+ getAlertGroupingCardClass,
+ getAlertGroupingCheckboxClass,
+} from '@/utils/alertGroupingPresentation';
+
+import type { GroupingConfig } from './types';
+
+interface AlertGroupingSectionProps {
+ grouping: GroupingConfig;
+ setGroupingEnabled: (value: boolean) => void;
+ setGroupingWindow: (value: string) => void;
+ setGroupingByNode: (value: boolean) => void;
+ setGroupingByGuest: (value: boolean) => void;
+}
+
+export function AlertGroupingSection(props: AlertGroupingSectionProps) {
+ return (
+ props.setGroupingEnabled(event.currentTarget.checked)}
+ containerClass="sm:self-start"
+ label={
+
+ {getAlertConfigToggleStatusLabel(props.grouping.enabled)}
+
+ }
+ />
+ }
+ class="space-y-4"
+ >
+
+
+
+
+
+
+ {ALERT_CONFIG_GROUPING_STRATEGY_LABEL}
+
+
+
+
+
+
+ );
+}
diff --git a/frontend-modern/src/features/alerts/AlertQuietHoursSection.tsx b/frontend-modern/src/features/alerts/AlertQuietHoursSection.tsx
new file mode 100644
index 000000000..3930ae4c2
--- /dev/null
+++ b/frontend-modern/src/features/alerts/AlertQuietHoursSection.tsx
@@ -0,0 +1,182 @@
+import { For, Show } from 'solid-js';
+
+import { controlClass, formField, labelClass } from '@/components/shared/Form';
+import { SettingsPanel } from '@/components/shared/SettingsPanel';
+import { Toggle } from '@/components/shared/Toggle';
+import {
+ ALERT_CONFIG_QUIET_HOURS_DESCRIPTION,
+ ALERT_CONFIG_QUIET_HOURS_END_TIME_LABEL,
+ ALERT_CONFIG_QUIET_HOURS_START_TIME_LABEL,
+ ALERT_CONFIG_QUIET_HOURS_TIMEZONE_LABEL,
+ ALERT_CONFIG_QUIET_HOURS_TITLE,
+} from '@/utils/alertConfigPresentation';
+import {
+ getAlertQuietDayButtonClass,
+ getAlertQuietSuppressCardClass,
+ getAlertQuietSuppressCheckboxClass,
+} from '@/utils/alertSchedulePresentation';
+
+import { ALERT_SCHEDULE_DAYS, ALERT_SCHEDULE_TIMEZONES } from './useAlertScheduleState';
+import type { QuietHoursConfig } from './types';
+
+interface QuietSuppressOption {
+ key: keyof QuietHoursConfig['suppress'];
+ label: string;
+ description: string;
+}
+
+interface AlertQuietHoursSectionProps {
+ quietHours: QuietHoursConfig;
+ quietHourSuppressOptions: QuietSuppressOption[];
+ weekdaysOnly: boolean;
+ weekendsOnly: boolean;
+ setQuietHoursEnabled: (value: boolean) => void;
+ setQuietHoursStart: (value: string) => void;
+ setQuietHoursEnd: (value: string) => void;
+ setQuietHoursTimezone: (value: string) => void;
+ toggleQuietDay: (day: keyof QuietHoursConfig['days']) => void;
+ setQuietSuppressCategory: (category: keyof QuietHoursConfig['suppress'], value: boolean) => void;
+}
+
+export function AlertQuietHoursSection(props: AlertQuietHoursSectionProps) {
+ return (
+ props.setQuietHoursEnabled(event.currentTarget.checked)}
+ containerClass="sm:self-start"
+ label={
+
+ {props.quietHours.enabled ? 'Enabled' : 'Disabled'}
+
+ }
+ />
+ }
+ class="space-y-4"
+ >
+
+
+
+
+
+ props.setQuietHoursStart(event.currentTarget.value)}
+ class={controlClass('font-mono')}
+ />
+
+
+
+ props.setQuietHoursEnd(event.currentTarget.value)}
+ class={controlClass('font-mono')}
+ />
+
+
+
+
+
+
+
+
+
+ Quiet days
+
+
+
+ {(day) => (
+
+ )}
+
+
+
+ Weekdays only
+ Weekends only
+
+
+
+
+
+ Suppress categories
+
+
+ Critical alerts in selected categories will stay silent during quiet hours.
+
+
+
+ {(option) => (
+
+ )}
+
+
+
+
+
+
+ );
+}
diff --git a/frontend-modern/src/features/alerts/AlertRecoverySection.tsx b/frontend-modern/src/features/alerts/AlertRecoverySection.tsx
new file mode 100644
index 000000000..deac9fa3c
--- /dev/null
+++ b/frontend-modern/src/features/alerts/AlertRecoverySection.tsx
@@ -0,0 +1,38 @@
+import { SettingsPanel } from '@/components/shared/SettingsPanel';
+import { Toggle } from '@/components/shared/Toggle';
+import { formHelpText } from '@/components/shared/Form';
+import {
+ ALERT_CONFIG_RECOVERY_DESCRIPTION,
+ ALERT_CONFIG_RECOVERY_TITLE,
+ getAlertConfigRecoveryHelp,
+ getAlertConfigToggleStatusLabel,
+} from '@/utils/alertConfigPresentation';
+
+interface AlertRecoverySectionProps {
+ notifyOnResolve: boolean;
+ setNotifyOnResolveEnabled: (value: boolean) => void;
+}
+
+export function AlertRecoverySection(props: AlertRecoverySectionProps) {
+ return (
+ props.setNotifyOnResolveEnabled(event.currentTarget.checked)}
+ containerClass="sm:self-start"
+ label={
+
+ {getAlertConfigToggleStatusLabel(props.notifyOnResolve)}
+
+ }
+ />
+ }
+ class="space-y-3"
+ >
+ {getAlertConfigRecoveryHelp()}
+
+ );
+}
diff --git a/frontend-modern/src/features/alerts/AlertScheduleSummarySection.tsx b/frontend-modern/src/features/alerts/AlertScheduleSummarySection.tsx
new file mode 100644
index 000000000..68f59fc74
--- /dev/null
+++ b/frontend-modern/src/features/alerts/AlertScheduleSummarySection.tsx
@@ -0,0 +1,103 @@
+import { Show } from 'solid-js';
+
+import { SettingsPanel } from '@/components/shared/SettingsPanel';
+import {
+ ALERT_CONFIG_SUMMARY_DESCRIPTION,
+ ALERT_CONFIG_SUMMARY_TITLE,
+ getAlertConfigSummaryAllDisabled,
+ getAlertConfigSummaryCooldown,
+ getAlertConfigSummaryEscalation,
+ getAlertConfigSummaryGrouping,
+ getAlertConfigSummaryQuietHours,
+ getAlertConfigSummaryRecoveryEnabled,
+ getAlertConfigSummarySuppressing,
+} from '@/utils/alertConfigPresentation';
+
+import type {
+ CooldownConfig,
+ EscalationConfig,
+ GroupingConfig,
+ QuietHoursConfig,
+} from './types';
+
+interface QuietSuppressOption {
+ key: keyof QuietHoursConfig['suppress'];
+ label: string;
+ description: string;
+}
+
+interface AlertScheduleSummarySectionProps {
+ quietHours: QuietHoursConfig;
+ cooldown: CooldownConfig;
+ grouping: GroupingConfig;
+ notifyOnResolve: boolean;
+ escalation: EscalationConfig;
+ quietHourSuppressOptions: QuietSuppressOption[];
+}
+
+export function AlertScheduleSummarySection(props: AlertScheduleSummarySectionProps) {
+ return (
+
+
+
+ {getAlertConfigSummaryQuietHours(
+ props.quietHours.start,
+ props.quietHours.end,
+ props.quietHours.timezone,
+ )}
+
+
+
+
+ {getAlertConfigSummarySuppressing(
+ props.quietHourSuppressOptions
+ .filter((option) => props.quietHours.suppress[option.key])
+ .map((option) => option.label),
+ )}
+
+
+
+ {getAlertConfigSummaryCooldown(props.cooldown.minutes, props.cooldown.maxAlerts)}
+
+
+
+ {getAlertConfigSummaryGrouping(
+ props.grouping.window,
+ props.grouping.byNode ?? false,
+ props.grouping.byGuest ?? false,
+ )}
+
+
+
+ {getAlertConfigSummaryRecoveryEnabled()}
+
+ 0}>
+ {getAlertConfigSummaryEscalation(props.escalation.levels.length)}
+
+
+ {getAlertConfigSummaryAllDisabled()}
+
+
+ );
+}
diff --git a/frontend-modern/src/features/alerts/tabs/ScheduleTab.tsx b/frontend-modern/src/features/alerts/tabs/ScheduleTab.tsx
index 4c6a2c194..a041da929 100644
--- a/frontend-modern/src/features/alerts/tabs/ScheduleTab.tsx
+++ b/frontend-modern/src/features/alerts/tabs/ScheduleTab.tsx
@@ -1,84 +1,18 @@
-import { For, Show } from 'solid-js';
-
import {
- controlClass,
- formHelpText,
- formField,
- labelClass,
-} from '@/components/shared/Form';
-import { SettingsPanel } from '@/components/shared/SettingsPanel';
-import { Toggle } from '@/components/shared/Toggle';
-import {
- ALERT_CONFIG_COOLDOWN_DESCRIPTION,
- ALERT_CONFIG_COOLDOWN_MAX_ALERTS_HELP,
- ALERT_CONFIG_COOLDOWN_MAX_ALERTS_LABEL,
- ALERT_CONFIG_COOLDOWN_MAX_ALERTS_SUFFIX,
- ALERT_CONFIG_COOLDOWN_PERIOD_HELP,
- ALERT_CONFIG_COOLDOWN_PERIOD_LABEL,
- ALERT_CONFIG_COOLDOWN_PERIOD_SUFFIX,
- ALERT_CONFIG_COOLDOWN_TITLE,
- ALERT_CONFIG_ESCALATION_ADD_LABEL,
- ALERT_CONFIG_ESCALATION_AFTER_LABEL,
- ALERT_CONFIG_ESCALATION_DESCRIPTION,
- ALERT_CONFIG_ESCALATION_MINUTES_SUFFIX,
- ALERT_CONFIG_ESCALATION_NOTIFY_LABEL,
- ALERT_CONFIG_ESCALATION_REMOVE_TITLE,
- ALERT_CONFIG_ESCALATION_TITLE,
- ALERT_CONFIG_GROUPING_BY_GUEST,
- ALERT_CONFIG_GROUPING_BY_NODE,
- ALERT_CONFIG_GROUPING_DESCRIPTION,
- ALERT_CONFIG_GROUPING_STRATEGY_LABEL,
- ALERT_CONFIG_GROUPING_TITLE,
- ALERT_CONFIG_GROUPING_WINDOW_HELP,
- ALERT_CONFIG_GROUPING_WINDOW_LABEL,
- ALERT_CONFIG_QUIET_HOURS_DESCRIPTION,
- ALERT_CONFIG_QUIET_HOURS_END_TIME_LABEL,
- ALERT_CONFIG_QUIET_HOURS_START_TIME_LABEL,
- ALERT_CONFIG_QUIET_HOURS_TIMEZONE_LABEL,
- ALERT_CONFIG_QUIET_HOURS_TITLE,
- ALERT_CONFIG_RECOVERY_DESCRIPTION,
- ALERT_CONFIG_RECOVERY_TITLE,
ALERT_CONFIG_SCHEDULING_DESCRIPTION,
ALERT_CONFIG_SCHEDULING_TITLE,
- ALERT_CONFIG_SUMMARY_DESCRIPTION,
- ALERT_CONFIG_SUMMARY_TITLE,
- getAlertConfigEscalationHelp,
- getAlertConfigEscalationNotifyLabel,
getAlertConfigQuietHourSuppressOptions,
- getAlertConfigRecoveryHelp,
getAlertConfigResetDefaultsLabel,
getAlertConfigResetDefaultsTitle,
- getAlertConfigSummaryAllDisabled,
- getAlertConfigSummaryCooldown,
- getAlertConfigSummaryEscalation,
- getAlertConfigSummaryGrouping,
- getAlertConfigSummaryQuietHours,
- getAlertConfigSummaryRecoveryEnabled,
- getAlertConfigSummarySuppressing,
- getAlertConfigToggleStatusLabel,
} from '@/utils/alertConfigPresentation';
-import {
- getAlertGroupingCardClass,
- getAlertGroupingCheckboxClass,
-} from '@/utils/alertGroupingPresentation';
-import {
- getAlertQuietDayButtonClass,
- getAlertQuietSuppressCardClass,
- getAlertQuietSuppressCheckboxClass,
-} from '@/utils/alertSchedulePresentation';
-
-import {
- ALERT_SCHEDULE_DAYS,
- ALERT_SCHEDULE_TIMEZONES,
- useAlertScheduleState,
-} from '../useAlertScheduleState';
-import type {
- CooldownConfig,
- EscalationConfig,
- EscalationNotifyTarget,
- GroupingConfig,
- QuietHoursConfig,
-} from '../types';
+import { AlertCooldownSection } from '../AlertCooldownSection';
+import { AlertEscalationSection } from '../AlertEscalationSection';
+import { AlertGroupingSection } from '../AlertGroupingSection';
+import { AlertQuietHoursSection } from '../AlertQuietHoursSection';
+import { AlertRecoverySection } from '../AlertRecoverySection';
+import { AlertScheduleSummarySection } from '../AlertScheduleSummarySection';
+import { useAlertScheduleState } from '../useAlertScheduleState';
+import type { CooldownConfig, EscalationConfig, GroupingConfig, QuietHoursConfig } from '../types';
export interface ScheduleTabProps {
setHasUnsavedChanges: (value: boolean) => void;
@@ -131,541 +65,56 @@ export function ScheduleTab(props: ScheduleTabProps) {
-
{
- scheduleState.setQuietHoursEnabled(event.currentTarget.checked);
- }}
- containerClass="sm:self-start"
- label={
-
- {props.quietHours().enabled ? 'Enabled' : 'Disabled'}
-
- }
- />
- }
- class="space-y-4"
- >
-
-
-
-
-
- {
- scheduleState.setQuietHoursStart(event.currentTarget.value);
- }}
- class={controlClass('font-mono')}
- />
-
-
-
- {
- scheduleState.setQuietHoursEnd(event.currentTarget.value);
- }}
- class={controlClass('font-mono')}
- />
-
-
-
-
-
-
+
-
-
- Quiet days
-
-
-
- {(day) => (
-
- )}
-
-
-
-
- Weekdays only
-
-
- Weekends only
-
-
-
+
-
-
- Suppress categories
-
-
- Critical alerts in selected categories will stay silent during quiet hours.
-
-
-
- {(option) => (
-
- )}
-
-
-
-
-
-
+
-
{
- scheduleState.setCooldownEnabled(event.currentTarget.checked);
- }}
- containerClass="sm:self-start"
- label={
-
- {getAlertConfigToggleStatusLabel(props.cooldown().enabled)}
-
- }
- />
- }
- class="space-y-4"
- >
-
-
-
-
+
-
{
- scheduleState.setGroupingEnabled(event.currentTarget.checked);
- }}
- containerClass="sm:self-start"
- label={
-
- {getAlertConfigToggleStatusLabel(props.grouping().enabled)}
-
- }
- />
- }
- class="space-y-4"
- >
-
-
-
-
-
-
- {ALERT_CONFIG_GROUPING_STRATEGY_LABEL}
-
-
-
-
-
-
-
-
{
- scheduleState.setNotifyOnResolveEnabled(event.currentTarget.checked);
- }}
- containerClass="sm:self-start"
- label={
-
- {getAlertConfigToggleStatusLabel(props.notifyOnResolve())}
-
- }
- />
- }
- class="space-y-3"
- >
- {getAlertConfigRecoveryHelp()}
-
-
-
{
- scheduleState.setEscalationEnabled(event.currentTarget.checked);
- }}
- containerClass="sm:self-start"
- label={
-
- {getAlertConfigToggleStatusLabel(props.escalation().enabled)}
-
- }
- />
- }
- class="space-y-4"
- >
-
-
-
{getAlertConfigEscalationHelp()}
-
- {(level, index) => (
-
-
-
-
- {ALERT_CONFIG_ESCALATION_AFTER_LABEL}
-
- {
- scheduleState.setEscalationAfter(
- index(),
- event.currentTarget.value,
- );
- }}
- class={`${controlClass('px-2 py-1 text-sm')} w-20`}
- />
-
- {ALERT_CONFIG_ESCALATION_MINUTES_SUFFIX}
-
-
-
-
- {ALERT_CONFIG_ESCALATION_NOTIFY_LABEL}
-
-
-
-
-
-
- )}
-
-
-
-
-
-
-
-
-
-
- {getAlertConfigSummaryQuietHours(
- props.quietHours().start,
- props.quietHours().end,
- props.quietHours().timezone,
- )}
-
-
-
-
- {getAlertConfigSummarySuppressing(
- quietHourSuppressOptions
- .filter((option) => props.quietHours().suppress[option.key])
- .map((option) => option.label),
- )}
-
-
-
-
- {getAlertConfigSummaryCooldown(
- props.cooldown().minutes,
- props.cooldown().maxAlerts,
- )}
-
-
-
-
- {getAlertConfigSummaryGrouping(
- props.grouping().window,
- props.grouping().byNode ?? false,
- props.grouping().byGuest ?? false,
- )}
-
-
-
- {getAlertConfigSummaryRecoveryEnabled()}
-
- 0}>
- {getAlertConfigSummaryEscalation(props.escalation().levels.length)}
-
-
- {getAlertConfigSummaryAllDisabled()}
-
-
+
);
diff --git a/frontend-modern/src/pages/__tests__/Alerts.helpers.test.ts b/frontend-modern/src/pages/__tests__/Alerts.helpers.test.ts
index a8ec98337..af7306123 100644
--- a/frontend-modern/src/pages/__tests__/Alerts.helpers.test.ts
+++ b/frontend-modern/src/pages/__tests__/Alerts.helpers.test.ts
@@ -309,6 +309,15 @@ describe('tab path helpers', () => {
expect(alertScheduleStateSource).toContain('createDefaultGrouping');
expect(alertScheduleStateSource).toContain('createDefaultEscalation');
expect(alertScheduleTabSource).toContain('getAlertConfigQuietHourSuppressOptions');
+ expect(alertScheduleTabSource).toContain('AlertQuietHoursSection');
+ expect(alertScheduleTabSource).toContain('AlertCooldownSection');
+ expect(alertScheduleTabSource).toContain('AlertGroupingSection');
+ expect(alertScheduleTabSource).toContain('AlertRecoverySection');
+ expect(alertScheduleTabSource).toContain('AlertEscalationSection');
+ expect(alertScheduleTabSource).toContain('AlertScheduleSummarySection');
+ expect(alertScheduleTabSource).not.toContain('ALERT_CONFIG_COOLDOWN_TITLE');
+ expect(alertScheduleTabSource).not.toContain('ALERT_CONFIG_QUIET_HOURS_TITLE');
+ expect(alertScheduleTabSource).not.toContain('ALERT_CONFIG_ESCALATION_TITLE');
expect(alertThresholdsTabSource).toContain('ThresholdsTable');
expect(thresholdsTableSource).toContain(
"import { useThresholdsTableState } from '@/features/alerts/thresholds/hooks/useThresholdsTableState';",
diff --git a/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts b/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts
index 9fb00267b..8f29172ff 100644
--- a/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts
+++ b/frontend-modern/src/utils/__tests__/frontendResourceTypeBoundaries.test.ts
@@ -2938,11 +2938,6 @@ describe('frontend resource type boundaries', () => {
'export function useThresholdsOverrideMutations',
);
expect(thresholdsOverrideMutationsHookSource).toContain('matchesAlertIdentifier');
- expect(alertScheduleTabSource).toContain('getAlertGroupingCardClass');
- expect(alertScheduleTabSource).toContain('getAlertGroupingCheckboxClass');
- expect(alertScheduleTabSource).toContain('getAlertQuietDayButtonClass');
- expect(alertScheduleTabSource).toContain('getAlertQuietSuppressCardClass');
- expect(alertScheduleTabSource).toContain('getAlertQuietSuppressCheckboxClass');
expect(alertsPageSource).not.toContain('getAlertConfigUnsavedChangesLabel');
expect(alertsConfigurationSurfaceSource).toContain('getAlertConfigUnsavedChangesLabel');
expect(alertsConfigurationSurfaceSource).toContain('getAlertConfigSaveChangesLabel');
@@ -2952,18 +2947,17 @@ describe('frontend resource type boundaries', () => {
expect(alertsPageSource).toContain('getAlertConfigLeaveConfirmation');
expect(alertScheduleTabSource).toContain('getAlertConfigResetDefaultsLabel');
expect(alertScheduleTabSource).toContain('getAlertConfigResetDefaultsTitle');
- expect(alertScheduleTabSource).toContain('getAlertConfigToggleStatusLabel');
- expect(alertScheduleTabSource).toContain('getAlertConfigSummaryQuietHours');
- expect(alertScheduleTabSource).toContain('getAlertConfigSummarySuppressing');
- expect(alertScheduleTabSource).toContain('getAlertConfigSummaryCooldown');
- expect(alertScheduleTabSource).toContain('getAlertConfigSummaryGrouping');
- expect(alertScheduleTabSource).toContain('getAlertConfigSummaryRecoveryEnabled');
- expect(alertScheduleTabSource).toContain('getAlertConfigSummaryEscalation');
expect(alertScheduleTabSource).toContain('getAlertConfigQuietHourSuppressOptions');
- expect(alertScheduleTabSource).toContain('ALERT_CONFIG_COOLDOWN_PERIOD_LABEL');
- expect(alertScheduleTabSource).toContain('ALERT_CONFIG_COOLDOWN_MAX_ALERTS_LABEL');
- expect(alertScheduleTabSource).toContain('ALERT_CONFIG_GROUPING_WINDOW_LABEL');
- expect(alertScheduleTabSource).toContain('ALERT_CONFIG_GROUPING_STRATEGY_LABEL');
+ expect(alertScheduleTabSource).toContain('AlertQuietHoursSection');
+ expect(alertScheduleTabSource).toContain('AlertCooldownSection');
+ expect(alertScheduleTabSource).toContain('AlertGroupingSection');
+ expect(alertScheduleTabSource).toContain('AlertRecoverySection');
+ expect(alertScheduleTabSource).toContain('AlertEscalationSection');
+ expect(alertScheduleTabSource).toContain('AlertScheduleSummarySection');
+ expect(alertScheduleTabSource).not.toContain('ALERT_CONFIG_COOLDOWN_PERIOD_LABEL');
+ expect(alertScheduleTabSource).not.toContain('ALERT_CONFIG_COOLDOWN_MAX_ALERTS_LABEL');
+ expect(alertScheduleTabSource).not.toContain('ALERT_CONFIG_GROUPING_WINDOW_LABEL');
+ expect(alertScheduleTabSource).not.toContain('ALERT_CONFIG_GROUPING_STRATEGY_LABEL');
expect(alertsPageSource).not.toContain('const statusClasses =');
expect(alertsPageSource).not.toContain('const levelClasses =');
expect(alertsPageSource).not.toContain("alert.source === 'ai' ? 'Patrol' : 'Alert'");
@@ -3896,8 +3890,8 @@ describe('frontend resource type boundaries', () => {
expect(alertDestinationsTabSource).toContain('AlertEmailDestinationsSection');
expect(alertDestinationsTabSource).toContain('AlertWebhookDestinationsSection');
expect(alertScheduleTabSource).toContain('getAlertConfigQuietHourSuppressOptions');
- expect(alertScheduleTabSource).toContain('getAlertGroupingCardClass');
- expect(alertScheduleTabSource).toContain('getAlertQuietDayButtonClass');
+ expect(alertScheduleTabSource).toContain('AlertGroupingSection');
+ expect(alertScheduleTabSource).toContain('AlertQuietHoursSection');
});
it('keeps alert resource table vocabulary in a shared presentation utility', () => {