diff --git a/frontend/src/NetworkMapUnifi.jsx b/frontend/src/NetworkMapUnifi.jsx
index cd0917e..01f7c63 100644
--- a/frontend/src/NetworkMapUnifi.jsx
+++ b/frontend/src/NetworkMapUnifi.jsx
@@ -63,7 +63,9 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
const [size, setSize] = useState({ w: 800, h: 520 });
const [positions, setPositions] = useState({});
const [drag, setDrag] = useState(null);
- const [isFullscreen, setIsFullscreen] = useState(false);
+ const [isFullscreen, setIsFullscreen] = useState(false);
+ const [hoveredEdgeKey, setHoveredEdgeKey] = useState(null);
+ const [selectedEdge, setSelectedEdge] = useState(null);
useEffect(() => {
const el = containerRef.current;
@@ -183,12 +185,14 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
if (typeof rev === 'number') parts.push(`← ${rev} ms`);
const pingLabel = parts.length ? parts.join(' ') : null;
return {
+ edgeKey: `${from}-${to}-${c.tunnelType || ''}`,
from,
to,
p1,
p2,
pingLabel,
tunnelType: c.tunnelType,
+ raw: c,
};
}).filter((e) => e.p1 && e.p2);
}, [connections, positions, pingMap]);
@@ -281,14 +285,27 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
const LABEL_W = 64;
const LABEL_H = 20;
const LABEL_GAP = 4;
- return edgesWithPing.map((e, idx) => (
-
+ return edgesWithPing.map((e, idx) => {
+ const dimFactor =
+ hoveredEdgeKey && hoveredEdgeKey !== e.edgeKey ? 0.25 : 1;
+ const strokeWidth = hoveredEdgeKey === e.edgeKey ? 3.2 : 2.5;
+ return (
+ setHoveredEdgeKey(e.edgeKey)}
+ onMouseLeave={() =>
+ setHoveredEdgeKey((prev) => (prev === e.edgeKey ? null : prev))
+ }
+ onClick={() => setSelectedEdge(e)}
+ style={{ cursor: 'pointer' }}
+ >
{e.pingLabel && (() => {
const base = labelPoint(e);
@@ -332,7 +349,8 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
);
})()}
- ));
+ );
+ });
})()}
@@ -389,6 +407,107 @@ export default function NetworkMapUnifi({ servers = [], connections = [], pingMa
);
})}
+ {selectedEdge && (() => {
+ const fromServer = servers.find((s) => String(s.ip) === selectedEdge.from);
+ const toServer = servers.find((s) => String(s.ip) === selectedEdge.to);
+ const fwd = pingMap[`${selectedEdge.from}:${selectedEdge.to}`];
+ const rev = pingMap[`${selectedEdge.to}:${selectedEdge.from}`];
+ return (
+
setSelectedEdge(null)}
+ >
+
e.stopPropagation()}
+ >
+
+
Детали соединения
+
+
+
+
+ От
+
+
+ {fromServer?.dns || fromServer?.ip || selectedEdge.from}{' '}
+ ({fromServer?.ip || '—'}){' '}
+
+ {String(fromServer?.type || '').toUpperCase() || 'SERVER'}
+
+
+
+
+
+ К
+
+
+ {toServer?.dns || toServer?.ip || selectedEdge.to}{' '}
+ ({toServer?.ip || '—'}){' '}
+
+ {String(toServer?.type || '').toUpperCase() || 'SERVER'}
+
+
+
+
+
+ Туннель
+
+
+
+ {selectedEdge.tunnelType || 'TUNNEL'}
+
+ {selectedEdge.raw?.internalFromTo && (
+
+ {selectedEdge.raw.internalToFrom} ⇄ {selectedEdge.raw.internalFromTo}
+
+ )}
+
+
+
+
+ Пинг
+
+
+
+ →
+ {typeof fwd === 'number' ? `${fwd} ms` : 'нет данных'}
+
+
+ ←
+ {typeof rev === 'number' ? `${rev} ms` : 'нет данных'}
+
+
+
+
+
+
+ );
+ })()}