"use client" import { Badge } from "@/components/reui/badge" import { cn } from "@/lib/utils" import { CompactDataGrid, type CompactDataGridColumn, type CompactDataGridProps, } from "@/components/data-grids/compact-data-grid" import type { AlertEngineRuleDiagSnapshot, PingProbeSnapshot, ResourceServerSnapshot, ServerRestPingSnapshot, SpeedRunSnapshot, TrafficServerSnapshot, } from "@/lib/scheduler-run-snapshot" type SnapshotRow = { id: string } function withRowId( rows: T[], idFn: (row: T, index: number) => string, ): (T & SnapshotRow)[] { return rows.map((row, index) => ({ ...row, id: idFn(row, index) })) } function SnapshotOkBadge({ ok, okLabel = "ok", errLabel = "ошибка", }: { ok: boolean okLabel?: string errLabel?: string }) { return ok ? ( {okLabel} ) : ( {errLabel} ) } function SnapshotErrorCell({ error, className }: { error?: string; className?: string }) { return ( {error ?? "—"} ) } function SnapshotDataGrid( props: CompactDataGridProps, ) { return } function TrafficSnapshotGrid({ servers }: { servers: TrafficServerSnapshot[] }) { const data = withRowId(servers, (s) => String(s.serverId)) const columns: CompactDataGridColumn<(typeof data)[number]>[] = [ { id: "name", header: "Сервер", accessorKey: "name", cell: (r) => {r.name} }, { id: "host", header: "Хост", accessorKey: "host", cell: (r) => {r.host}, }, { id: "ok", header: "Результат", enableSorting: false, cell: (r) => , }, { id: "interfaces", header: "IF", accessorKey: "interfaces", headerClassName: "text-right", cellClassName: "text-right tabular-nums", cell: (r) => r.interfaces ?? "—", }, { id: "sumRxMbps", header: "Σ RX", headerClassName: "text-right", cellClassName: "text-right tabular-nums", cell: (r) => (r.sumRxMbps != null ? `${r.sumRxMbps} Мбит/с` : "—"), }, { id: "sumTxMbps", header: "Σ TX", headerClassName: "text-right", cellClassName: "text-right tabular-nums", cell: (r) => (r.sumTxMbps != null ? `${r.sumTxMbps} Мбит/с` : "—"), }, { id: "error", header: "Ошибка", enableSorting: false, cell: (r) => , }, ] return } function ResourcesSnapshotGrid({ servers }: { servers: ResourceServerSnapshot[] }) { const data = withRowId(servers, (s) => String(s.serverId)) const columns: CompactDataGridColumn<(typeof data)[number]>[] = [ { id: "name", header: "Сервер", enableSorting: false, cell: (r) => (
{r.name} {r.host}
), }, { id: "status", header: "Статус", enableSorting: false, cell: (r) => ( {r.status} ), }, { id: "cpuLoadPct", header: "CPU %", headerClassName: "text-right", cellClassName: "text-right tabular-nums", cell: (r) => r.cpuLoadPct ?? "—", }, { id: "memory", header: "Память", enableSorting: false, headerClassName: "text-right", cellClassName: "text-right tabular-nums whitespace-nowrap", cell: (r) => r.memUsedMb != null && r.memTotalMb != null ? `${r.memUsedMb} / ${r.memTotalMb} МБ` : "—", }, { id: "memUsedPct", header: "% RAM", headerClassName: "text-right", cellClassName: "text-right tabular-nums", cell: (r) => (r.memUsedPct != null ? `${r.memUsedPct}%` : "—"), }, { id: "disk", header: "Диск своб.", enableSorting: false, headerClassName: "text-right", cellClassName: "text-right tabular-nums whitespace-nowrap", cell: (r) => r.diskFreeMb != null && r.diskTotalMb != null ? `${r.diskFreeMb} / ${r.diskTotalMb} МБ` : "—", }, { id: "uptimeSeconds", header: "Uptime", cellClassName: "tabular-nums", cell: (r) => (r.uptimeSeconds != null ? fmtUptimeSec(r.uptimeSeconds) : "—"), }, { id: "board", header: "Плата / ROS", enableSorting: false, cell: (r) => (
{r.boardName || "—"} {r.rosVersion || ""}
), }, { id: "error", header: "Ошибка", enableSorting: false, cell: (r) => , }, ] return } function ServersRestPingSnapshotGrid({ servers }: { servers: ServerRestPingSnapshot[] }) { const data = withRowId(servers, (s) => String(s.serverId)) const columns: CompactDataGridColumn<(typeof data)[number]>[] = [ { id: "name", header: "Сервер", accessorKey: "name", cell: (r) => {r.name} }, { id: "host", header: "Хост", accessorKey: "host", cell: (r) => {r.host}, }, { id: "ok", header: "Результат", enableSorting: false, cell: (r) => , }, { id: "latencyMs", header: "RTT REST", headerClassName: "text-right", cellClassName: "text-right tabular-nums", cell: (r) => (r.latencyMs != null ? `${r.latencyMs} мс` : "—"), }, { id: "error", header: "Ошибка", enableSorting: false, cell: (r) => , }, ] return } function PingSnapshotGrid({ probes }: { probes: PingProbeSnapshot[] }) { const data = withRowId(probes, (p) => `${p.probeId}-${p.target}`) const columns: CompactDataGridColumn<(typeof data)[number]>[] = [ { id: "name", header: "Проба", enableSorting: false, cell: (r) => (
{r.name} {r.probeId}
), }, { id: "target", header: "Цель", accessorKey: "target", cell: (r) => {r.target}, }, { id: "srcServerName", header: "Источник", accessorKey: "srcServerName" }, { id: "srcInterface", header: "IF", accessorKey: "srcInterface", cell: (r) => {r.srcInterface || "—"}, }, { id: "rttMs", header: "RTT", headerClassName: "text-right", cellClassName: "text-right tabular-nums", cell: (r) => (r.rttMs != null ? `${r.rttMs} мс` : "—"), }, { id: "lossPct", header: "Loss", headerClassName: "text-right", cellClassName: "text-right tabular-nums", cell: (r) => `${r.lossPct}%`, }, { id: "status", header: "Статус", enableSorting: false, cell: (r) => {r.status}, }, { id: "error", header: "Ошибка", enableSorting: false, cell: (r) => , }, ] return } function SpeedSnapshotGrid({ runs }: { runs: SpeedRunSnapshot[] }) { const data = withRowId(runs, (r) => r.probeId) const columns: CompactDataGridColumn<(typeof data)[number]>[] = [ { id: "probeId", header: "Проба", accessorKey: "probeId", cell: (r) => {r.probeId}, }, { id: "route", header: "Маршрут", enableSorting: false, cell: (r) => ( {r.srcServerName} {r.dstServerName} ), }, { id: "interfaces", header: "Интерфейсы", enableSorting: false, cell: (r) => ( {r.srcInterface || "—"} {r.dstInterface || "—"} ), }, { id: "protocol", header: "Протокол", enableSorting: false, cell: (r) => `${r.protocol} / ${r.direction} / ${r.durationSec}s`, }, { id: "txAvgMbps", header: "TX", headerClassName: "text-right", cellClassName: "text-right tabular-nums", cell: (r) => (r.txAvgMbps != null ? `${Number(r.txAvgMbps).toFixed(1)}` : "—"), }, { id: "rxAvgMbps", header: "RX", headerClassName: "text-right", cellClassName: "text-right tabular-nums", cell: (r) => (r.rxAvgMbps != null ? `${Number(r.rxAvgMbps).toFixed(1)}` : "—"), }, { id: "pingRttMs", header: "Ping RTT", headerClassName: "text-right", cellClassName: "text-right tabular-nums", cell: (r) => (r.pingRttMs != null ? `${r.pingRttMs} мс` : "—"), }, { id: "pingLossPct", header: "Loss", headerClassName: "text-right", cellClassName: "text-right tabular-nums", cell: (r) => (r.pingLossPct != null ? `${r.pingLossPct}%` : "—"), }, { id: "ok", header: "Результат", enableSorting: false, cell: (r) => , }, { id: "error", header: "Ошибка", enableSorting: false, cell: (r) => (
{r.error ?? ""} {r.pingError ? ( ping: {r.pingError} ) : null}
), }, ] return } function transitionRu(t: AlertEngineRuleDiagSnapshot["hitTransition"]): string { switch (t) { case "problem": return "проблема" case "recovery": return "восстановление" case "neutral": return "нейтрально" default: return "—" } } function blockedRu(b: AlertEngineRuleDiagSnapshot["blocked"]): string { switch (b) { case "no_hit": return "условие не выполнено" case "stability": return "стабильность (confirmStabilitySec)" case "cooldown": return "cooldown" case "no_telegram": return "нет Telegram" case "dedupe_positive": return "дедуп восстановления" case "in_group": return "в группе (отдельно не шлём)" default: return "—" } } function AlertEngineRuleDiagGrid({ ruleDiag }: { ruleDiag: AlertEngineRuleDiagSnapshot[] }) { const data = withRowId(ruleDiag, (d) => d.ruleId) const columns: CompactDataGridColumn<(typeof data)[number]>[] = [ { id: "ruleId", header: "ID правила", accessorKey: "ruleId", cell: (r) => ( {r.ruleId} ), }, { id: "evalHit", header: "Сработало", cell: (r) => (r.evalHit ? "Да" : "Нет"), }, { id: "hitTransition", header: "Тип срабатывания", cell: (r) => transitionRu(r.hitTransition), }, { id: "stabilityOk", header: "Стабильность", cell: (r) => (r.stabilityOk ? "Да" : "Нет"), }, { id: "cooldownOk", header: "Кулдаун", cell: (r) => (r.cooldownOk ? "Да" : "Нет"), }, { id: "telegramOk", header: "Telegram", cell: (r) => (r.telegramOk ? "Да" : "Нет"), }, { id: "hitMessage", header: "Сообщение", enableSorting: false, cell: (r) => ( {r.hitMessage ?? "—"} ), }, { id: "blocked", header: "Причина блока", cell: (r) => {blockedRu(r.blocked)}, }, ] return ( ) } function fmtUptimeSec(sec: number): string { if (sec <= 0) return "—" const d = Math.floor(sec / 86400) const h = Math.floor((sec % 86400) / 3600) const m = Math.floor((sec % 3600) / 60) if (d > 0) return `${d}д ${h}ч` if (h > 0) return `${h}ч ${m}м` return `${m}м` } export { SnapshotDataGrid, TrafficSnapshotGrid, ResourcesSnapshotGrid, ServersRestPingSnapshotGrid, PingSnapshotGrid, SpeedSnapshotGrid, AlertEngineRuleDiagGrid, type SnapshotRow, }