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:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user