import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { Bell, Plus, Trash2, Check, AlertTriangle, Info, AlertCircle } from 'lucide-react' import { alertsApi, alertRulesApi } from '../api/client' import type { AlertSeverity, AlertRule, CreateAlertRuleRequest } from '../api/types' import { cn } from '../lib/utils' function getSeverityStyle(severity: AlertSeverity) { return { Critical: 'bg-red-500/20 text-red-400 border border-red-500/30', Warning: 'bg-yellow-500/20 text-yellow-400 border border-yellow-500/30', Info: 'bg-blue-500/20 text-blue-400 border border-blue-500/30', }[severity] } function getSeverityIcon(severity: AlertSeverity) { return { Critical: , Warning: , Info: , }[severity] } const metricPathOptions = [ { value: 'cpu_usage_percent', label: 'CPU-Auslastung (%)' }, { value: 'memory_usage_percent', label: 'RAM-Auslastung (%)' }, ] const operatorOptions = [ { value: '>', label: '>' }, { value: '>=', label: '>=' }, { value: '<', label: '<' }, { value: '<=', label: '<=' }, { value: '==', label: '==' }, ] const severityOptions = [ { value: 'Info' as AlertSeverity, label: 'Info' }, { value: 'Warning' as AlertSeverity, label: 'Warning' }, { value: 'Critical' as AlertSeverity, label: 'Critical' }, ] interface CreateRuleForm { name: string metricPath: string operator: string threshold: string severity: AlertSeverity } export default function AlertsPage() { const [activeTab, setActiveTab] = useState<'alerts' | 'rules'>('alerts') const [alertFilter, setAlertFilter] = useState<'all' | 'unacknowledged' | 'acknowledged'>('unacknowledged') const [showCreateRuleModal, setShowCreateRuleModal] = useState(false) const [formData, setFormData] = useState({ name: '', metricPath: 'cpu_usage_percent', operator: '>', threshold: '', severity: 'Warning', }) const queryClient = useQueryClient() // Queries const alertsQuery = useQuery({ queryKey: ['alerts'], queryFn: () => alertsApi.list(), }) const rulesQuery = useQuery({ queryKey: ['alert-rules'], queryFn: () => alertRulesApi.list(), }) // Mutations const acknowledgeMutation = useMutation({ mutationFn: (id: number) => alertsApi.acknowledge(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['alerts'] }) }, }) const createRuleMutation = useMutation({ mutationFn: (data: CreateAlertRuleRequest) => alertRulesApi.create(data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['alert-rules'] }) setShowCreateRuleModal(false) setFormData({ name: '', metricPath: 'cpu_usage_percent', operator: '>', threshold: '', severity: 'Warning', }) }, }) const updateRuleMutation = useMutation({ mutationFn: ({ id, enabled }: { id: number; enabled: boolean }) => alertRulesApi.update(id, { enabled }), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['alert-rules'] }) }, }) const deleteRuleMutation = useMutation({ mutationFn: (id: number) => alertRulesApi.delete(id), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['alert-rules'] }) }, }) // Filter alerts const filteredAlerts = alertsQuery.data?.filter((alert) => { if (alertFilter === 'acknowledged') return alert.acknowledged if (alertFilter === 'unacknowledged') return !alert.acknowledged return true }) || [] // Handlers const handleCreateRule = async () => { if (!formData.name.trim() || !formData.threshold) { alert('Bitte alle erforderlichen Felder ausfüllen') return } try { await createRuleMutation.mutateAsync({ name: formData.name, metricPath: formData.metricPath, operator: formData.operator, threshold: parseFloat(formData.threshold), severity: formData.severity, }) } catch (err) { console.error('Fehler beim Erstellen der Regel:', err) } } const handleDeleteRule = (id: number) => { if (window.confirm('Möchten Sie diese Regel wirklich löschen?')) { deleteRuleMutation.mutate(id) } } const handleToggleRuleEnabled = (rule: AlertRule) => { updateRuleMutation.mutate({ id: rule.id, enabled: !rule.enabled }) } return (
{/* Header */}

Alerts

{/* Tabs */}
{/* Tab 1: Aktive Alerts */} {activeTab === 'alerts' && (
{/* Filter */}
{/* Loading / Error */} {alertsQuery.isLoading &&

Lädt...

} {alertsQuery.isError && (

Fehler beim Laden der Alerts

)} {/* Empty State */} {alertsQuery.isSuccess && filteredAlerts.length === 0 && (

Keine aktiven Alerts

)} {/* Table */} {alertsQuery.isSuccess && filteredAlerts.length > 0 && (
{filteredAlerts.map((alert) => ( ))}
Erstellt Agent Regel Nachricht Schweregrad Aktionen
{new Date(alert.createdAt).toLocaleString('de-DE')}

{alert.agentHostname}

{alert.agentId}

{alert.ruleName} {alert.message}
{getSeverityIcon(alert.severity)} {alert.severity}
)}
)} {/* Tab 2: Regeln */} {activeTab === 'rules' && (
{/* Create Button */} {/* Loading / Error */} {rulesQuery.isLoading &&

Lädt...

} {rulesQuery.isError && (

Fehler beim Laden der Regeln

)} {/* Empty State */} {rulesQuery.isSuccess && rulesQuery.data.length === 0 && (

Keine Regeln vorhanden

)} {/* Table */} {rulesQuery.isSuccess && rulesQuery.data.length > 0 && (
{rulesQuery.data.map((rule) => { const metricLabel = metricPathOptions.find( (opt) => opt.value === rule.metricPath, )?.label || rule.metricPath return ( ) })}
Name Metrik Bedingung Schweregrad Aktiv Aktionen
{rule.name} {metricLabel} {metricLabel} {rule.operator} {rule.threshold}
{getSeverityIcon(rule.severity)} {rule.severity}
)}
)} {/* Create Rule Modal */} {showCreateRuleModal && (

Neue Regel erstellen

{/* Name */}
setFormData({ ...formData, name: e.target.value })} className="w-full px-3 py-2 bg-background border border-border rounded-md text-foreground focus:outline-none focus:ring-2 focus:ring-primary" placeholder="z.B. CPU über 90%" />
{/* MetricPath */}
{/* Operator */}
{/* Threshold */}
setFormData({ ...formData, threshold: e.target.value })} className="w-full px-3 py-2 bg-background border border-border rounded-md text-foreground focus:outline-none focus:ring-2 focus:ring-primary" placeholder="z.B. 90" />
{/* Severity */}
{/* Buttons */}
)}
) }