feat(ui/alerts): TriggerStep (evaluation interval, for-duration, re-notify, test-evaluate)

Three numeric inputs for evaluation cadence, for-duration, and
re-notification window, plus a Test evaluate button for saved rules.
TestEvaluateRequest is empty on the wire (server uses the rule id), so
we send {} and rely on the backend to evaluate the current saved state.

Deviation: plan draft passed {condition: toRequest(form).condition} into
the request body. The generated TestEvaluateRequest type is
Record<string, never>, so we send an empty body.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-20 14:00:33 +02:00
parent ef8c60c2b5
commit d42a6ca6a8

View File

@@ -1,13 +1,85 @@
import { useState } from 'react';
import { Button, FormField, Input, useToast } from '@cameleer/design-system';
import { useTestEvaluate } from '../../../api/queries/alertRules';
import type { FormState } from './form-state';
export function TriggerStep({
form: _form,
setForm: _setForm,
ruleId: _ruleId,
form,
setForm,
ruleId,
}: {
form: FormState;
setForm: (f: FormState) => void;
ruleId?: string;
}) {
return <div>Trigger step &mdash; TODO Task 22</div>;
const testEvaluate = useTestEvaluate();
const { toast } = useToast();
const [lastResult, setLastResult] = useState<string | null>(null);
const onTest = async () => {
if (!ruleId) {
toast({ title: 'Save rule first to run test evaluate', variant: 'error' });
return;
}
try {
const result = await testEvaluate.mutateAsync({ id: ruleId, req: {} });
setLastResult(JSON.stringify(result, null, 2));
} catch (e) {
toast({ title: 'Test-evaluate failed', description: String(e), variant: 'error' });
}
};
return (
<div style={{ display: 'grid', gap: 12, maxWidth: 600 }}>
<FormField label="Evaluation interval (seconds, min 5)">
<Input
type="number"
min={5}
value={form.evaluationIntervalSeconds}
onChange={(e) => setForm({ ...form, evaluationIntervalSeconds: Number(e.target.value) })}
/>
</FormField>
<FormField label="For-duration before firing (seconds, 0 = fire immediately)">
<Input
type="number"
min={0}
value={form.forDurationSeconds}
onChange={(e) => setForm({ ...form, forDurationSeconds: Number(e.target.value) })}
/>
</FormField>
<FormField label="Re-notify cadence (minutes, 0 = notify once)">
<Input
type="number"
min={0}
value={form.reNotifyMinutes}
onChange={(e) => setForm({ ...form, reNotifyMinutes: Number(e.target.value) })}
/>
</FormField>
<div>
<Button variant="secondary" onClick={onTest} disabled={testEvaluate.isPending}>
Test evaluate (uses saved rule)
</Button>
{!ruleId && (
<p style={{ marginTop: 8, fontSize: 12, color: 'var(--muted)' }}>
Save the rule first to enable test-evaluate.
</p>
)}
{lastResult && (
<pre
style={{
marginTop: 12,
padding: 8,
background: 'var(--code-bg)',
borderRadius: 6,
fontSize: 12,
maxHeight: 240,
overflow: 'auto',
}}
>
{lastResult}
</pre>
)}
</div>
</div>
);
}