infrastruktur karte
This commit is contained in:
@@ -1,8 +1,11 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const SVG_NS = 'http://www.w3.org/2000/svg';
|
||||
const DEFAULT_PLAN_SIZE = { width: 2000, height: 1000 };
|
||||
|
||||
const canvas = document.getElementById('floor-plan-canvas');
|
||||
const marker = document.getElementById('floor-plan-marker');
|
||||
const overlay = document.getElementById('floor-plan-overlay');
|
||||
const positionLabel = document.getElementById('floor-plan-position');
|
||||
if (!canvas || !marker) {
|
||||
if (!canvas || !overlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -14,8 +17,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
return;
|
||||
}
|
||||
|
||||
const markerWidth = Math.max(1, Number(canvas.dataset.markerWidth) || marker.offsetWidth);
|
||||
const markerHeight = Math.max(1, Number(canvas.dataset.markerHeight) || marker.offsetHeight);
|
||||
const markerWidth = Math.max(1, Number(canvas.dataset.markerWidth) || 10);
|
||||
const markerHeight = Math.max(1, Number(canvas.dataset.markerHeight) || 10);
|
||||
const markerType = canvas.dataset.markerType || '';
|
||||
const activeId = Number(canvas.dataset.activeId || 0);
|
||||
const panelReferences = JSON.parse(canvas.dataset.referencePanels || '[]');
|
||||
@@ -23,7 +26,27 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
|
||||
const clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
||||
|
||||
marker.classList.add('is-active');
|
||||
let markerX = 0;
|
||||
let markerY = 0;
|
||||
let dragging = false;
|
||||
let dragOffsetX = 0;
|
||||
let dragOffsetY = 0;
|
||||
|
||||
const activeMarker = document.createElementNS(SVG_NS, 'rect');
|
||||
activeMarker.classList.add('active-marker');
|
||||
if (markerType === 'patchpanel') {
|
||||
activeMarker.classList.add('panel-marker');
|
||||
} else {
|
||||
activeMarker.classList.add('outlet-marker');
|
||||
}
|
||||
activeMarker.setAttribute('width', String(markerWidth));
|
||||
activeMarker.setAttribute('height', String(markerHeight));
|
||||
overlay.appendChild(activeMarker);
|
||||
|
||||
const planSize = { ...DEFAULT_PLAN_SIZE };
|
||||
const updateOverlayViewBox = () => {
|
||||
overlay.setAttribute('viewBox', `0 0 ${planSize.width} ${planSize.height}`);
|
||||
};
|
||||
|
||||
const updatePositionLabel = (x, y) => {
|
||||
if (positionLabel) {
|
||||
@@ -31,81 +54,138 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
};
|
||||
|
||||
const paintActiveMarker = () => {
|
||||
activeMarker.setAttribute('x', String(Math.round(markerX)));
|
||||
activeMarker.setAttribute('y', String(Math.round(markerY)));
|
||||
};
|
||||
|
||||
const setMarkerPosition = (rawX, rawY) => {
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
const maxX = Math.max(0, rect.width - markerWidth);
|
||||
const maxY = Math.max(0, rect.height - markerHeight);
|
||||
const left = clamp(rawX, 0, maxX);
|
||||
const top = clamp(rawY, 0, maxY);
|
||||
marker.style.left = `${left}px`;
|
||||
marker.style.top = `${top}px`;
|
||||
xField.value = Math.round(left);
|
||||
yField.value = Math.round(top);
|
||||
updatePositionLabel(left, top);
|
||||
const maxX = Math.max(0, planSize.width - markerWidth);
|
||||
const maxY = Math.max(0, planSize.height - markerHeight);
|
||||
markerX = clamp(rawX, 0, maxX);
|
||||
markerY = clamp(rawY, 0, maxY);
|
||||
|
||||
paintActiveMarker();
|
||||
xField.value = Math.round(markerX);
|
||||
yField.value = Math.round(markerY);
|
||||
updatePositionLabel(markerX, markerY);
|
||||
};
|
||||
|
||||
const toOverlayPoint = (clientX, clientY) => {
|
||||
const pt = overlay.createSVGPoint();
|
||||
pt.x = clientX;
|
||||
pt.y = clientY;
|
||||
const ctm = overlay.getScreenCTM();
|
||||
if (!ctm) {
|
||||
return null;
|
||||
}
|
||||
const transformed = pt.matrixTransform(ctm.inverse());
|
||||
return { x: transformed.x, y: transformed.y };
|
||||
};
|
||||
|
||||
const updateFromInputs = () => {
|
||||
setMarkerPosition(Number(xField.value) || 0, Number(yField.value) || 0);
|
||||
};
|
||||
|
||||
updateFromInputs();
|
||||
|
||||
let dragging = false;
|
||||
let offsetX = 0;
|
||||
let offsetY = 0;
|
||||
|
||||
const startDrag = (clientX, clientY) => {
|
||||
const markerRect = marker.getBoundingClientRect();
|
||||
offsetX = clientX - markerRect.left;
|
||||
offsetY = clientY - markerRect.top;
|
||||
const clearReferenceMarkers = () => {
|
||||
overlay.querySelectorAll('.reference-marker').forEach((node) => node.remove());
|
||||
};
|
||||
|
||||
marker.addEventListener('pointerdown', (event) => {
|
||||
event.preventDefault();
|
||||
dragging = true;
|
||||
startDrag(event.clientX, event.clientY);
|
||||
marker.setPointerCapture(event.pointerId);
|
||||
});
|
||||
|
||||
marker.addEventListener('pointermove', (event) => {
|
||||
if (!dragging) {
|
||||
return;
|
||||
}
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
setMarkerPosition(event.clientX - rect.left - offsetX, event.clientY - rect.top - offsetY);
|
||||
});
|
||||
|
||||
const stopDrag = (event) => {
|
||||
if (!dragging) {
|
||||
return;
|
||||
}
|
||||
dragging = false;
|
||||
if (marker.hasPointerCapture(event.pointerId)) {
|
||||
marker.releasePointerCapture(event.pointerId);
|
||||
}
|
||||
const clearRoomHighlight = () => {
|
||||
overlay.querySelectorAll('.room-highlight').forEach((node) => node.remove());
|
||||
};
|
||||
|
||||
['pointerup', 'pointercancel', 'pointerleave'].forEach((evt) => {
|
||||
marker.addEventListener(evt, stopDrag);
|
||||
});
|
||||
const getNumericCoord = (value) => {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return null;
|
||||
}
|
||||
const num = Number(value);
|
||||
return Number.isFinite(num) ? num : null;
|
||||
};
|
||||
|
||||
canvas.addEventListener('pointerdown', (event) => {
|
||||
if (event.target !== canvas) {
|
||||
const appendReference = (entry, cssClass, width, height) => {
|
||||
const rawX = entry.x ?? entry.pos_x;
|
||||
const rawY = entry.y ?? entry.pos_y;
|
||||
const x = getNumericCoord(rawX);
|
||||
const y = getNumericCoord(rawY);
|
||||
if (x === null || y === null) {
|
||||
return;
|
||||
}
|
||||
const rect = canvas.getBoundingClientRect();
|
||||
setMarkerPosition(event.clientX - rect.left - markerWidth / 2, event.clientY - rect.top - markerHeight / 2);
|
||||
});
|
||||
|
||||
[xField, yField].forEach((input) => {
|
||||
input.addEventListener('input', () => {
|
||||
updateFromInputs();
|
||||
});
|
||||
});
|
||||
const ref = document.createElementNS(SVG_NS, 'rect');
|
||||
ref.classList.add('reference-marker', cssClass);
|
||||
ref.setAttribute('x', String(Math.round(x)));
|
||||
ref.setAttribute('y', String(Math.round(y)));
|
||||
ref.setAttribute('width', String(Math.max(1, Math.round(width))));
|
||||
ref.setAttribute('height', String(Math.max(1, Math.round(height))));
|
||||
if (entry.name) {
|
||||
ref.setAttribute('aria-label', String(entry.name));
|
||||
}
|
||||
overlay.insertBefore(ref, activeMarker);
|
||||
};
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
updateFromInputs();
|
||||
});
|
||||
const appendRoomHighlight = () => {
|
||||
if (!outletRoomSelect) {
|
||||
return;
|
||||
}
|
||||
const selectedRoomOption = outletRoomSelect.selectedOptions?.[0];
|
||||
if (!selectedRoomOption || !selectedRoomOption.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentFloorId = getCurrentFloorId();
|
||||
const roomFloorId = Number(selectedRoomOption.dataset.floorId || 0);
|
||||
if (!currentFloorId || roomFloorId !== currentFloorId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const polygonRaw = String(selectedRoomOption.dataset.roomPolygon || '').trim();
|
||||
if (polygonRaw) {
|
||||
try {
|
||||
const parsed = JSON.parse(polygonRaw);
|
||||
if (Array.isArray(parsed)) {
|
||||
const points = parsed
|
||||
.map((point) => ({
|
||||
x: Number(point && point.x),
|
||||
y: Number(point && point.y)
|
||||
}))
|
||||
.filter((point) => Number.isFinite(point.x) && Number.isFinite(point.y));
|
||||
|
||||
if (points.length >= 3) {
|
||||
const polygon = document.createElementNS(SVG_NS, 'polygon');
|
||||
polygon.classList.add('room-highlight');
|
||||
polygon.setAttribute(
|
||||
'points',
|
||||
points.map((point) => `${Math.round(point.x)},${Math.round(point.y)}`).join(' ')
|
||||
);
|
||||
overlay.insertBefore(polygon, activeMarker);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// ignore invalid room polygon json
|
||||
}
|
||||
}
|
||||
|
||||
const x = Number(selectedRoomOption.dataset.roomX || 0);
|
||||
const y = Number(selectedRoomOption.dataset.roomY || 0);
|
||||
const width = Number(selectedRoomOption.dataset.roomWidth || 0);
|
||||
const height = Number(selectedRoomOption.dataset.roomHeight || 0);
|
||||
if (!Number.isFinite(x) || !Number.isFinite(y) || !Number.isFinite(width) || !Number.isFinite(height)) {
|
||||
return;
|
||||
}
|
||||
if (width <= 0 || height <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const rect = document.createElementNS(SVG_NS, 'rect');
|
||||
rect.classList.add('room-highlight');
|
||||
rect.setAttribute('x', String(Math.round(x)));
|
||||
rect.setAttribute('y', String(Math.round(y)));
|
||||
rect.setAttribute('width', String(Math.round(width)));
|
||||
rect.setAttribute('height', String(Math.round(height)));
|
||||
overlay.insertBefore(rect, activeMarker);
|
||||
};
|
||||
|
||||
const panelLocationSelect = document.getElementById('panel-location-select');
|
||||
const panelBuildingSelect = document.getElementById('panel-building-select');
|
||||
@@ -129,26 +209,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
};
|
||||
|
||||
const renderReferenceMarkers = () => {
|
||||
canvas.querySelectorAll('.floor-plan-reference').forEach((node) => node.remove());
|
||||
clearRoomHighlight();
|
||||
clearReferenceMarkers();
|
||||
const currentFloorId = getCurrentFloorId();
|
||||
if (!currentFloorId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const appendReference = (entry, cssClass, width, height) => {
|
||||
const markerRef = document.createElement('div');
|
||||
markerRef.className = `floor-plan-reference ${cssClass}`;
|
||||
markerRef.style.left = `${Number(entry.x || entry.pos_x || 0)}px`;
|
||||
markerRef.style.top = `${Number(entry.y || entry.pos_y || 0)}px`;
|
||||
if (width > 0) {
|
||||
markerRef.style.width = `${width}px`;
|
||||
}
|
||||
if (height > 0) {
|
||||
markerRef.style.height = `${height}px`;
|
||||
}
|
||||
markerRef.title = entry.name || '';
|
||||
canvas.appendChild(markerRef);
|
||||
};
|
||||
appendRoomHighlight();
|
||||
|
||||
panelReferences.forEach((entry) => {
|
||||
if (Number(entry.floor_id) !== currentFloorId) {
|
||||
@@ -157,7 +224,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (markerType === 'patchpanel' && Number(entry.id) === activeId) {
|
||||
return;
|
||||
}
|
||||
appendReference(entry, 'panel-marker', Math.max(1, Number(entry.width) || 140), Math.max(1, Number(entry.height) || 40));
|
||||
appendReference(entry, 'panel-marker', Math.max(1, Number(entry.width) || 20), Math.max(1, Number(entry.height) || 5));
|
||||
});
|
||||
|
||||
outletReferences.forEach((entry) => {
|
||||
@@ -183,9 +250,13 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
if (svgUrl) {
|
||||
floorPlanSvg.src = svgUrl;
|
||||
floorPlanSvg.hidden = false;
|
||||
loadPlanDimensions(svgUrl);
|
||||
} else {
|
||||
floorPlanSvg.removeAttribute('src');
|
||||
floorPlanSvg.hidden = true;
|
||||
planSize.width = DEFAULT_PLAN_SIZE.width;
|
||||
planSize.height = DEFAULT_PLAN_SIZE.height;
|
||||
updateOverlayViewBox();
|
||||
}
|
||||
renderReferenceMarkers();
|
||||
};
|
||||
@@ -197,6 +268,57 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
|
||||
const loadPlanDimensions = async (svgUrl) => {
|
||||
if (!svgUrl) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(svgUrl, { credentials: 'same-origin' });
|
||||
if (!response.ok) {
|
||||
throw new Error('SVG not available');
|
||||
}
|
||||
const raw = await response.text();
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(raw, 'image/svg+xml');
|
||||
const root = doc.documentElement;
|
||||
if (!root || root.nodeName.toLowerCase() === 'parsererror') {
|
||||
throw new Error('Invalid SVG');
|
||||
}
|
||||
|
||||
const vb = String(root.getAttribute('viewBox') || '').trim();
|
||||
if (vb) {
|
||||
const parts = vb.split(/\s+/).map((value) => Number(value));
|
||||
if (parts.length === 4 && parts.every((value) => Number.isFinite(value))) {
|
||||
planSize.width = Math.max(1, parts[2]);
|
||||
planSize.height = Math.max(1, parts[3]);
|
||||
updateOverlayViewBox();
|
||||
renderReferenceMarkers();
|
||||
updateFromInputs();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const width = Number(root.getAttribute('width'));
|
||||
const height = Number(root.getAttribute('height'));
|
||||
if (Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0) {
|
||||
planSize.width = width;
|
||||
planSize.height = height;
|
||||
} else {
|
||||
planSize.width = DEFAULT_PLAN_SIZE.width;
|
||||
planSize.height = DEFAULT_PLAN_SIZE.height;
|
||||
}
|
||||
updateOverlayViewBox();
|
||||
renderReferenceMarkers();
|
||||
updateFromInputs();
|
||||
} catch (error) {
|
||||
planSize.width = DEFAULT_PLAN_SIZE.width;
|
||||
planSize.height = DEFAULT_PLAN_SIZE.height;
|
||||
updateOverlayViewBox();
|
||||
renderReferenceMarkers();
|
||||
updateFromInputs();
|
||||
}
|
||||
};
|
||||
|
||||
const updatePanelPlacementVisibility = () => {
|
||||
if (!panelFloorSelect || !panelPlacementFields || !panelFloorPlanGroup) {
|
||||
return;
|
||||
@@ -257,6 +379,65 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
}
|
||||
};
|
||||
|
||||
activeMarker.addEventListener('pointerdown', (event) => {
|
||||
event.preventDefault();
|
||||
dragging = true;
|
||||
const point = toOverlayPoint(event.clientX, event.clientY);
|
||||
if (!point) {
|
||||
return;
|
||||
}
|
||||
dragOffsetX = point.x - markerX;
|
||||
dragOffsetY = point.y - markerY;
|
||||
activeMarker.setPointerCapture(event.pointerId);
|
||||
});
|
||||
|
||||
activeMarker.addEventListener('pointermove', (event) => {
|
||||
if (!dragging) {
|
||||
return;
|
||||
}
|
||||
const point = toOverlayPoint(event.clientX, event.clientY);
|
||||
if (!point) {
|
||||
return;
|
||||
}
|
||||
setMarkerPosition(point.x - dragOffsetX, point.y - dragOffsetY);
|
||||
});
|
||||
|
||||
const stopDrag = (event) => {
|
||||
if (!dragging) {
|
||||
return;
|
||||
}
|
||||
dragging = false;
|
||||
if (activeMarker.hasPointerCapture(event.pointerId)) {
|
||||
activeMarker.releasePointerCapture(event.pointerId);
|
||||
}
|
||||
};
|
||||
|
||||
['pointerup', 'pointercancel', 'pointerleave'].forEach((evt) => {
|
||||
activeMarker.addEventListener(evt, stopDrag);
|
||||
});
|
||||
|
||||
overlay.addEventListener('pointerdown', (event) => {
|
||||
if (event.target !== overlay) {
|
||||
return;
|
||||
}
|
||||
const point = toOverlayPoint(event.clientX, event.clientY);
|
||||
if (!point) {
|
||||
return;
|
||||
}
|
||||
setMarkerPosition(point.x - markerWidth / 2, point.y - markerHeight / 2);
|
||||
});
|
||||
|
||||
[xField, yField].forEach((input) => {
|
||||
input.addEventListener('input', () => {
|
||||
updateFromInputs();
|
||||
});
|
||||
});
|
||||
|
||||
window.addEventListener('resize', () => {
|
||||
updateFromInputs();
|
||||
renderReferenceMarkers();
|
||||
});
|
||||
|
||||
if (panelLocationSelect) {
|
||||
panelLocationSelect.addEventListener('change', () => {
|
||||
filterBuildingOptions();
|
||||
@@ -283,6 +464,9 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
});
|
||||
}
|
||||
|
||||
updateOverlayViewBox();
|
||||
updateFromInputs();
|
||||
|
||||
if (panelLocationSelect) {
|
||||
filterBuildingOptions();
|
||||
filterFloorOptions();
|
||||
|
||||
148
app/assets/js/floor-infrastructure-list.js
Normal file
148
app/assets/js/floor-infrastructure-list.js
Normal file
@@ -0,0 +1,148 @@
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const SVG_NS = 'http://www.w3.org/2000/svg';
|
||||
const DEFAULT_PLAN_SIZE = { width: 2000, height: 1000 };
|
||||
|
||||
const filterForm = document.getElementById('infra-filter-form');
|
||||
const floorSelect = document.getElementById('infra-floor-select');
|
||||
if (filterForm && floorSelect) {
|
||||
floorSelect.addEventListener('change', () => {
|
||||
filterForm.submit();
|
||||
});
|
||||
}
|
||||
|
||||
const canvas = document.getElementById('infra-floor-canvas');
|
||||
const overlay = document.getElementById('infra-floor-overlay');
|
||||
const floorSvg = canvas ? canvas.querySelector('.infra-floor-svg') : null;
|
||||
if (!canvas || !overlay || !floorSvg) {
|
||||
return;
|
||||
}
|
||||
|
||||
const patchPanels = safeJsonParse(canvas.dataset.patchpanels);
|
||||
const outlets = safeJsonParse(canvas.dataset.outlets);
|
||||
const planSize = { ...DEFAULT_PLAN_SIZE };
|
||||
|
||||
const updateOverlayViewBox = () => {
|
||||
overlay.setAttribute('viewBox', `0 0 ${planSize.width} ${planSize.height}`);
|
||||
};
|
||||
|
||||
const createMarker = (entry, type) => {
|
||||
const x = Number(entry.x);
|
||||
const y = Number(entry.y);
|
||||
if (!Number.isFinite(x) || !Number.isFinite(y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const marker = document.createElementNS(SVG_NS, 'rect');
|
||||
marker.classList.add('infra-overlay-marker', type);
|
||||
marker.setAttribute('x', String(Math.round(x)));
|
||||
marker.setAttribute('y', String(Math.round(y)));
|
||||
|
||||
if (type === 'patchpanel') {
|
||||
marker.setAttribute('width', String(Math.max(1, Number(entry.width) || 20)));
|
||||
marker.setAttribute('height', String(Math.max(1, Number(entry.height) || 5)));
|
||||
} else {
|
||||
marker.setAttribute('width', '10');
|
||||
marker.setAttribute('height', '10');
|
||||
}
|
||||
|
||||
const tooltipLines = buildTooltipLines(entry, type);
|
||||
if (tooltipLines.length > 0) {
|
||||
const titleNode = document.createElementNS(SVG_NS, 'title');
|
||||
titleNode.textContent = tooltipLines.join('\n');
|
||||
marker.appendChild(titleNode);
|
||||
}
|
||||
|
||||
overlay.appendChild(marker);
|
||||
};
|
||||
|
||||
patchPanels.forEach((entry) => createMarker(entry, 'patchpanel'));
|
||||
outlets.forEach((entry) => createMarker(entry, 'outlet'));
|
||||
|
||||
const loadPlanDimensions = async (svgUrl) => {
|
||||
if (!svgUrl) {
|
||||
updateOverlayViewBox();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const response = await fetch(svgUrl, { credentials: 'same-origin' });
|
||||
if (!response.ok) {
|
||||
throw new Error('SVG not available');
|
||||
}
|
||||
const raw = await response.text();
|
||||
const parser = new DOMParser();
|
||||
const doc = parser.parseFromString(raw, 'image/svg+xml');
|
||||
const root = doc.documentElement;
|
||||
if (!root || root.nodeName.toLowerCase() === 'parsererror') {
|
||||
throw new Error('Invalid SVG');
|
||||
}
|
||||
|
||||
const vb = String(root.getAttribute('viewBox') || '').trim();
|
||||
if (vb) {
|
||||
const parts = vb.split(/\s+/).map((value) => Number(value));
|
||||
if (parts.length === 4 && parts.every((value) => Number.isFinite(value))) {
|
||||
planSize.width = Math.max(1, parts[2]);
|
||||
planSize.height = Math.max(1, parts[3]);
|
||||
updateOverlayViewBox();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const width = Number(root.getAttribute('width'));
|
||||
const height = Number(root.getAttribute('height'));
|
||||
if (Number.isFinite(width) && Number.isFinite(height) && width > 0 && height > 0) {
|
||||
planSize.width = width;
|
||||
planSize.height = height;
|
||||
} else {
|
||||
planSize.width = DEFAULT_PLAN_SIZE.width;
|
||||
planSize.height = DEFAULT_PLAN_SIZE.height;
|
||||
}
|
||||
updateOverlayViewBox();
|
||||
} catch (error) {
|
||||
planSize.width = DEFAULT_PLAN_SIZE.width;
|
||||
planSize.height = DEFAULT_PLAN_SIZE.height;
|
||||
updateOverlayViewBox();
|
||||
}
|
||||
};
|
||||
|
||||
loadPlanDimensions(floorSvg.getAttribute('src') || '');
|
||||
});
|
||||
|
||||
function safeJsonParse(value) {
|
||||
if (!value) {
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(value);
|
||||
return Array.isArray(parsed) ? parsed : [];
|
||||
} catch (error) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
function buildTooltipLines(entry, type) {
|
||||
if (type === 'patchpanel') {
|
||||
const lines = [
|
||||
`Patchpanel: ${entry.name || '-'}`,
|
||||
`Ports: ${Number(entry.port_count) || 0}`,
|
||||
`Position: ${Number(entry.x) || 0} x ${Number(entry.y) || 0}`
|
||||
];
|
||||
if (entry.comment) {
|
||||
lines.push(`Kommentar: ${entry.comment}`);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
const roomLabel = `${entry.room_name || '-'}${entry.room_number ? ` (${entry.room_number})` : ''}`;
|
||||
const lines = [
|
||||
`Wandbuchse: ${entry.name || '-'}`,
|
||||
`Raum: ${roomLabel}`,
|
||||
`Position: ${Number(entry.x) || 0} x ${Number(entry.y) || 0}`
|
||||
];
|
||||
if (entry.port_names) {
|
||||
lines.push(`Ports: ${entry.port_names}`);
|
||||
}
|
||||
if (entry.comment) {
|
||||
lines.push(`Kommentar: ${entry.comment}`);
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
Reference in New Issue
Block a user