diff --git a/frontend/src/features/alerting-rules/components/rule-drawer.tsx b/frontend/src/features/alerting-rules/components/rule-drawer.tsx index 409a9b942..4bc9d9635 100644 --- a/frontend/src/features/alerting-rules/components/rule-drawer.tsx +++ b/frontend/src/features/alerting-rules/components/rule-drawer.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useEffect, useState } from 'react' import type { TFunction } from 'i18next' import { Code2, Download, FlaskConical, LayoutList, Loader2, Lock, Pencil, Trash2, X } from 'lucide-react' import { toast } from 'sonner' @@ -43,28 +43,22 @@ export function RuleDrawer({ const [busy, setBusy] = useState(false) const [showTestModal, setShowTestModal] = useState(false) - // Visual ↔ Code. The structured form stays canonical; Code shows/edits the - // whole rule as YAML and syncs back into the form on toggle / save. + // Visual ↔ Code sync live: form is canonical while visual is active, YAML is + // canonical while code is active. Whichever pane is not being edited derives + // from the other on every keystroke, so switching is just `setMode`. const [mode, setMode] = useState<'visual' | 'code'>('visual') - const [yaml, setYaml] = useState('') + const [yaml, setYaml] = useState(() => ruleFormToYaml(form)) - const toCode = () => { - setYaml(ruleFormToYaml(form)) - setMode('code') - } - const toVisual = () => { - // Only sync YAML → form when actually editing; in view mode (incl. system - // rules) the YAML is read-only, so there's nothing to apply back. - if (showForm) { - const r = yamlToRuleForm(yaml) - if (!r.ok) { - toast.error(t('alertingRules.editor.yamlError', { error: r.error })) - return - } - // active isn't part of the YAML — keep the current value. - setForm({ ...r.form, ruleActive: form.ruleActive }) - } - setMode('visual') + useEffect(() => { + if (mode === 'visual') setYaml(ruleFormToYaml(form)) + }, [form, mode]) + + const onYamlChange = (next: string) => { + setYaml(next) + if (!showForm) return // read-only path + const r = yamlToRuleForm(next) + if (r.ok) setForm({ ...r.form, ruleActive: form.ruleActive }) + // Invalid YAML → keep the last-valid form; save-time will re-toast. } const cancelEdit = () => { @@ -127,7 +121,7 @@ export function RuleDrawer({