import { useState, useEffect, useCallback, useMemo } from 'react';
import { useParams, useNavigate, Link } from 'react-router-dom';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import {
Select, SelectContent, SelectItem, SelectTrigger, SelectValue,
} from '@/components/ui/select';
import { RefreshCw, ChevronRight, Bell, AlertTriangle, X } from 'lucide-react';
import { kubernetesApi } from '@/api';
import { EventTable } from './EventTable';
import { normalizeK8sEvent, formatEventRelative } from './k8s-event-utils';
import type { JsonData } from '@/types/common';
interface RankItem { name: string; count: number }
// 简洁排名列表,替代花哨柱状图 // Logan.Li
function RankPanel({
title, items, selected, onSelect, emptyText,
}: {
title: string;
items: RankItem[];
selected?: string;
onSelect?: (name: string) => void;
emptyText?: string;
}) {
const max = Math.max(...items.map((i) => i.count), 1);
return (
{title}
{items.length === 0 ? (
{emptyText || '暂无数据'}
) : (
{items.map((item) => {
const active = selected === item.name;
return (
);
})}
)}
);
}
function StatCell({ label, value, hint }: { label: string; value: number | string; hint?: string }) {
return (
{label}
{value}
{hint &&
{hint}
}
);
}
export default function EventsPage() {
const { clusterCode } = useParams<{ clusterCode: string }>();
const navigate = useNavigate();
const [loading, setLoading] = useState(false);
const [events, setEvents] = useState([]);
const [clusters, setClusters] = useState([]);
const [namespaces, setNamespaces] = useState([]);
const [selectedNs, setSelectedNs] = useState('all');
const [filterType, setFilterType] = useState('all');
const [timeWindow, setTimeWindow] = useState('0');
const [activeCluster, setActiveCluster] = useState(clusterCode || '');
const [summary, setSummary] = useState(null);
const [selectedReason, setSelectedReason] = useState('');
const [selectedKind, setSelectedKind] = useState('');
useEffect(() => {
kubernetesApi.getClusters().then((res: JsonData) => {
const list = res?.list || [];
setClusters(list);
if (!activeCluster && list.length > 0) {
const code = list[0].code || String(list[0].id);
setActiveCluster(code);
navigate(`/kubernetes/${code}/events`, { replace: true });
}
}).catch(() => {});
}, []);
useEffect(() => {
if (!activeCluster) return;
kubernetesApi.getNamespaces(activeCluster).then((res: JsonData) => {
setNamespaces(Array.isArray(res) ? res : (res?.list || res?.data || []));
}).catch(() => {});
}, [activeCluster]);
const loadEvents = useCallback(async () => {
if (!activeCluster) return;
setLoading(true);
try {
const ns = selectedNs === 'all' ? undefined : selectedNs;
const ft = filterType === 'all' ? undefined : filterType;
const res: JsonData = await kubernetesApi.getEvents(activeCluster, ns, ft);
setEvents(Array.isArray(res) ? res : (res?.list || []));
} catch { setEvents([]); } finally { setLoading(false); }
}, [activeCluster, selectedNs, filterType]);
const loadSummary = useCallback(async () => {
if (!activeCluster) return;
try {
const ns = selectedNs === 'all' ? undefined : selectedNs;
const hours = parseInt(timeWindow) || undefined;
const res: JsonData = await kubernetesApi.getEventsSummary(activeCluster, ns, hours);
setSummary(res || null);
} catch { setSummary(null); }
}, [activeCluster, selectedNs, timeWindow]);
useEffect(() => { loadEvents(); }, [loadEvents]);
useEffect(() => { loadSummary(); }, [loadSummary]);
const displayEvents = useMemo(() => {
let filtered = events.map((e) => normalizeK8sEvent(e));
const hours = parseInt(timeWindow, 10);
if (hours > 0) {
const since = Date.now() - hours * 3600 * 1000;
filtered = filtered.filter((e) => !e.lastTime || new Date(e.lastTime).getTime() >= since);
}
if (selectedReason) filtered = filtered.filter((e) => e.reason === selectedReason);
if (selectedKind) {
filtered = filtered.filter((e) =>
e.objectKind === selectedKind || e.object.startsWith(`${selectedKind}/`),
);
}
return filtered;
}, [events, selectedReason, selectedKind, timeWindow]);
const warningCount = useMemo(() => events.filter((e) => e.type === 'Warning').length, [events]);
const normalCount = useMemo(() => events.filter((e) => e.type === 'Normal').length, [events]);
const reasonData = useMemo(() =>
(summary?.by_reason || []).map((r: JsonData) => ({ name: r.reason || 'Unknown', count: r.count })),
[summary]);
const nsData = useMemo(() =>
(summary?.by_namespace || []).map((n: JsonData) => ({ name: n.namespace || '', count: n.count })),
[summary]);
const kindData = useMemo(() =>
(summary?.by_kind || []).map((k: JsonData) => ({ name: k.kind || 'Unknown', count: k.count })),
[summary]);
return (
集群
{activeCluster && (
<>
{activeCluster}
>
)}
事件
事件中心
{/* 统计条:统一边框,无彩色卡片 */}
{(selectedReason || selectedKind) && (
筛选:
{selectedReason && (
)}
{selectedKind && (
)}
)}
{reasonData.length > 0 && (
{nsData.length > 0 && }
{kindData.length > 0 && (
)}
)}
{summary?.recent_warnings?.length > 0 && (
最新告警 ({summary.recent_warnings.length})
{summary.recent_warnings.map((w: JsonData, i: number) => (
{w.reason}
{w.object}
{w.namespace}
{w.source && {w.source}}
×{w.count}
{formatEventRelative(w.last_time)}
{w.message}
))}
)}
事件列表 ({displayEvents.length})
);
}