fix(ui): сохранять переходные и неизвестные состояния OSPF

This commit is contained in:
Denozordec committed 2026-09-30 18:02:11 +07:00
1 parent c18c148a23
commit 3dc25628af
9 files changed
+82 -22

No files matched your search

+13 -15
View File
@@ -1,5 +1,6 @@
"use client"
import { normalizeOspfState, normalizeBfdState, ospfNodeState, type OspfNeighborState, type BfdState } from "@/lib/ospf-state"
import { useMemo, useState, useEffect, useRef } from "react"
import { ospfCatalogSchema, ospfServersSchema, ospfOptimizeSchema, type OspfCatalog } from "@/lib/ospf-response"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
@@ -62,7 +63,7 @@ interface OspfNeighbor {
remoteLabel: string
remoteRouterId: string
area: string
state: "Full" | "2-Way" | "ExStart" | "Down"
state: OspfNeighborState
cost: number
uptime: string
priority: number
@@ -88,7 +89,7 @@ interface BfdSession {
serverLabel: string
localAddr: string
remoteAddr: string
state: "Up" | "Down" | "Init" | "AdminDown"
state: BfdState
interval: number // txInterval ms
multiplier: number
iface: string
@@ -139,8 +140,7 @@ function isRefInterfaceName(name: string): boolean {
// ─── backend → frontend mappers ───────────────────────────────────────────────
function backendToNeighbor(b: BackendNeighbor, ifaceMap: Map<string, number>): OspfNeighbor {
const validStates = ["Full", "2-Way", "ExStart", "Down"]
const state = validStates.includes(b.state) ? b.state as OspfNeighbor["state"] : "Down"
const state = normalizeOspfState(b.state)
const costKey = `${b.serverId}::${b.interface}`
return {
id: `${b.serverId}-${b.id}`,
@@ -176,8 +176,7 @@ function backendToItem(b: BackendInterface): OspfItem {
}
function backendToBfdSession(b: BackendBfdSession): BfdSession {
const validStates = ["Up", "Down", "Init", "AdminDown"]
const state = validStates.includes(b.state) ? b.state as BfdSession["state"] : "Down"
const state = normalizeBfdState(b.state)
return {
id: `${b.serverId}-${b.id}`,
serverId: String(b.serverId),
@@ -283,6 +282,8 @@ const TOPO_H = 360
const NODE_R = 34
const NODE_THEME = {
Pending: { fill: "#2d1e06", stroke: "#fbbf24", glow: "transparent" },
Unknown: { fill: "#111827", stroke: "#9ca3af", glow: "transparent" },
Full: { fill: "#0f2d1f", stroke: "#4ade80", glow: "rgba(74,222,128,0.18)" },
"2-Way":{ fill: "#2d1e06", stroke: "#fbbf24", glow: "rgba(251,191,36,0.14)" },
Down: { fill: "#2d0f0f", stroke: "#f87171", glow: "transparent" },
@@ -290,6 +291,7 @@ const NODE_THEME = {
}
const EDGE_COLOR: Record<string, string> = {
Attempt: "#fbbf24", Init: "#fbbf24", ExStart: "#fbbf24", Exchange: "#fbbf24", Loading: "#fbbf24", Unknown: "#9ca3af",
Full: "#4ade80",
"2-Way":"#fbbf24",
Down: "#f87171",
@@ -413,10 +415,7 @@ function TopologyGraph({ graphNodes, graphEdges, highlightId, selectedId, showDo
function nodeState(id: string): keyof typeof NODE_THEME {
const involved = graphEdges.filter(e => e.from === id || e.to === id)
if (!involved.length) return "none"
if (involved.some(e => e.state === "Full")) return "Full"
if (involved.some(e => e.state === "2-Way")) return "2-Way"
return "Down"
return ospfNodeState(involved.map(edge => edge.state))
}
return (
@@ -568,10 +567,7 @@ function NodeDetailPanel({
const partCnt = adjacencies.filter(a => a.state === "2-Way").length
// derive state from edges
let panelState: keyof typeof NODE_THEME = "none"
if (adjacencies.some(a => a.state === "Full")) panelState = "Full"
else if (adjacencies.some(a => a.state === "2-Way")) panelState = "2-Way"
else if (adjacencies.length) panelState = "Down"
const panelState = ospfNodeState(adjacencies.map(edge => edge.state))
const theme = NODE_THEME[panelState]
const siteFill =
@@ -926,6 +922,7 @@ function NeighborsTab({
const [selectedId, setSelectedId] = useState<string | null>(null)
const [showDots, setShowDots] = useState(true)
dataIncomplete = dataIncomplete || neighbors.some(n => n.state === "Unknown")
const fullCount = neighbors.filter(n => n.state === "Full").length
return (
@@ -1054,6 +1051,7 @@ function RoutesTab({ routes }: { routes: OspfRoute[] }) {
// ─── BFD tab ──────────────────────────────────────────────────────────────────
function BfdTab({ sessions, dataIncomplete }: { sessions: BfdSession[]; dataIncomplete: boolean }) {
dataIncomplete = dataIncomplete || sessions.some(b => b.state === "Unknown")
const upCount = sessions.filter(b => b.state === "Up").length
const downCount = sessions.filter(b => b.state === "Down" || b.state === "AdminDown").length
const initCount = sessions.filter(b => b.state === "Init").length
@@ -1094,7 +1092,7 @@ function BfdTab({ sessions, dataIncomplete }: { sessions: BfdSession[]; dataInco
},
{
id: "init",
label: "Init / другие",
label: "Init",
value: dataIncomplete ? "—" : initCount,
icon: <LayersIcon className="size-4" />,
iconClassName: initCount > 0 ? "text-warning" : "text-muted-foreground",