diff --git a/ui/src/pages/Alerts/RuleEditor/ReviewStep.tsx b/ui/src/pages/Alerts/RuleEditor/ReviewStep.tsx index bda16fd5..d2febd96 100644 --- a/ui/src/pages/Alerts/RuleEditor/ReviewStep.tsx +++ b/ui/src/pages/Alerts/RuleEditor/ReviewStep.tsx @@ -1,6 +1,26 @@ -import { Toggle } from '@cameleer/design-system'; +import { useMemo } from 'react'; +import { Alert, Toggle } from '@cameleer/design-system'; import { toRequest, type FormState } from './form-state'; +/** + * Pure helper: returns a human-readable reason why saving should be blocked, + * or null when the rule is safe to save (from the wizard's perspective). + * + * Currently covers: a rule with no webhooks AND no targets would be rejected + * at the server edge (Task 3.3 validator) and would never notify anyone, so + * the wizard blocks it earlier with a clear reason. The helper is exported + * so `RuleEditorWizard` can also drive the Save button's `disabled` state + * off the same single source of truth. + */ +export function computeSaveBlockReason(form: FormState): string | null { + const noWebhooks = (form.webhooks ?? []).length === 0; + const noTargets = (form.targets ?? []).length === 0; + if (noWebhooks && noTargets) { + return 'Add at least one webhook or target \u2014 a rule with no recipients never notifies anyone.'; + } + return null; +} + export function ReviewStep({ form, setForm, @@ -9,8 +29,14 @@ export function ReviewStep({ setForm?: (f: FormState) => void; }) { const req = toRequest(form); + const saveBlockReason = useMemo(() => computeSaveBlockReason(form), [form]); return (