Files
aiops/frontend/src/components/monitor/AlertManager.tsx
T

198 lines
7.1 KiB
TypeScript

import { useState, useEffect } from 'react';
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Plus, Settings, Bell } from 'lucide-react';
import {
ResponsiveContainer,
AreaChart,
Area,
XAxis,
YAxis,
CartesianGrid,
Tooltip,
} from 'recharts';
import { alertApi } from '@/api';
import { AlertEventsTab } from './AlertEventsTab';
import { AlertRulesTab } from './AlertRulesTab';
import { AlertRuleDialog } from './AlertRuleDialog';
import { AlertChannelDialog } from './AlertChannelDialog';
import { PageShell } from '@/components/layout/PageShell';
import { StatStrip } from '@/components/ui/stat-strip';
import type { JsonData } from '@/types/common';
import type { AlertEventRecord, AlertRuleRecord, AlertChannel } from '@/types';
import i18n from '@/i18n';
export function AlertManager() {
const [activeTab, setActiveTab] = useState('events');
const [alerts, setAlerts] = useState<AlertEventRecord[]>([]);
const [rules, setRules] = useState<AlertRuleRecord[]>([]);
const [loading, setLoading] = useState(true);
const [ruleDialogOpen, setRuleDialogOpen] = useState(false);
const [editingRule, setEditingRule] = useState<AlertRuleRecord | null>(null);
const [channelDialogOpen, setChannelDialogOpen] = useState(false);
const [notifyChannels, setNotifyChannels] = useState<AlertChannel[]>([]);
const loadData = async () => {
try {
setLoading(true);
const [alertsData, rulesData, channelsData] = await Promise.all([
alertApi.getAlerts(),
alertApi.getRules(),
alertApi.getNotifyChannels(),
]);
setAlerts((alertsData as JsonData)?.list || []);
setRules((rulesData as JsonData)?.list || (Array.isArray(rulesData) ? rulesData : []));
setNotifyChannels((channelsData as JsonData)?.list || []);
} catch (error) {
console.error('Failed to load alert data:', error);
} finally {
setLoading(false);
}
};
useEffect(() => {
loadData();
}, []);
const openCreateRule = () => {
setEditingRule(null);
setRuleDialogOpen(true);
};
const openEditRule = (rule: AlertRuleRecord) => {
setEditingRule(rule);
setRuleDialogOpen(true);
};
// 告警趋势数据
const alertTrendData = [
{ time: '00:00', critical: 2, warning: 5, info: 8 },
{ time: '04:00', critical: 1, warning: 3, info: 6 },
{ time: '08:00', critical: 3, warning: 7, info: 12 },
{ time: '12:00', critical: 5, warning: 10, info: 15 },
{ time: '16:00', critical: 4, warning: 8, info: 10 },
{ time: '20:00', critical: 3, warning: 6, info: 9 },
{ time: '24:00', critical: 2, warning: 4, info: 7 },
];
const pageTabs = [
{ id: 'events', label: i18n.t('monitor.alertMgr2.alertEvents') },
{ id: 'rules', label: i18n.t('monitor.alertRules') },
];
return (
<PageShell
variant="list"
title={i18n.t('monitor.alertMgr2.monitorAlert')}
subtitle={i18n.t('monitor.alertMgr2.subtitle')}
icon={<Bell className="h-5 w-5" />}
tabs={pageTabs}
activeTab={activeTab}
onTabChange={setActiveTab}
metrics={(
<StatStrip
items={[
{ label: i18n.t('monitor.alertRules'), value: rules.length, color: 'blue' },
{ label: i18n.t('monitor.alertHelper.statusFiring'), value: alerts.filter((e) => e.status === 'firing').length, color: 'red' },
{ label: i18n.t('monitor.alertMgr2.todayAlerts'), value: alerts.length, color: 'amber' },
{ label: i18n.t('kubernetes.msg.uncordonSuccess'), value: alerts.filter((e) => e.status === 'resolved').length, color: 'emerald' },
]}
/>
)}
actions={(
<>
<Button
variant="outline"
className="dg-btn-secondary"
onClick={() => setChannelDialogOpen(true)}
>
<Settings className="h-4 w-4 mr-2" />
通知配置
</Button>
<Button className="dg-btn-primary" onClick={openCreateRule}>
<Plus className="h-4 w-4 mr-2" />
新建规则
</Button>
</>
)}
>
<Card className="dg-card">
<CardHeader className="pb-2">
<CardTitle className="text-[var(--dg-text-primary)]"> {i18n.t('monitor.alertmanagertsx.s358b05d0')} </CardTitle>
<CardDescription className="dg-text-muted"> {i18n.t('monitor.alertmanagertsx.s615fa6cb')} </CardDescription>
</CardHeader>
<CardContent>
<div className="h-[200px]">
<ResponsiveContainer width="100%" height="100%">
<AreaChart data={alertTrendData}>
<defs>
<linearGradient id="colorCritical" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#ef4444" stopOpacity={0.3} />
<stop offset="95%" stopColor="#ef4444" stopOpacity={0} />
</linearGradient>
<linearGradient id="colorWarning" x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor="#f59e0b" stopOpacity={0.3} />
<stop offset="95%" stopColor="#f59e0b" stopOpacity={0} />
</linearGradient>
</defs>
<CartesianGrid strokeDasharray="3 3" stroke="#1e293b" />
<XAxis dataKey="time" stroke="#475569" fontSize={12} />
<YAxis stroke="#475569" fontSize={12} />
<Tooltip
contentStyle={{
backgroundColor: '#0f172a',
border: '1px solid #1e293b',
borderRadius: '6px',
color: '#f8fafc',
}}
/>
<Area
type="monotone"
dataKey="critical"
stroke="#ef4444"
fillOpacity={1}
fill="url(#colorCritical)"
name={i18n.t('monitor.alertHelper.sevCritical')}
/>
<Area
type="monotone"
dataKey="warning"
stroke="#f59e0b"
fillOpacity={1}
fill="url(#colorWarning)"
name={i18n.t('kubernetes.event.warning')}
/>
</AreaChart>
</ResponsiveContainer>
</div>
</CardContent>
</Card>
{activeTab === 'events' && (
<AlertEventsTab alerts={alerts} loading={loading} onReload={loadData} />
)}
{activeTab === 'rules' && (
<AlertRulesTab
rules={rules}
notifyChannels={notifyChannels}
onReload={loadData}
onEditRule={openEditRule}
/>
)}
<AlertRuleDialog
open={ruleDialogOpen}
onOpenChange={setRuleDialogOpen}
editingRule={editingRule}
notifyChannels={notifyChannels}
onSaved={loadData}
/>
<AlertChannelDialog
open={channelDialogOpen}
onOpenChange={setChannelDialogOpen}
notifyChannels={notifyChannels}
onChannelsChanged={loadData}
/>
</PageShell>
);
}