"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" : "мок"}
)
}