"use client" import { useEffect, useMemo, useState, type ReactNode } from "react" import { Activity, Filter, HeartPulse, Server } from "lucide-react" import { Badge } from "@/components/reui/badge" import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover" import { cn } from "@/lib/utils" import { useDataSource } from "@/lib/data-source" import { filters, pingProbes, servers } from "@/lib/data" import type { SidebarCountsDto } from "@/lib/sidebar-badges" type MonitorMetric = { id: string label: string value: string unit: string percent: number icon: ReactNode tone: "success" | "warning" | "destructive" | "info" alert: boolean } type HealthDto = { status?: string } function toneColor(tone: MonitorMetric["tone"]) { switch (tone) { case "success": return "var(--color-success)" case "warning": return "var(--color-warning)" case "destructive": return "var(--color-destructive)" default: return "var(--color-info)" } } function MetricBar({ percent, color }: { percent: number; color: string }) { return (
) } function MetricCell({ metric }: { metric: MonitorMetric }) { const color = toneColor(metric.tone) return (
{metric.icon}
{metric.label}
{metric.value} {metric.unit}
) } /** Live system monitor popover — app-shell-7. @see https://reui.io/preview/base/app-shell-7 */ export function SystemMonitorPopover() { const { mode, backendUrl } = useDataSource() const [healthOk, setHealthOk] = useState(null) const [counts, setCounts] = useState(null) useEffect(() => { if (mode !== "live") { setHealthOk(true) setCounts({ servers: servers.length, filterRules: filters.length, uptimeProbes: pingProbes.length, uptimeSpeedProbes: 0, monitoringItems: pingProbes.length, recursiveRoutes: 0, }) return } let cancelled = false const load = async () => { const base = backendUrl.replace(/\/$/, "") try { const [hRes, cRes] = await Promise.all([ fetch(`${base}/health`), fetch(`${base}/api/sidebar-counts`), ]) if (cancelled) return if (hRes.ok) { const json = (await hRes.json()) as HealthDto setHealthOk(json.status === "ok") } else { setHealthOk(false) } if (cRes.ok) { setCounts((await cRes.json()) as SidebarCountsDto) } else { setCounts(null) } } catch { if (!cancelled) { setHealthOk(false) setCounts(null) } } } void load() const id = window.setInterval(load, 30_000) return () => { cancelled = true window.clearInterval(id) } }, [mode, backendUrl]) const serversCount = counts?.servers ?? 0 const filtersCount = counts?.filterRules ?? 0 const monitoringCount = counts?.monitoringItems ?? 0 const apiOk = healthOk === true const metrics = useMemo( () => [ { id: "api", label: "API", value: healthOk == null ? "…" : apiOk ? "OK" : "—", unit: "", percent: apiOk ? 100 : 20, icon: , tone: apiOk ? "success" : "destructive", alert: healthOk === false, }, { id: "servers", label: "Серверы", value: String(serversCount), unit: "шт.", percent: Math.min(100, Math.max(12, serversCount * 8)), icon: , tone: serversCount > 0 ? "success" : "warning", alert: false, }, { id: "filters", label: "Фильтры", value: String(filtersCount), unit: "шт.", percent: Math.min(100, Math.max(12, filtersCount * 5)), icon: , tone: "info", alert: false, }, { id: "uptime", label: "Мониторинг", value: String(monitoringCount), unit: "шт.", percent: Math.min(100, Math.max(12, monitoringCount * 8)), icon: , tone: monitoringCount > 0 ? "success" : "warning", alert: false, }, ], [apiOk, filtersCount, healthOk, monitoringCount, serversCount], ) const spiking = metrics.some((m) => m.alert) return ( } > {spiking ? ( ) : null} Система {spiking ? "Внимание" : "Норма"}
Монитор MikrotikManager {new Date().toLocaleTimeString("ru-RU")}
{metrics.map((metric, i) => (
= 2 && "border-border border-t", )} >
))}
Источник:{" "} {mode === "live" ? "API" : "мок"}
) }