'use client'; import Link from 'next/link'; import { useQuery } from '@tanstack/react-query'; import { TrendChart } from '@/components/data/trend-chart'; import { EmptyState } from '@/components/feedback/empty-state'; import { ErrorState } from '@/components/feedback/error-state'; import { LoadingState } from '@/components/feedback/loading-state'; import { AppCard } from '@/components/ui/app-card'; import { StatusBadge } from '@/components/ui/status-badge'; import { getDashboardOverview } from '@/features/dashboard/api/overview'; import type { DashboardAlert, DashboardNodeHealth, } from '@/features/dashboard/types'; import { getNodeStatusLabel, getNodeStatusVariant, getOpenrestyStatusLabel, getOpenrestyStatusVariant, } from '@/features/nodes/utils'; import { formatDateTime, formatRelativeTime } from '@/lib/utils/date'; function formatPercent(value?: number | null) { if (value === undefined || value === null || Number.isNaN(value)) { return '—'; } return `${value.toFixed(1)}%`; } function getErrorMessage(error: unknown) { return error instanceof Error ? error.message : '请求失败,请稍后重试。'; } function formatTrendHour(value: string) { const date = new Date(value); if (Number.isNaN(date.getTime())) { return '—'; } return `${date.getHours().toString().padStart(2, '0')}:00`; } function getAlertVariant( alert: DashboardAlert, ): 'success' | 'warning' | 'danger' | 'info' { if (alert.status === 'resolved') { return 'success'; } if (alert.severity === 'critical') { return 'danger'; } if (alert.severity === 'warning') { return 'warning'; } return 'info'; } function OverviewMetric({ label, value, hint, }: { label: string; value: string | number; hint?: string; }) { return (

{label}

{value}

{hint ? (

{hint}

) : null}
); } function NodeHealthRow({ node }: { node: DashboardNodeHealth }) { return (

{node.name}

{node.node_id}

CPU

{formatPercent(node.cpu_usage_percent)}

内存

{formatPercent(node.memory_usage_percent)}

最近窗口请求

{node.request_count}

活动异常

{node.active_event_count}

当前版本:{node.current_version || '未应用'}

最近心跳: {node.last_seen_at ? ` ${formatRelativeTime(node.last_seen_at)} · ${formatDateTime(node.last_seen_at)}` : ' 暂无'}

); } export function DashboardOverview() { const overviewQuery = useQuery({ queryKey: ['dashboard', 'overview'], queryFn: getDashboardOverview, refetchInterval: 10000, }); if (overviewQuery.isLoading) { return ; } if (overviewQuery.isError) { return ( ); } const overview = overviewQuery.data; if (!overview) { return ( ); } return (
生成时间:{formatDateTime(overview.generated_at)}
} >
formatTrendHour(point.bucket_started_at), )} series={[ { label: '请求量', color: '#f59e0b', fillColor: 'rgba(245, 158, 11, 0.18)', variant: 'area', values: overview.trends.traffic_24h.map( (point) => point.request_count, ), }, { label: '错误量', color: '#ef4444', values: overview.trends.traffic_24h.map( (point) => point.error_count, ), }, ]} /> formatTrendHour(point.bucket_started_at), )} series={[ { label: '平均 CPU', color: '#0f766e', fillColor: 'rgba(15, 118, 110, 0.15)', variant: 'area', values: overview.trends.capacity_24h.map( (point) => point.average_cpu_usage_percent, ), valueFormatter: formatPercent, }, { label: '平均内存', color: '#2563eb', values: overview.trends.capacity_24h.map( (point) => point.average_memory_usage_percent, ), valueFormatter: formatPercent, }, ]} />
进入节点页 } > {overview.nodes.length === 0 ? ( ) : (
{overview.nodes.slice(0, 8).map((node) => ( ))}
)}
{overview.active_alerts.length === 0 ? ( ) : (
{overview.active_alerts.map((alert) => (

{alert.node_name}

{alert.message || '暂无详细消息'}

{formatRelativeTime(alert.last_triggered_at)} ·{' '} {formatDateTime(alert.last_triggered_at)}

))}
)}
); }