feat(alerting): Plan 03 — UI + backfills (SSRF guard, metrics caching, docker stack) #144

Merged
hsiegeln merged 39 commits from feat/alerting-03-ui into main 2026-04-20 16:27:49 +02:00
Showing only changes of commit d42a6ca6a8 - Show all commits

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>
);
}