198 lines
7.1 KiB
TypeScript
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>
|
|
);
|
|
} |