feat: Enhance GraphView and ServerManager components with tunnel type styles and hover effects for improved visualization and user interaction
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m45s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m45s
This commit is contained in:
+54
-23
@@ -4,6 +4,7 @@ function GraphView({ servers, connections }) {
|
|||||||
const [nodePositions, setNodePositions] = useState({});
|
const [nodePositions, setNodePositions] = useState({});
|
||||||
const [draggedNode, setDraggedNode] = useState(null);
|
const [draggedNode, setDraggedNode] = useState(null);
|
||||||
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
|
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
|
||||||
|
const [hoveredLinkId, setHoveredLinkId] = useState(null);
|
||||||
const svgRef = useRef(null);
|
const svgRef = useRef(null);
|
||||||
|
|
||||||
if (servers.length === 0) {
|
if (servers.length === 0) {
|
||||||
@@ -98,6 +99,17 @@ function GraphView({ servers, connections }) {
|
|||||||
return colors[country] || '#6b7280';
|
return colors[country] || '#6b7280';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Цвета/стили для разных типов туннелей
|
||||||
|
const getTunnelStyle = (tunnelType) => {
|
||||||
|
const map = {
|
||||||
|
GRE: { color: '#206bc4', dash: '0', width: 3 },
|
||||||
|
IPSec: { color: '#f59f00', dash: '6,4', width: 3 },
|
||||||
|
WireGuard: { color: '#2fb344', dash: '0', width: 4 },
|
||||||
|
OpenVPN: { color: '#be4bdb', dash: '3,3', width: 3 },
|
||||||
|
};
|
||||||
|
return map[tunnelType] || { color: '#667382', dash: '5,5', width: 2 };
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ width: '100%', height: '500px', border: '1px solid #e5e7eb', borderRadius: '8px', overflow: 'hidden', background: '#f8fafc' }}>
|
<div style={{ width: '100%', height: '500px', border: '1px solid #e5e7eb', borderRadius: '8px', overflow: 'hidden', background: '#f8fafc' }}>
|
||||||
<svg
|
<svg
|
||||||
@@ -110,6 +122,32 @@ function GraphView({ servers, connections }) {
|
|||||||
onMouseLeave={handleMouseUp}
|
onMouseLeave={handleMouseUp}
|
||||||
style={{ cursor: draggedNode ? 'grabbing' : 'default' }}
|
style={{ cursor: draggedNode ? 'grabbing' : 'default' }}
|
||||||
>
|
>
|
||||||
|
<defs>
|
||||||
|
{/* Определение стрелок под цвет канала */}
|
||||||
|
{Array.from(new Set(connections.map(c => c.tunnelType))).map((t) => {
|
||||||
|
const style = getTunnelStyle(t);
|
||||||
|
return (
|
||||||
|
<marker key={`arrow-${t}`}
|
||||||
|
id={`arrow-${t}`}
|
||||||
|
markerWidth="12"
|
||||||
|
markerHeight="8"
|
||||||
|
refX="10"
|
||||||
|
refY="4"
|
||||||
|
orient="auto"
|
||||||
|
>
|
||||||
|
<polygon points="0 0, 12 4, 0 8" fill={style.color} />
|
||||||
|
</marker>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
{/* Свечение для выделения */}
|
||||||
|
<filter id="glow" x="-50%" y="-50%" width="200%" height="200%">
|
||||||
|
<feGaussianBlur stdDeviation="2" result="coloredBlur"/>
|
||||||
|
<feMerge>
|
||||||
|
<feMergeNode in="coloredBlur"/>
|
||||||
|
<feMergeNode in="SourceGraphic"/>
|
||||||
|
</feMerge>
|
||||||
|
</filter>
|
||||||
|
</defs>
|
||||||
{/* Связи */}
|
{/* Связи */}
|
||||||
{connections.map((connection, index) => {
|
{connections.map((connection, index) => {
|
||||||
const fromServer = servers.find(s => s.ip === connection.from);
|
const fromServer = servers.find(s => s.ip === connection.from);
|
||||||
@@ -119,19 +157,27 @@ function GraphView({ servers, connections }) {
|
|||||||
|
|
||||||
const fromPos = getNodePosition(connection.from);
|
const fromPos = getNodePosition(connection.from);
|
||||||
const toPos = getNodePosition(connection.to);
|
const toPos = getNodePosition(connection.to);
|
||||||
|
const style = getTunnelStyle(connection.tunnelType);
|
||||||
|
const id = `${connection.from}-${connection.to}-${index}`;
|
||||||
|
const isHovered = hoveredLinkId === id;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<g key={`link-${index}`}>
|
<g key={`link-${index}`}
|
||||||
|
onMouseEnter={() => setHoveredLinkId(id)}
|
||||||
|
onMouseLeave={() => setHoveredLinkId(null)}
|
||||||
|
>
|
||||||
{/* Линия связи */}
|
{/* Линия связи */}
|
||||||
<line
|
<line
|
||||||
x1={fromPos.x}
|
x1={fromPos.x}
|
||||||
y1={fromPos.y}
|
y1={fromPos.y}
|
||||||
x2={toPos.x}
|
x2={toPos.x}
|
||||||
y2={toPos.y}
|
y2={toPos.y}
|
||||||
stroke="#94a3b8"
|
stroke={style.color}
|
||||||
strokeWidth="3"
|
strokeWidth={isHovered ? style.width + 1 : style.width}
|
||||||
strokeDasharray="5,5"
|
strokeDasharray={style.dash}
|
||||||
markerEnd="url(#arrowhead)"
|
markerEnd={`url(#arrow-${connection.tunnelType})`}
|
||||||
|
filter={isHovered ? 'url(#glow)' : undefined}
|
||||||
|
opacity={isHovered ? 1 : 0.9}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Подпись связи */}
|
{/* Подпись связи */}
|
||||||
@@ -141,8 +187,8 @@ function GraphView({ servers, connections }) {
|
|||||||
width="80"
|
width="80"
|
||||||
height="30"
|
height="30"
|
||||||
rx="15"
|
rx="15"
|
||||||
fill="white"
|
fill="#ffffff"
|
||||||
stroke="#e5e7eb"
|
stroke={style.color}
|
||||||
strokeWidth="1"
|
strokeWidth="1"
|
||||||
/>
|
/>
|
||||||
<text
|
<text
|
||||||
@@ -282,22 +328,7 @@ function GraphView({ servers, connections }) {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
{/* Определение стрелки */}
|
{/* Доп. определения добавлены выше */}
|
||||||
<defs>
|
|
||||||
<marker
|
|
||||||
id="arrowhead"
|
|
||||||
markerWidth="12"
|
|
||||||
markerHeight="8"
|
|
||||||
refX="10"
|
|
||||||
refY="4"
|
|
||||||
orient="auto"
|
|
||||||
>
|
|
||||||
<polygon
|
|
||||||
points="0 0, 12 4, 0 8"
|
|
||||||
fill="#94a3b8"
|
|
||||||
/>
|
|
||||||
</marker>
|
|
||||||
</defs>
|
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -700,11 +700,20 @@ function ServerManager() {
|
|||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="card w-100 mb-4">
|
<div className="card w-100 mb-4">
|
||||||
<div className="card-header">
|
<div className="card-header d-flex justify-content-between align-items-center">
|
||||||
<h3 className="card-title mb-0">Граф связей между серверами</h3>
|
<h3 className="card-title mb-0">Граф связей между серверами</h3>
|
||||||
|
<div className="text-muted small">Поддержка: GRE, IPSec, WireGuard, OpenVPN</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
<GraphView servers={servers} connections={connections} />
|
<GraphView servers={servers} connections={connections} />
|
||||||
|
<div className="mt-3">
|
||||||
|
<div className="d-flex align-items-center gap-3 flex-wrap">
|
||||||
|
<div className="d-flex align-items-center gap-2"><span style={{display:'inline-block',width:24,height:4,background:'#206bc4'}}></span><span className="text-muted small">GRE</span></div>
|
||||||
|
<div className="d-flex align-items-center gap-2"><span style={{display:'inline-block',width:24,height:4,background:'#f59f00',borderBottom:'2px dashed #f59f00'}}></span><span className="text-muted small">IPSec</span></div>
|
||||||
|
<div className="d-flex align-items-center gap-2"><span style={{display:'inline-block',width:24,height:4,background:'#2fb344'}}></span><span className="text-muted small">WireGuard</span></div>
|
||||||
|
<div className="d-flex align-items-center gap-2"><span style={{display:'inline-block',width:24,height:4,background:'#be4bdb',borderBottom:'2px dotted #be4bdb'}}></span><span className="text-muted small">OpenVPN</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="card w-100 mb-4">
|
<div className="card w-100 mb-4">
|
||||||
|
|||||||
Reference in New Issue
Block a user