diff --git a/ui/src/pages/Alerts/RuleEditor/RuleEditorWizard.tsx b/ui/src/pages/Alerts/RuleEditor/RuleEditorWizard.tsx index 250f5bdc..d33f916b 100644 --- a/ui/src/pages/Alerts/RuleEditor/RuleEditorWizard.tsx +++ b/ui/src/pages/Alerts/RuleEditor/RuleEditorWizard.tsx @@ -20,7 +20,10 @@ import { ConditionStep } from './ConditionStep'; import { TriggerStep } from './TriggerStep'; import { NotifyStep } from './NotifyStep'; import { ReviewStep } from './ReviewStep'; -import { prefillFromPromotion } from './promotion-prefill'; +import { prefillFromPromotion, type PrefillWarning } from './promotion-prefill'; +import { useCatalog } from '../../../api/queries/catalog'; +import { useOutboundConnections } from '../../../api/queries/admin/outboundConnections'; +import { useSelectedEnv } from '../../../api/queries/alertMeta'; import css from './wizard.module.css'; const STEP_LABELS: Record = { @@ -47,8 +50,20 @@ export default function RuleEditorWizard() { const promoteRuleId = search.get('ruleId') ?? undefined; const sourceRuleQuery = useAlertRule(promoteFrom ? promoteRuleId : undefined); + // Target-env data for promotion warnings. + const env = useSelectedEnv(); + const targetEnv = search.get('targetEnv') ?? env; + const { data: targetCatalog } = useCatalog(targetEnv ?? undefined); + const { data: connections } = useOutboundConnections(); + + const targetAppSlugs = (targetCatalog ?? []).map((a) => a.slug); + const targetAllowedConnIds = (connections ?? []) + .filter((c) => c.allowedEnvironmentIds.length === 0 || (!!targetEnv && c.allowedEnvironmentIds.includes(targetEnv))) + .map((c) => c.id); + const [step, setStep] = useState('scope'); const [form, setForm] = useState(null); + const [warnings, setWarnings] = useState([]); // Initialize form once the existing or source rule loads. useEffect(() => { @@ -58,14 +73,29 @@ export default function RuleEditorWizard() { return; } if (promoteFrom && sourceRuleQuery.data) { - const { form: prefilled } = prefillFromPromotion(sourceRuleQuery.data); + const { form: prefilled, warnings: w } = prefillFromPromotion(sourceRuleQuery.data, { + targetEnvAppSlugs: targetAppSlugs, + targetEnvAllowedConnectionIds: targetAllowedConnIds, + }); setForm(prefilled); + setWarnings(w); return; } if (!isEdit && !promoteFrom) { setForm(initialForm()); } - }, [form, isEdit, existingQuery.data, promoteFrom, sourceRuleQuery.data]); + // Intentionally depend on join()'d slug/id strings so the effect + // doesn't retrigger on new array identities when contents are equal. + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ + form, + isEdit, + existingQuery.data, + promoteFrom, + sourceRuleQuery.data, + targetAppSlugs.join(','), + targetAllowedConnIds.join(','), + ]); const create = useCreateAlertRule(); const update = useUpdateAlertRule(id ?? ''); @@ -124,6 +154,18 @@ export default function RuleEditorWizard() { )} + {warnings.length > 0 && ( +
+ Review before saving: +
    + {warnings.map((w) => ( +
  • + {w.field}: {w.message} +
  • + ))} +
+
+ )}