import { useCallback, useEffect, useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import { Button } from '@/components/ui/button'; import { cn } from '@/lib/utils'; import { Clock, Server, ArrowRight } from 'lucide-react'; import { dashboardStatsApi, kubernetesApi } from '@/api'; import { StatStrip } from '@/components/ui/stat-strip'; import { CostOverviewCard, TopResourceUsageCard, ResourceUsageCard } from './DashboardCharts'; import { AlertMessagesCard, CertificateExpiryCard, ReleaseHistoryCard, OpsOverviewCard, KubernetesClusterCard, } from './DashboardRecent'; import type { DashboardRelease, DashboardCluster, DashboardAutomationStats, DashboardOpsStats, } from './DashboardRecent'; import { SkeletonDashboard } from '@/components/layout/SkeletonCard'; import { PageShell } from '@/components/layout/PageShell'; import type { JsonData } from '@/types/common'; import type { AlertEventRecord, CertExpiryRecord } from '@/types'; /** * Dashboard - 优化后的仪表盘页面 * * 优化点: * 1. 使用 SkeletonDashboard 骨架屏替代简单 spinner * 2. 页面结构使用 dg-page* 工具类 * 3. 统计卡片添加 staggered fade-in 动画 * 4. 操作按钮使用 dg-btn* 工具类 * 5. 更好的空状态处理 * 6. 添加页面标题区域的操作入口 */ export function Dashboard() { const { t } = useTranslation(); const navigate = useNavigate(); const [initialLoading, setInitialLoading] = useState(true); const [refreshing, setRefreshing] = useState(false); const [stats, setStats] = useState(null); const [clusters, setClusters] = useState([]); const [alerts, setAlerts] = useState([]); const [releases, setReleases] = useState([]); const [expCerts, setExpCerts] = useState([]); const [metrics, setMetrics] = useState([]); const refreshData = useCallback(async () => { setRefreshing(true); try { const [statsRes, clustersRes, alertsRes, releasesRes, metricsRes] = await Promise.all([ dashboardStatsApi.getStats().catch((e) => { console.error('Failed to fetch dashboard stats:', e); return null; }), kubernetesApi.getClusters().catch((e) => { console.error('Failed to fetch k8s clusters:', e); return null; }), dashboardStatsApi.getAlerts(10).catch((e) => { console.error('Failed to fetch dashboard alerts:', e); return null; }), dashboardStatsApi.getReleases(5).catch((e) => { console.error('Failed to fetch dashboard releases:', e); return null; }), dashboardStatsApi.getMetrics(6).catch((e) => { console.error('Failed to fetch metrics:', e); return []; }), ]); setStats(statsRes); setClusters(clustersRes?.list ?? []); setAlerts(Array.isArray(alertsRes) ? alertsRes : []); setReleases(Array.isArray(releasesRes) ? releasesRes : []); setExpCerts(statsRes?.expiring_certs ?? []); setMetrics(Array.isArray(metricsRes) ? metricsRes : []); } catch (error) { console.error('Failed to load dashboard data:', error); } finally { setRefreshing(false); setInitialLoading(false); } }, []); useEffect(() => { refreshData(); // 每 30 秒自动刷新(集群列表含实时统计,不宜过频)// Logan.Li const interval = setInterval(refreshData, 30_000); return () => clearInterval(interval); }, [refreshData]); if (initialLoading) { return ; } const serverStats = stats?.server_stats; const k8sStats = stats?.kubernetes_stats; const alertStats = stats?.alert_stats; const costStats = stats?.cost_stats; const automationStats = stats?.automation_stats as DashboardAutomationStats | undefined; const opsStats = stats?.ops_stats as DashboardOpsStats | undefined; return ( )} metrics={( 0 ? 'red' : 'muted' }, { label: t('dashboard.stat.monthlyCost'), value: `¥${Number(costStats?.month_cost ?? 0).toLocaleString()}`, color: 'amber' }, ]} /> )} contentClassName="space-y-6" > {/* 资源使用率趋势 — 第一行 */}
{/* 运维概览 → K8s 集群 → Top5 资源使用 */}
{/* 费用概览 & 发布历史 */}
{/* 告警消息 & 证书到期提醒 — 置底 */}
); }