feat: Add debug logging and enhance flag emoji handling in GraphView for improved debugging and country code support
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m42s

This commit is contained in:
2025-08-08 16:50:21 +07:00
parent eaa88e2897
commit 8ea6f9526e
+46 -13
View File
@@ -3,6 +3,10 @@ import * as d3 from 'd3';
import { IconZoomIn, IconZoomOut, IconMaximize, IconMinimize } from '@tabler/icons-react';
function GraphView({ servers, connections }) {
// Debug helper (enable/disable via window.GRAPH_DEBUG = true/false in console)
const isDebug = typeof window !== 'undefined' ? (window.GRAPH_DEBUG !== false) : true;
const dbg = (...args) => { if (isDebug && typeof console !== 'undefined') console.log('[GraphView]', ...args); };
const [nodePositions, setNodePositions] = useState({});
const [draggedNode, setDraggedNode] = useState(null);
const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
@@ -80,16 +84,34 @@ function GraphView({ servers, connections }) {
.translateExtent([[-10000, -10000], [10000, 10000]])
.on('zoom', (event) => {
g.attr('transform', event.transform);
// dbg('onZoom', { k: event.transform.k, x: event.transform.x, y: event.transform.y });
});
svg.call(zoomRef.current);
dbg('zoom initialized', { width, height });
}, []);
// Контролы масштабирования
const zoomBy = (factor) => {
if (!zoomRef.current || !svgRef.current) return;
const svg = d3.select(svgRef.current);
// Используем встроенную математику d3.zoom с учётом extent
svg.transition().duration(220).call(zoomRef.current.scaleBy, factor);
const svgEl = svgRef.current;
const current = d3.zoomTransform(svgEl);
const cx = svgEl.clientWidth / 2;
const cy = svgEl.clientHeight / 2;
dbg('zoomBy click', { factor, current: { k: current.k, x: current.x, y: current.y }, center: { cx, cy } });
const next = d3.zoomIdentity
.translate(current.x, current.y)
.scale(current.k)
.translate(cx, cy)
.scale(factor)
.translate(-cx, -cy);
d3.select(svgEl)
.transition()
.duration(220)
.call(zoomRef.current.transform, next)
.on('end', () => {
const nt = d3.zoomTransform(svgEl);
dbg('zoomBy end', { next: { k: nt.k, x: nt.x, y: nt.y } });
});
};
const fitToView = () => {
@@ -127,6 +149,7 @@ function GraphView({ servers, connections }) {
const toggleFullscreen = () => {
const el = containerRef.current;
if (!el) return;
dbg('toggleFullscreen', { isFullscreen: Boolean(document.fullscreenElement) });
if (!document.fullscreenElement) {
el.requestFullscreen?.();
} else {
@@ -236,11 +259,13 @@ function GraphView({ servers, connections }) {
return map[tunnelType] || { color: '#667382', dash: '5,5', width: 2 };
};
// Флаг страны по ISO-2 (emoji). Для не-ISO-2 вернём код как есть
const getFlagEmoji = (countryCode) => {
if (!countryCode) return '';
const cc = String(countryCode).trim().toUpperCase();
if (cc.length !== 2) return cc; // например, SWE
// Флаг страны по ISO-2 с поддержкой нестандартных кодов из списка (SWE->SE)
const getFlagEmoji = (code) => {
if (!code) return '';
const map = { SWE: 'SE', UK: 'GB' };
const ccRaw = String(code).trim().toUpperCase();
const cc = (map[ccRaw] || ccRaw).slice(0, 2);
if (cc.length !== 2) return ccRaw;
return cc.replace(/./g, (ch) => String.fromCodePoint(127397 + ch.charCodeAt()));
};
@@ -429,21 +454,29 @@ function GraphView({ servers, connections }) {
y={badgeY}
rx={8}
ry={8}
width={36}
height={18}
width={52}
height={20}
fill="#fff"
stroke={countryColor}
/>
<text
x={badgeX + 18}
y={badgeY + 12}
textAnchor="middle"
x={badgeX + 10}
y={badgeY + 13}
fontSize="14"
dominantBaseline="middle"
style={{ fontFamily: 'Segoe UI Emoji, Noto Color Emoji, Apple Color Emoji, "Twemoji Mozilla", "EmojiOne Color", sans-serif' }}
>
{flag}
</text>
<text
x={badgeX + 28}
y={badgeY + 13}
fontSize="11"
fill="#475569"
dominantBaseline="middle"
>
{String(server.country).toUpperCase().slice(0,3)}
</text>
</g>
);
})()}