import { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/dialog"; import { Button } from "@/components/button"; import { Input } from "@/components/input"; import { toast } from "sonner"; import { createAlertRule, updateAlertRule, type AlertRule, type NotificationChannel, } from "@/api/alerts-api"; const TRIGGER_TYPES = [ { value: "host_offline", label: "Host Offline", hasThreshold: false }, { value: "host_online", label: "Host Online", hasThreshold: false }, { value: "cpu_threshold", label: "CPU Usage Threshold", hasThreshold: true }, { value: "memory_threshold", label: "Memory Usage Threshold", hasThreshold: true, }, { value: "disk_threshold", label: "Disk Usage Threshold", hasThreshold: true, }, { value: "health_check_failure", label: "Health Check Failure", hasThreshold: false, }, { value: "user_login", label: "User SSH Login", hasThreshold: false }, ]; interface AlertRuleDialogProps { open: boolean; onOpenChange: (open: boolean) => void; rule: AlertRule | null; channels: NotificationChannel[]; onSaved: () => void; } export function AlertRuleDialog({ open, onOpenChange, rule, channels, onSaved, }: AlertRuleDialogProps) { const { t } = useTranslation(); const [name, setName] = useState(""); const [triggerType, setTriggerType] = useState("host_offline"); const [threshold, setThreshold] = useState("80"); const [durationSeconds, setDurationSeconds] = useState("0"); const [cooldownMinutes, setCooldownMinutes] = useState("15"); const [selectedChannels, setSelectedChannels] = useState([]); const [saving, setSaving] = useState(false); const triggerDef = TRIGGER_TYPES.find((tt) => tt.value === triggerType); const hasThreshold = triggerDef?.hasThreshold ?? false; useEffect(() => { if (!open) return; if (rule) { setName(rule.name); setTriggerType(rule.triggerType); setThreshold( rule.thresholdValue != null ? String(rule.thresholdValue) : "80", ); setDurationSeconds( rule.thresholdDurationSeconds != null ? String(rule.thresholdDurationSeconds) : "0", ); setCooldownMinutes(String(rule.cooldownMinutes)); setSelectedChannels(rule.channelIds ?? []); } else { setName(""); setTriggerType("host_offline"); setThreshold("80"); setDurationSeconds("0"); setCooldownMinutes("15"); setSelectedChannels([]); } }, [open, rule]); function toggleChannel(id: number) { setSelectedChannels((prev) => prev.includes(id) ? prev.filter((c) => c !== id) : [...prev, id], ); } async function handleSave() { if (!name.trim()) { toast.error(t("alerts.ruleNameRequired", "Name is required")); return; } setSaving(true); try { const payload = { name: name.trim(), triggerType, thresholdValue: hasThreshold ? Number(threshold) : null, thresholdDurationSeconds: hasThreshold && Number(durationSeconds) > 0 ? Number(durationSeconds) : null, cooldownMinutes: Number(cooldownMinutes) || 15, channels: selectedChannels, }; if (rule) { await updateAlertRule(rule.id, payload); } else { await createAlertRule(payload); } onSaved(); } catch { toast.error(t("alerts.ruleSaveFailed", "Failed to save rule")); } finally { setSaving(false); } } return ( {rule ? t("alerts.editRule", "Edit Alert Rule") : t("alerts.addRule", "Add Alert Rule")}
setName(e.target.value)} className="text-sm" />
{TRIGGER_TYPES.map((tt) => ( ))}
{hasThreshold && ( <>
setThreshold(e.target.value)} className="w-20 text-sm" /> %
setDurationSeconds(e.target.value)} className="w-24 text-sm" />
)}
setCooldownMinutes(e.target.value)} className="w-24 text-sm" />
{channels.length === 0 ? ( {t( "alerts.noChannelsHint", "Add channels in the Channels tab first", )} ) : (
{channels.map((ch) => ( ))}
)}
); }