diff --git a/ui/src/pages/Alerts/RuleEditor/condition-forms/ExchangeMatchForm.tsx b/ui/src/pages/Alerts/RuleEditor/condition-forms/ExchangeMatchForm.tsx index 2100a8b4..52fd4f29 100644 --- a/ui/src/pages/Alerts/RuleEditor/condition-forms/ExchangeMatchForm.tsx +++ b/ui/src/pages/Alerts/RuleEditor/condition-forms/ExchangeMatchForm.tsx @@ -1,6 +1,7 @@ import { FormField, Input, Select } from '@cameleer/design-system'; import type { FormState } from '../form-state'; -import { EXCHANGE_FIRE_MODE_OPTIONS } from '../../enums'; +import { applyFireModeChange } from '../form-state'; +import { EXCHANGE_FIRE_MODE_OPTIONS, type ExchangeFireMode } from '../../enums'; // ExchangeFilter.status is typed as `String` on the backend (no @Schema // allowableValues yet) so options stay hand-typed. Follow-up: annotate the @@ -23,7 +24,7 @@ export function ExchangeMatchForm({ form, setForm }: { form: FormState; setForm: . Guarantees state + * hygiene across toggles: + * + * - Switching to PER_EXCHANGE clears COUNT_IN_WINDOW-only condition fields + * (threshold, windowSeconds) AND forces top-level reNotifyMinutes = 0 + * and forDurationSeconds = 0 — PER_EXCHANGE is exactly-once-per-exchange, + * so re-notify cadence and hold-duration are meaningless and must not leak + * into toRequest(). + * - Switching back to COUNT_IN_WINDOW resets threshold/windowSeconds to 0 + * (never restoring stale values from the previous COUNT_IN_WINDOW session). + * + * No-op for non-EXCHANGE_MATCH conditions. Returns a new form object. + */ +export function applyFireModeChange(form: FormState, newMode: ExchangeFireMode): FormState { + const c = form.condition as Record; + if (c.kind !== 'EXCHANGE_MATCH') return form; + const base: FormState = { + ...form, + condition: { + ...c, + fireMode: newMode, + threshold: 0, + windowSeconds: 0, + } as FormState['condition'], + }; + if (newMode === 'PER_EXCHANGE') { + return { ...base, reNotifyMinutes: 0, forDurationSeconds: 0 }; + } + return base; +} + export function validateStep(step: WizardStep, f: FormState): string[] { const errs: string[] = []; if (step === 'scope') {