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

This commit is contained in:
2025-08-08 14:55:23 +07:00
parent 68e9d5be7d
commit 0e81f36200
2 changed files with 64 additions and 24 deletions
+54 -23
View File
@@ -4,6 +4,7 @@ function GraphView({ servers, connections }) {
const [nodePositions, setNodePositions] = useState({});
const [draggedNode, setDraggedNode] = useState(null);
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
const [hoveredLinkId, setHoveredLinkId] = useState(null);
const svgRef = useRef(null);
if (servers.length === 0) {
@@ -98,6 +99,17 @@ function GraphView({ servers, connections }) {
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 (
<div style={{ width: '100%', height: '500px', border: '1px solid #e5e7eb', borderRadius: '8px', overflow: 'hidden', background: '#f8fafc' }}>
<svg
@@ -110,6 +122,32 @@ function GraphView({ servers, connections }) {
onMouseLeave={handleMouseUp}
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) => {
const fromServer = servers.find(s => s.ip === connection.from);
@@ -119,19 +157,27 @@ function GraphView({ servers, connections }) {
const fromPos = getNodePosition(connection.from);
const toPos = getNodePosition(connection.to);
const style = getTunnelStyle(connection.tunnelType);
const id = `${connection.from}-${connection.to}-${index}`;
const isHovered = hoveredLinkId === id;
return (
<g key={`link-${index}`}>
<g key={`link-${index}`}
onMouseEnter={() => setHoveredLinkId(id)}
onMouseLeave={() => setHoveredLinkId(null)}
>
{/* Линия связи */}
<line
x1={fromPos.x}
y1={fromPos.y}
x2={toPos.x}
y2={toPos.y}
stroke="#94a3b8"
strokeWidth="3"
strokeDasharray="5,5"
markerEnd="url(#arrowhead)"
stroke={style.color}
strokeWidth={isHovered ? style.width + 1 : style.width}
strokeDasharray={style.dash}
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"
height="30"
rx="15"
fill="white"
stroke="#e5e7eb"
fill="#ffffff"
stroke={style.color}
strokeWidth="1"
/>
<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>
</div>
);
+10 -1
View File
@@ -700,11 +700,20 @@ function ServerManager() {
/>
<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>
<div className="text-muted small">Поддержка: GRE, IPSec, WireGuard, OpenVPN</div>
</div>
<div className="card-body">
<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 className="card w-100 mb-4">