ui(alerts): ExchangeMatchForm — enforce PER_EXCHANGE UI constraints

Disable reNotifyMinutes at 0 with tooltip when PER_EXCHANGE is selected
(server rejects non-zero per Task 3.3 validator). Hide forDurationSeconds
entirely for PER_EXCHANGE (not applicable to per-exchange semantics).
Values stay zeroed via Task 4.3's applyFireModeChange helper on any
mode toggle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-22 17:51:58 +02:00
parent 9960fd8c36
commit 36cb93ecdd

View File

@@ -17,6 +17,13 @@ export function TriggerStep({
const { toast } = useToast(); const { toast } = useToast();
const [lastResult, setLastResult] = useState<string | null>(null); const [lastResult, setLastResult] = useState<string | null>(null);
// PER_EXCHANGE fires exactly once per exchange (Task 3.3 server validator
// rejects non-zero reNotifyMinutes / forDurationSeconds for this mode).
// Gate the two inputs here so users can't type values the server will reject.
const isPerExchange =
form.conditionKind === 'EXCHANGE_MATCH' &&
(form.condition as Record<string, unknown>).fireMode === 'PER_EXCHANGE';
const onTest = async () => { const onTest = async () => {
if (!ruleId) { if (!ruleId) {
toast({ title: 'Save rule first to run test evaluate', variant: 'error' }); toast({ title: 'Save rule first to run test evaluate', variant: 'error' });
@@ -40,6 +47,7 @@ export function TriggerStep({
onChange={(e) => setForm({ ...form, evaluationIntervalSeconds: Number(e.target.value) })} onChange={(e) => setForm({ ...form, evaluationIntervalSeconds: Number(e.target.value) })}
/> />
</FormField> </FormField>
{!isPerExchange && (
<FormField label="For-duration before firing (seconds, 0 = fire immediately)"> <FormField label="For-duration before firing (seconds, 0 = fire immediately)">
<Input <Input
type="number" type="number"
@@ -48,12 +56,19 @@ export function TriggerStep({
onChange={(e) => setForm({ ...form, forDurationSeconds: Number(e.target.value) })} onChange={(e) => setForm({ ...form, forDurationSeconds: Number(e.target.value) })}
/> />
</FormField> </FormField>
)}
<FormField label="Re-notify cadence (minutes, 0 = notify once)"> <FormField label="Re-notify cadence (minutes, 0 = notify once)">
<Input <Input
type="number" type="number"
min={0} min={0}
value={form.reNotifyMinutes} value={form.reNotifyMinutes}
onChange={(e) => setForm({ ...form, reNotifyMinutes: Number(e.target.value) })} onChange={(e) => setForm({ ...form, reNotifyMinutes: Number(e.target.value) })}
disabled={isPerExchange}
title={
isPerExchange
? 'Per-exchange rules fire exactly once per exchange — re-notify does not apply.'
: undefined
}
/> />
</FormField> </FormField>
<div> <div>