- AlertCenter.tsx: 723→390行,提取类型/helpers/渠道Tab到子组件 - alert-center-helpers.tsx: 新建(127行),共享Alert/AlertRule/NotificationChannel类型+mapAlert+badge helpers - AlertChannelDialog.tsx: 新建(121行),通知渠道创建/编辑对话框(自包含useEffect同步) - AlertChannelsTab.tsx: 新建(152行),通知渠道Tab自包含(状态+API+渲染+CRUD) - AlertCenterDetail.tsx: 去重getSeverityBadge,统一从helpers导入 - TopologyView.tsx: 修复editForm.type类型推断(string→TopologyNode['type'])
131 lines
4.5 KiB
TypeScript
131 lines
4.5 KiB
TypeScript
import { Check, ChevronRight } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Label } from '@/components/ui/label';
|
|
import {
|
|
Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter,
|
|
} from '@/components/ui/dialog';
|
|
import { type Alert, getSeverityBadge } from './alert-center-helpers'; // Logan.Li — 统一从 helpers 导入
|
|
|
|
export function AlertDetailDialog({
|
|
alert,
|
|
open,
|
|
onOpenChange,
|
|
onAcknowledge,
|
|
}: {
|
|
alert: Alert | null;
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
onAcknowledge: (id: string) => void;
|
|
}) {
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent className="max-w-3xl">
|
|
<DialogHeader>
|
|
<DialogTitle className="flex items-center gap-2">
|
|
{alert?.title}
|
|
{alert && getSeverityBadge(alert.severity)}
|
|
</DialogTitle>
|
|
</DialogHeader>
|
|
{alert && (
|
|
<div className="space-y-4">
|
|
<div>
|
|
<Label className="text-muted-foreground">描述</Label>
|
|
<p className="mt-1">{alert.description}</p>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-4">
|
|
<div>
|
|
<Label className="text-muted-foreground">来源</Label>
|
|
<p className="font-medium">{alert.source}</p>
|
|
</div>
|
|
<div>
|
|
<Label className="text-muted-foreground">服务</Label>
|
|
<p className="font-medium">{alert.service}</p>
|
|
</div>
|
|
<div>
|
|
<Label className="text-muted-foreground">指标</Label>
|
|
<p className="font-medium">{alert.metric}</p>
|
|
</div>
|
|
<div>
|
|
<Label className="text-muted-foreground">当前值 / 阈值</Label>
|
|
<p className="font-medium">{alert.value} / {alert.threshold}</p>
|
|
</div>
|
|
<div>
|
|
<Label className="text-muted-foreground">开始时间</Label>
|
|
<p className="font-medium">{alert.startedAt}</p>
|
|
</div>
|
|
{alert.resolvedAt && (
|
|
<div>
|
|
<Label className="text-muted-foreground">解决时间</Label>
|
|
<p className="font-medium">{alert.resolvedAt}</p>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div>
|
|
<Label className="text-muted-foreground">标签</Label>
|
|
<div className="flex flex-wrap gap-2 mt-1">
|
|
{Object.entries(alert.labels).map(([k, v]) => (
|
|
<Badge key={k} variant="outline">{k}={v}</Badge>
|
|
))}
|
|
</div>
|
|
</div>
|
|
{alert.runbookUrl && (
|
|
<div>
|
|
<Label className="text-muted-foreground">处理手册</Label>
|
|
<a href={alert.runbookUrl} target="_blank" rel="noopener noreferrer" className="text-blue-500 hover:underline block mt-1">
|
|
查看处理手册 →
|
|
</a>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
<DialogFooter>
|
|
<Button variant="outline" onClick={() => onOpenChange(false)}>关闭</Button>
|
|
{alert?.status === 'firing' && (
|
|
<Button onClick={() => { onAcknowledge(alert.id); onOpenChange(false); }}>
|
|
<Check className="w-4 h-4 mr-2" />
|
|
确认告警
|
|
</Button>
|
|
)}
|
|
</DialogFooter>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|
|
|
|
export function AlertSilenceDialog({
|
|
alert,
|
|
open,
|
|
onOpenChange,
|
|
onSilence,
|
|
}: {
|
|
alert: Alert | null;
|
|
open: boolean;
|
|
onOpenChange: (open: boolean) => void;
|
|
onSilence: (id: string, duration: string) => void;
|
|
}) {
|
|
return (
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
<DialogContent>
|
|
<DialogHeader>
|
|
<DialogTitle>静默告警</DialogTitle>
|
|
<DialogDescription>选择静默持续时间</DialogDescription>
|
|
</DialogHeader>
|
|
<div className="space-y-2">
|
|
{['1h', '4h', '8h', '24h', '7d'].map((duration) => (
|
|
<Button
|
|
key={duration}
|
|
variant="outline"
|
|
className="w-full justify-between"
|
|
onClick={() => alert && onSilence(alert.id, duration)}
|
|
>
|
|
<span>静默 {duration}</span>
|
|
<ChevronRight className="w-4 h-4" />
|
|
</Button>
|
|
))}
|
|
</div>
|
|
</DialogContent>
|
|
</Dialog>
|
|
);
|
|
}
|