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, serverApi, kubernetesApi } from '@/api'; import { StatStrip } from '@/components/ui/stat-strip'; import { CostOverviewCard, TopResourceUsageCard, ResourceUsageCard } from './DashboardCharts'; import { AlertMessagesCard, CertificateExpiryCard, ReleaseHistoryCard, ServerStatusCard, KubernetesClusterCard, } from './DashboardRecent'; import type { DashboardRelease, DashboardServer, DashboardCluster, } from './DashboardRecent'; import { SkeletonDashboard } from '@/components/layout/SkeletonCard'; 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 [servers, setServers] = useState([]); 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, serversRes, clustersRes, alertsRes, releasesRes, metricsRes] = await Promise.all([ dashboardStatsApi.getStats().catch((e) => { console.error('Failed to fetch dashboard stats:', e); return null; }), serverApi.getServers({ page: 1, page_size: 100 }).catch((e) => { console.error('Failed to fetch servers:', 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); setServers(serversRes?.list ?? []); 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(); // 每 5 秒自动刷新 const interval = setInterval(refreshData, 5000); 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; return (
{/* Page Header */}

{t('dashboard.title')}

{t('dashboard.subtitle')} · {t('dashboard.lastUpdated')}: {new Date().toLocaleTimeString()}

{/* Stats — compact strip */}
0 ? 'red' : 'muted' }, { label: t('dashboard.stat.critical'), value: alertStats?.critical ?? 0, color: 'red' }, { label: t('dashboard.stat.monthlyCost'), value: `¥${Number(costStats?.month_cost ?? 0).toLocaleString()}`, color: 'amber' }, ]} />
{/* Resource Usage Chart */}
{/* Cost & Top Resource Usage */}
{/* Alert Messages & Certificate Expiry */}
{/* Bottom Row */}
); }