diff --git a/frontend/assets/js/app.js b/frontend/assets/js/app.js index 053c35b..79fb7c7 100644 --- a/frontend/assets/js/app.js +++ b/frontend/assets/js/app.js @@ -26,6 +26,8 @@ let selectRect = null; let nextShapeZ = 0; let copyBuffer = null; +let editingNodeId = null; +let editingShapeId = null; document.addEventListener('DOMContentLoaded', () => { canvas = document.getElementById('networkCanvas'); @@ -275,7 +277,10 @@ function renderShapeList() { ${esc(s.label) || (s.shape_type === 'rectangle' ? 'Box' : 'Ellipse')} - ${s.shape_type} +
+ ${s.shape_type} + +
`).join(''); @@ -301,7 +306,10 @@ function selectNode(id, add = false) {
Status: ${n.status}
Group: ${n.group_name}
${selectedNodeIds.length > 1 ? `+${selectedNodeIds.length - 1} more selected` : ''} - +
+ + +
`; } @@ -417,12 +425,55 @@ async function saveNode() { pos_y: Math.random() * canvas.height * 0.6 + canvas.height * 0.2 - panY }; if (!data.label) return alert('Label required'); - await apiFetch('nodes', { method: 'POST', body: JSON.stringify(data) }); + if (editingNodeId) { + const updates = { + label: data.label, + ip_address: data.ip_address, + node_type: data.node_type, + status: data.status, + group_name: data.group_name + }; + await apiFetch(`nodes/${editingNodeId}`, { method: 'PUT', body: JSON.stringify(updates) }); + editingNodeId = null; + } else { + await apiFetch('nodes', { method: 'POST', body: JSON.stringify(data) }); + } bootstrap.Modal.getInstance(document.getElementById('nodeModal')).hide(); document.getElementById('nodeForm').reset(); + document.getElementById('nodeModalLabel').textContent = 'Add Network Node'; + document.getElementById('saveNode').innerHTML = ' Add Node'; loadNetworkData(); } +function editSelectedNode(id) { + const n = nodes.find(x => x.id == id); + if (!n) return; + editingNodeId = id; + document.getElementById('nodeLabel').value = n.label; + document.getElementById('nodeIp').value = n.ip_address || ''; + document.getElementById('nodeType').value = n.node_type; + document.getElementById('nodeStatus').value = n.status; + document.getElementById('nodeGroup').value = n.group_name; + document.getElementById('nodeModalLabel').textContent = 'Edit Network Node'; + document.getElementById('saveNode').innerHTML = ' Update Node'; + new bootstrap.Modal(document.getElementById('nodeModal')).show(); +} + +function editSelectedShape(id) { + const s = shapes.find(x => x.id == id); + if (!s) return; + editingShapeId = id; + document.getElementById('shapeLabel').value = s.label; + document.getElementById('shapeType').value = s.shape_type; + document.getElementById('shapeColor').value = s.color; + document.getElementById('shapeBorderColor').value = s.border_color; + document.getElementById('shapeOpacity').value = s.opacity; + document.getElementById('opacityVal').textContent = s.opacity; + document.getElementById('shapeModalLabel').textContent = 'Edit Shape'; + document.getElementById('saveShape').innerHTML = ' Update Shape'; + new bootstrap.Modal(document.getElementById('shapeModal')).show(); +} + async function saveLink() { const data = { source_id: document.getElementById('linkSource').value, @@ -441,18 +492,27 @@ async function saveShape() { const data = { label: document.getElementById('shapeLabel').value, shape_type: document.getElementById('shapeType').value, - pos_x: canvas.width / 2 - 100 - panX, - pos_y: canvas.height / 2 - 75 - panY, - width: 200, - height: 150, color: document.getElementById('shapeColor').value, border_color: document.getElementById('shapeBorderColor').value, opacity: parseFloat(document.getElementById('shapeOpacity').value), - z_index: nextShapeZ++ }; - await apiFetch('shapes', { method: 'POST', body: JSON.stringify(data) }); + if (editingShapeId) { + const s = shapes.find(x => x.id == editingShapeId); + data.pos_x = s.pos_x; data.pos_y = s.pos_y; data.width = s.width; data.height = s.height; data.z_index = s.z_index; + await apiFetch(`shapes/${editingShapeId}`, { method: 'PUT', body: JSON.stringify(data) }); + editingShapeId = null; + } else { + data.pos_x = canvas.width / 2 - 100 - panX; + data.pos_y = canvas.height / 2 - 75 - panY; + data.width = 200; + data.height = 150; + data.z_index = nextShapeZ++; + await apiFetch('shapes', { method: 'POST', body: JSON.stringify(data) }); + } bootstrap.Modal.getInstance(document.getElementById('shapeModal')).hide(); document.getElementById('shapeForm').reset(); + document.getElementById('shapeModalLabel').textContent = 'Add Shape'; + document.getElementById('saveShape').innerHTML = ' Add Shape'; loadNetworkData(); } diff --git a/frontend/index.html b/frontend/index.html index 2ee12b4..f486692 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -193,7 +193,7 @@