import { useMemo } from 'react' import { KpiSparklineCard, type KpiSparklineMetric } from '@/components/patterns/kpi-sparkline-card' import { kpiGridClassName } from '@/lib/ui-surface' import { aggregateNetworkMetrics, runningJobCount } from '@/queries/overview' import type { JobRow, ModuleRow, PeerRow, SpeakerRow } from '@/types/api' function syntheticSparkline(seed: number, points = 9): number[] { const base = Math.max(4, seed) return Array.from({ length: points }, (_, i) => Math.round(base * (0.82 + (i / points) * 0.18 + Math.sin(i + seed) * 0.04)), ) } function buildMetrics({ modules, peers, speakers, jobs, loading, }: { modules: ModuleRow[] peers: PeerRow[] speakers: SpeakerRow[] jobs: JobRow[] loading?: boolean }): KpiSparklineMetric[] { const enabledModules = modules.filter((m) => m.enabled !== false).length const network = aggregateNetworkMetrics(peers, speakers) const bgpPct = network.peersEnabled > 0 ? Math.round((network.peersEstablished / network.peersEnabled) * 100) : 0 const running = runningJobCount(jobs) const failedJobs = jobs.filter((j) => ['failed', 'error', 'cancelled'].includes(j.status.toLowerCase()), ).length return [ { id: 'bgp', title: 'BGP готовность', label: 'Установлено / включено', value: loading || network.peersEnabled === 0 ? '—' : `${bgpPct}%`, delta: loading ? '…' : bgpPct >= 90 ? 'стабильно' : 'внимание', deltaVariant: bgpPct >= 90 ? 'success-light' : bgpPct >= 50 ? 'warning-light' : 'destructive-light', detail: loading ? '' : `${network.peersEstablished} сессий`, tone: bgpPct >= 90 ? 'success' : bgpPct >= 50 ? 'warning' : 'danger', sparkline: syntheticSparkline(bgpPct || 40), }, { id: 'modules', title: 'Модули', label: 'Активные списки', value: loading ? '—' : `${enabledModules}`, delta: loading ? '…' : `${modules.length} всего`, deltaVariant: 'primary-light', detail: loading ? '' : 'маршрутизация', tone: 'info', sparkline: syntheticSparkline(enabledModules || 3), }, { id: 'speakers', title: 'Спикеры', label: 'В сети / всего', value: loading ? '—' : `${network.speakersOnline}/${network.speakersTotal}`, delta: loading || network.speakersTotal === 0 ? '…' : network.speakersOnline === network.speakersTotal ? 'все в сети' : 'частично', deltaVariant: network.speakersOnline === network.speakersTotal ? 'success-light' : 'warning-light', detail: loading ? '' : 'live-снимок', tone: network.speakersOnline === network.speakersTotal ? 'success' : 'warning', sparkline: syntheticSparkline(network.speakersOnline || 2), }, { id: 'jobs', title: 'Задачи', label: 'Активные / ошибки', value: loading ? '—' : String(running), delta: loading ? '…' : failedJobs > 0 ? `${failedJobs} ошибок` : 'без сбоев', deltaVariant: failedJobs > 0 ? 'destructive-light' : 'success-light', detail: loading ? '' : `${jobs.length} в выборке`, tone: failedJobs > 0 ? 'danger' : running > 0 ? 'info' : 'success', sparkline: syntheticSparkline(running + failedJobs || 1), }, ] } export function DashboardKpiSparklineRow({ modules, peers, speakers, jobs, loading, }: { modules: ModuleRow[] peers: PeerRow[] speakers: SpeakerRow[] jobs: JobRow[] loading?: boolean }) { const metrics = useMemo( () => buildMetrics({ modules, peers, speakers, jobs, loading }), [modules, peers, speakers, jobs, loading], ) return (
{metrics.map((metric) => ( ))}
) }