ui(alerts): ReviewStep blocks save on empty webhooks+targets
Shows a warning banner and disables the Save button when a rule has neither webhooks nor targets — would have been rejected at the server edge (Task 3.3 validator), now caught earlier in the wizard with clear reason.
This commit is contained in:
@@ -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 (
|
||||
<div style={{ display: 'grid', gap: 12, maxWidth: 720 }}>
|
||||
{saveBlockReason && (
|
||||
<Alert variant="error" title="Rule cannot be saved yet">
|
||||
{saveBlockReason}
|
||||
</Alert>
|
||||
)}
|
||||
<div>
|
||||
<strong>Name:</strong> {form.name}
|
||||
</div>
|
||||
|
||||
@@ -19,7 +19,7 @@ import { ScopeStep } from './ScopeStep';
|
||||
import { ConditionStep } from './ConditionStep';
|
||||
import { TriggerStep } from './TriggerStep';
|
||||
import { NotifyStep } from './NotifyStep';
|
||||
import { ReviewStep } from './ReviewStep';
|
||||
import { ReviewStep, computeSaveBlockReason } from './ReviewStep';
|
||||
import { prefillFromPromotion, type PrefillWarning } from './promotion-prefill';
|
||||
import { useCatalog } from '../../../api/queries/catalog';
|
||||
import { useOutboundConnections } from '../../../api/queries/admin/outboundConnections';
|
||||
@@ -194,7 +194,11 @@ export default function RuleEditorWizard() {
|
||||
Next
|
||||
</Button>
|
||||
) : (
|
||||
<Button variant="primary" onClick={onSave} disabled={create.isPending || update.isPending}>
|
||||
<Button
|
||||
variant="primary"
|
||||
onClick={onSave}
|
||||
disabled={create.isPending || update.isPending || computeSaveBlockReason(form) !== null}
|
||||
>
|
||||
{isEdit ? 'Save changes' : 'Create rule'}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user