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 @@