Skip to content

Commit 0937f5d

Browse files
authored
Merge pull request #26 from drizuid/node-edit
add support to edit nodes and fix box dragging/resizing
2 parents 1696fbc + ff20dec commit 0937f5d

1 file changed

Lines changed: 230 additions & 23 deletions

File tree

static/js/app.js

Lines changed: 230 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,11 @@ const NetworkMonitor = () => {
256256
// AVG latency stuff
257257
const [deviceStats, setDeviceStats] = useState({});
258258

259+
// Node editing stuff
260+
const [editingDevice, setEditingDevice] = useState(null);
261+
const [editingBox, setEditingBox] = useState(null);
262+
const [editForm, setEditForm] = useState({ name: '', ip: '', type: '', color: '' });
263+
259264
const devicesRef = React.useRef(devices);
260265

261266
// Load configuration on mount
@@ -568,6 +573,53 @@ const NetworkMonitor = () => {
568573
}
569574
};
570575

576+
const startEditDevice = (device) => {
577+
setEditingDevice(device.id);
578+
setEditForm({
579+
name: device.name,
580+
ip: device.ip,
581+
type: device.type
582+
});
583+
};
584+
585+
const saveDeviceEdit = () => {
586+
if (editForm.name && editForm.ip) {
587+
setDevices(prev => prev.map(d =>
588+
d.id === editingDevice
589+
? { ...d, name: editForm.name, ip: editForm.ip, type: editForm.type }
590+
: d
591+
));
592+
setEditingDevice(null);
593+
setEditForm({ name: '', ip: '', type: '', color: '' });
594+
}
595+
};
596+
597+
const cancelEdit = () => {
598+
setEditingDevice(null);
599+
setEditingBox(null);
600+
setEditForm({ name: '', ip: '', type: '', color: '' });
601+
};
602+
603+
const startEditBox = (box) => {
604+
setEditingBox(box.id);
605+
setEditForm({
606+
name: box.name,
607+
color: box.color
608+
});
609+
};
610+
611+
const saveBoxEdit = () => {
612+
if (editForm.name) {
613+
setBoxes(prev => prev.map(b =>
614+
b.id === editingBox
615+
? { ...b, name: editForm.name, color: editForm.color }
616+
: b
617+
));
618+
setEditingBox(null);
619+
setEditForm({ name: '', ip: '', type: '', color: '' });
620+
}
621+
};
622+
571623
const addDevice = () => {
572624
if (newDevice.name && newDevice.ip) {
573625
setDevices(prev => [...prev, {
@@ -817,6 +869,109 @@ showAddBox && React.createElement(
817869
}, 'Cancel')
818870
)
819871
),
872+
(editingDevice || editingBox) && React.createElement(
873+
'div',
874+
{
875+
className: 'fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50',
876+
onClick: cancelEdit
877+
},
878+
React.createElement(
879+
'div',
880+
{
881+
className: 'bg-gray-800 rounded-lg p-6 border border-gray-700 min-w-96',
882+
onClick: (e) => e.stopPropagation()
883+
},
884+
React.createElement('h2', { className: 'text-xl font-bold text-white mb-4' },
885+
editingDevice ? 'Edit Device' : 'Edit Box'
886+
),
887+
React.createElement(
888+
'div',
889+
{ className: 'space-y-4' },
890+
React.createElement(
891+
'div',
892+
{},
893+
React.createElement('label', { className: 'text-gray-400 text-sm block mb-1' },
894+
editingDevice ? 'Device Name' : 'Box Name'
895+
),
896+
React.createElement('input', {
897+
type: 'text',
898+
value: editForm.name,
899+
onChange: (e) => setEditForm({...editForm, name: e.target.value}),
900+
className: 'w-full px-3 py-2 bg-gray-700 text-white rounded border border-gray-600 focus:outline-none focus:border-blue-500'
901+
})
902+
),
903+
editingDevice && React.createElement(
904+
'div',
905+
{},
906+
React.createElement('label', { className: 'text-gray-400 text-sm block mb-1' }, 'IP Address'),
907+
React.createElement('input', {
908+
type: 'text',
909+
value: editForm.ip,
910+
onChange: (e) => setEditForm({...editForm, ip: e.target.value}),
911+
className: 'w-full px-3 py-2 bg-gray-700 text-white rounded border border-gray-600 focus:outline-none focus:border-blue-500'
912+
})
913+
),
914+
editingDevice && React.createElement(
915+
'div',
916+
{},
917+
React.createElement('label', { className: 'text-gray-400 text-sm block mb-1' }, 'Type'),
918+
React.createElement(
919+
'select',
920+
{
921+
value: editForm.type,
922+
onChange: (e) => setEditForm({...editForm, type: e.target.value}),
923+
className: 'w-full px-3 py-2 bg-gray-700 text-white rounded border border-gray-600 focus:outline-none focus:border-blue-500'
924+
},
925+
React.createElement('option', { value: 'router' }, 'Router'),
926+
React.createElement('option', { value: 'server' }, 'Server'),
927+
React.createElement('option', { value: 'wifi' }, 'WiFi AP'),
928+
React.createElement('option', { value: 'storage' }, 'Storage'),
929+
React.createElement('option', { value: 'monitor' }, 'End-User Device'),
930+
React.createElement('option', { value: 'switch' }, 'Switch')
931+
)
932+
),
933+
editingBox && React.createElement(
934+
'div',
935+
{},
936+
React.createElement('label', { className: 'text-gray-400 text-sm block mb-1' }, 'Color'),
937+
React.createElement(
938+
'select',
939+
{
940+
value: editForm.color,
941+
onChange: (e) => setEditForm({...editForm, color: e.target.value}),
942+
className: 'w-full px-3 py-2 bg-gray-700 text-white rounded border border-gray-600 focus:outline-none focus:border-blue-500'
943+
},
944+
React.createElement('option', { value: 'blue' }, 'Blue'),
945+
React.createElement('option', { value: 'green' }, 'Green'),
946+
React.createElement('option', { value: 'purple' }, 'Purple'),
947+
React.createElement('option', { value: 'red' }, 'Red'),
948+
React.createElement('option', { value: 'yellow' }, 'Yellow'),
949+
React.createElement('option', { value: 'orange' }, 'Orange')
950+
)
951+
),
952+
React.createElement(
953+
'div',
954+
{ className: 'flex gap-2 mt-6' },
955+
React.createElement(
956+
'button',
957+
{
958+
onClick: editingDevice ? saveDeviceEdit : saveBoxEdit,
959+
className: 'flex-1 bg-green-600 hover:bg-green-700 text-white px-4 py-2 rounded'
960+
},
961+
'Save'
962+
),
963+
React.createElement(
964+
'button',
965+
{
966+
onClick: cancelEdit,
967+
className: 'flex-1 bg-gray-700 hover:bg-gray-600 text-white px-4 py-2 rounded'
968+
},
969+
'Cancel'
970+
)
971+
)
972+
)
973+
)
974+
),
820975
React.createElement(
821976
'div',
822977
{
@@ -970,57 +1125,83 @@ showAddBox && React.createElement(
9701125
'div',
9711126
{
9721127
key: box.id,
973-
className: `absolute border-2 ${boxColors[box.color]} bg-transparent rounded-lg group`,
1128+
className: 'absolute rounded-lg group',
9741129
style: {
9751130
left: `${box.x}px`,
9761131
top: `${box.y}px`,
9771132
width: `${box.width}px`,
9781133
height: `${box.height}px`,
9791134
zIndex: 2,
9801135
pointerEvents: 'none' // fix because i couldn't hover the connection lines while they were behind a box
981-
},
982-
onMouseDown: (e) => { //yah i broke dragging the box with the connection line fix.. smh
983-
const rect = e.currentTarget.getBoundingClientRect();
984-
const borderWidth = 2;
985-
const clickX = e.clientX - rect.left;
986-
const clickY = e.clientY - rect.top;
987-
const onBorder = clickX < borderWidth || clickX > rect.width - borderWidth ||
988-
clickY < borderWidth || clickY > rect.height - borderWidth;
989-
990-
if (onBorder) {
991-
handleBoxMouseDown(e, box)
992-
}
993-
}
1136+
}
9941137
},
9951138
React.createElement('div', {
1139+
className: `absolute top-0 left-0 right-0 border-t-2 ${boxColors[box.color]}`,
9961140
style: {
997-
position: 'absolute',
998-
top: '2px',
999-
left: '2px',
1000-
right: '2px',
1001-
bottom: '2px',
1002-
pointerEvents: 'none'
1003-
}
1141+
height: '8px',
1142+
pointerEvents: 'none',
1143+
cursor: 'move'
1144+
},
1145+
onMouseDown: (e) => handleBoxMouseDown(e, box)
10041146
}),
1147+
React.createElement('div', {
1148+
className: `absolute bottom-0 left-0 right-0 border-b-2 ${boxColors[box.color]}`,
1149+
style: {
1150+
height: '8px',
1151+
pointerEvents: 'auto',
1152+
cursor: 'move'
1153+
},
1154+
onMouseDown: (e) => handleBoxMouseDown(e, box)
1155+
}),
1156+
React.createElement('div', {
1157+
className: `absolute top-0 bottom-0 left-0 border-l-2 ${boxColors[box.color]}`,
1158+
style: {
1159+
width: '8px',
1160+
pointerEvents: 'auto',
1161+
cursor: 'move'
1162+
},
1163+
onMouseDown: (e) => handleBoxMouseDown(e, box)
1164+
}),
1165+
React.createElement('div', {
1166+
className: `absolute top-0 bottom-0 right-0 border-r-2 ${boxColors[box.color]}`,
1167+
style: {
1168+
width: '8px',
1169+
pointerEvents: 'auto',
1170+
cursor: 'move'
1171+
},
1172+
onMouseDown: (e) => handleBoxMouseDown(e, box)
1173+
}),
1174+
React.createElement('div', {
1175+
className: `absolute inset-0 border-2 ${boxColors[box.color]} rounded-lg`,
1176+
style: { pointerEvents: 'none' }
1177+
}),
10051178
React.createElement(
10061179
'div',
10071180
{
1008-
className: 'absolute -top-6 left-0 bg-gray-800 px-2 py-1 rounded text-white text-sm font-semibold' },
1181+
className: 'absolute -top-6 left-0 bg-gray-800 px-2 py-1 rounded text-white text-sm font-semibold cursor-pointer hover:bg-gray-700',
1182+
style: { pointerEvents: 'auto' },
1183+
onClick: (e) => {
1184+
e.stopPropagation();
1185+
startEditBox(box);
1186+
}
1187+
},
10091188
box.name
10101189
),
10111190
React.createElement(
10121191
'button',
10131192
{
10141193
onClick: () => removeBox(box.id),
10151194
className: 'absolute -top-2 -right-2 bg-red-600 hover:bg-red-700 rounded-full p-1 opacity-0 group-hover:opacity-100 transition-opacity z-10',
1016-
onMouseDown: (e) => e.stopPropagation()
1195+
onMouseDown: (e) => e.stopPropagation(),
1196+
style: { pointerEvents: 'auto' }
10171197
},
10181198
React.createElement(XIcon, { size: 12 })
10191199
),
10201200
React.createElement(
10211201
'div',
10221202
{
10231203
className: 'absolute bottom-0 right-0 w-4 h-4 cursor-nwse-resize',
1204+
style: { pointerEvents: 'auto' },
10241205
onMouseDown: (e) => {
10251206
e.stopPropagation();
10261207
setResizingBox(box.id);
@@ -1065,6 +1246,32 @@ showAddBox && React.createElement(
10651246
},
10661247
React.createElement(XIcon, { size: 12 })
10671248
),
1249+
React.createElement(
1250+
'button',
1251+
{
1252+
onClick: (e) => {
1253+
e.stopPropagation();
1254+
startEditDevice(device);
1255+
},
1256+
className: 'absolute -top-2 -right-12 bg-blue-600 hover:bg-blue-700 rounded-full p-1 opacity-0 group-hover:opacity-100 transition-opacity z-10',
1257+
onMouseDown: (e) => e.stopPropagation()
1258+
},
1259+
React.createElement('svg', {
1260+
xmlns: 'http://www.w3.org/2000/svg',
1261+
fill: 'none',
1262+
viewBox: '0 0 24 24',
1263+
strokeWidth: 2,
1264+
stroke: 'currentColor',
1265+
width: 12,
1266+
height: 12
1267+
},
1268+
React.createElement('path', {
1269+
strokeLinecap: 'round',
1270+
strokeLinejoin: 'round',
1271+
d: 'M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10'
1272+
})
1273+
)
1274+
),
10681275
React.createElement(
10691276
'div',
10701277
{ className: 'flex flex-col items-center' },

0 commit comments

Comments
 (0)