import { createAlertRule, updateAlertRule } from "@/api/alert-rule" import { Button } from "@/components/ui/button" import { Checkbox } from "@/components/ui/checkbox" import { Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog" import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form" import { Input } from "@/components/ui/input" import { Label } from "@/components/ui/label" import { ScrollArea } from "@/components/ui/scroll-area" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select" import { IconButton } from "@/components/xui/icon-button" import { useNotification } from "@/hooks/useNotfication" import { conv } from "@/lib/utils" import { ModelAlertRule } from "@/types" import { triggerModes } from "@/types" import { zodResolver } from "@hookform/resolvers/zod" import { useEffect, useState } from "react" import { useForm } from "react-hook-form" import { useTranslation } from "react-i18next" import { toast } from "sonner" import { KeyedMutator } from "swr" import { z } from "zod" import { Combobox } from "./ui/combobox" import { Textarea } from "./ui/textarea" interface AlertRuleCardProps { data?: ModelAlertRule mutate: KeyedMutator } const ruleSchema = z.object({ type: z.string(), min: z.number().optional(), max: z.number().optional(), cycle_start: z.string().optional(), cycle_interval: z.number().optional(), cycle_unit: z.enum(["hour", "day", "week", "month", "year"]).optional(), duration: z.number().optional(), cover: z.number().int().min(0), ignore: z.record(z.string(), z.boolean()).optional(), next_transfer_at: z.record(z.string(), z.string()).optional(), last_cycle_status: z.boolean().optional(), }) const alertRuleFormSchema = z.object({ name: z.string().min(1), rules_raw: z.string().refine( (val) => { try { JSON.parse(val) return true } catch (e) { return false } }, { message: "Invalid JSON string", }, ), rules: z.array(ruleSchema), fail_trigger_tasks: z.array(z.number()), fail_trigger_tasks_raw: z.string(), recover_trigger_tasks: z.array(z.number()), recover_trigger_tasks_raw: z.string(), notification_group_id: z.coerce.number().int(), trigger_mode: z.coerce.number().int().min(0), enable: z.boolean().optional(), }) export const AlertRuleCard: React.FC = ({ data, mutate }) => { const { t } = useTranslation() type AlertRuleFormData = z.infer const form = useForm({ resolver: zodResolver(alertRuleFormSchema) as any, defaultValues: data ? { ...data, rules_raw: JSON.stringify(data.rules), fail_trigger_tasks_raw: conv.arrToStr(data.fail_trigger_tasks), recover_trigger_tasks_raw: conv.arrToStr(data.recover_trigger_tasks), } : { name: "", rules_raw: "", rules: [], fail_trigger_tasks: [], fail_trigger_tasks_raw: "", recover_trigger_tasks: [], recover_trigger_tasks_raw: "", notification_group_id: 0, trigger_mode: 0, }, resetOptions: { keepDefaultValues: false, }, }) const [open, setOpen] = useState(false) // 结构化规则编辑状态:从已有数据或 rules_raw 初始化 const initialRules = (() => { try { if (data?.rules) return data.rules as any[] const raw = form.getValues("rules_raw") return raw ? JSON.parse(raw) : [] } catch { return [] } })() const [rulesUI, setRulesUI] = useState(initialRules) // 同步到 rules_raw(提交仍走 JSON 字符串) useEffect(() => { try { form.setValue("rules_raw", JSON.stringify(rulesUI), { shouldDirty: true }) } catch { // ignore } }, [rulesUI]) const onSubmit = async (values: AlertRuleFormData) => { values.rules = JSON.parse(values.rules_raw) values.fail_trigger_tasks = conv.strToArr(values.fail_trigger_tasks_raw).map(Number) values.recover_trigger_tasks = conv.strToArr(values.recover_trigger_tasks_raw).map(Number) const { rules_raw, ...requiredFields } = values try { data?.id ? await updateAlertRule(data.id, requiredFields) : await createAlertRule(requiredFields) } catch (e) { console.error(e) toast(t("Error"), { description: t("Results.UnExpectedError"), }) return } setOpen(false) await mutate() form.reset() } const { notifierGroup } = useNotification() const ngroupList = notifierGroup?.map((ng) => ({ value: `${ng.group.id}`, label: ng.group.name, })) || [{ value: "", label: "" }] return ( {data ? : }
{data ? t("EditAlertRule") : t("CreateAlertRule")}
( {t("Name")} )} /> {/* 结构化规则编辑器 */} {t("Rules")}
{rulesUI.map((r, idx) => { const isCycle = typeof r.type === "string" && r.type.endsWith("_cycle") const isOffline = r.type === "offline" return (
{/* 类型选择 */}
{ const next = [...rulesUI] next[idx] = { ...next[idx], duration: e.target.value ? Number(e.target.value) : undefined, } setRulesUI(next) }} placeholder="10" />
{/* 阈值:offline 不需要 min/max */} {!isOffline && (
{ const next = [...rulesUI] next[idx] = { ...next[idx], min: e.target.value ? Number( e.target .value, ) : undefined, } setRulesUI(next) }} placeholder="0" />
{ const next = [...rulesUI] next[idx] = { ...next[idx], max: e.target.value ? Number( e.target .value, ) : undefined, } setRulesUI(next) }} placeholder="100" />
)} {/* 覆盖/忽略 */}
{/* 简化:以 JSON 对象输入 */}