Merge pull request 'develop' (#6) from develop into master

Reviewed-on: #6
This commit is contained in:
dvirlabs 2025-07-01 23:36:39 +00:00
commit 73dffb2893
6 changed files with 95 additions and 11 deletions

View File

@ -1,4 +1,25 @@
{
"nodes": [],
"nodes": [
{
"id": "1",
"type": "custom",
"data": {
"label": "Node 1",
"icon": "https://s3.dvirlabs.com/lab-icons/dev-tools/argocd.svg"
},
"position": {
"x": 68.5849844537563,
"y": 225.18693121594464
},
"width": 82,
"height": 82,
"selected": false,
"positionAbsolute": {
"x": 68.5849844537563,
"y": 225.18693121594464
},
"dragging": false
}
],
"edges": []
}

View File

@ -10,6 +10,7 @@
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-toastify": "^11.0.5",
"reactflow": "^11.11.4"
},
"devDependencies": {
@ -1589,6 +1590,14 @@
"resolved": "https://registry.npmjs.org/classcat/-/classcat-5.0.5.tgz",
"integrity": "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w=="
},
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"engines": {
"node": ">=6"
}
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -2434,6 +2443,18 @@
"react": "^19.1.0"
}
},
"node_modules/react-toastify": {
"version": "11.0.5",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
"integrity": "sha512-EpqHBGvnSTtHYhCPLxML05NLY2ZX0JURbAdNYa6BUkk+amz4wbKBQvoKQAB0ardvSarUBuY4Q4s1sluAzZwkmA==",
"dependencies": {
"clsx": "^2.1.1"
},
"peerDependencies": {
"react": "^18 || ^19",
"react-dom": "^18 || ^19"
}
},
"node_modules/reactflow": {
"version": "11.11.4",
"resolved": "https://registry.npmjs.org/reactflow/-/reactflow-11.11.4.tgz",

View File

@ -12,6 +12,7 @@
"dependencies": {
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-toastify": "^11.0.5",
"reactflow": "^11.11.4"
},
"devDependencies": {

View File

@ -1,7 +1,14 @@
import Diagram from './components/Diagram';
import { ToastContainer } from 'react-toastify';
import 'react-toastify/dist/ReactToastify.css';
function App() {
return <Diagram />;
return (
<>
<Diagram />
<ToastContainer position="bottom-right" autoClose={3000} />
</>
);
}
export default App;

View File

@ -3,31 +3,61 @@ import { Handle, Position } from 'reactflow';
function CustomNode({ data }) {
const icon = data.icon || 'https://s3.dvirlabs.com/lab-icons/default.svg';
const handleSize = 5;
const visibleHandleStyle = {
background: '#1976d2',
width: handleSize,
height: handleSize,
borderRadius: '50%',
zIndex: 2,
};
const invisibleHandleStyle = {
background: 'transparent',
width: handleSize,
height: handleSize,
borderRadius: '50%',
zIndex: 1,
};
return (
<div style={{
background: 'transparent',
border: '2px solid #1976d2',
borderRadius: 6,
padding: 10,
textAlign: 'center',
width: 80,
height: 40,
height: 80,
boxShadow: '0 2px 6px rgba(0,0,0,0.2)',
position: 'relative',
}}>
{/* Handles - חובה */}
<Handle type="target" position={Position.Top} />
<Handle type="source" position={Position.Bottom} />
{/* === TOP === */}
<Handle id="source-top" type="source" position={Position.Top} style={{ ...visibleHandleStyle, top: -6 }} />
<Handle id="target-top" type="target" position={Position.Top} style={{ ...invisibleHandleStyle, top: -6 }} />
{/* === BOTTOM === */}
<Handle id="source-bottom" type="source" position={Position.Bottom} style={{ ...visibleHandleStyle, bottom: -6 }} />
<Handle id="target-bottom" type="target" position={Position.Bottom} style={{ ...invisibleHandleStyle, bottom: -6 }} />
{/* === LEFT === */}
<Handle id="source-left" type="source" position={Position.Left} style={{ ...visibleHandleStyle, left: -6, top: 30 }} />
<Handle id="target-left" type="target" position={Position.Left} style={{ ...invisibleHandleStyle, left: -6, top: 30 }} />
{/* === RIGHT === */}
<Handle id="source-right" type="source" position={Position.Right} style={{ ...visibleHandleStyle, right: -6, top: 30 }} />
<Handle id="target-right" type="target" position={Position.Right} style={{ ...invisibleHandleStyle, right: -6, top: 30 }} />
{/* === ICON + LABEL === */}
<img
src={icon}
alt={data.label}
onError={(e) => {
e.target.src = 'https://s3.dvirlabs.com/lab-icons/default.svg';
}}
style={{ width: 40, height: 40, marginBottom: 6 }}
style={{ width: 40, height: 40, marginTop: 6 }}
/>
<div style={{ fontWeight: 'bold' }}>{data.label}</div>
<div style={{ fontWeight: 'bold', fontSize: 12 }}>{data.label}</div>
</div>
);
}

View File

@ -1,4 +1,3 @@
// src/components/Diagram.jsx
import { useEffect, useState, useCallback } from 'react';
import ReactFlow, {
addEdge,
@ -11,6 +10,7 @@ import 'reactflow/dist/style.css';
import { fetchDiagram, saveDiagram } from '../services/api';
import CustomNode from './CustomNode';
import IconSelector from './IconSelector';
import { toast } from 'react-toastify';
const nodeTypes = {
custom: CustomNode,
@ -45,7 +45,7 @@ function Diagram() {
animated: !!animated,
}));
await saveDiagram({ nodes, edges: cleanedEdges });
alert('Diagram saved!');
toast.success('✅ Diagram saved!');
};
const handleAddNode = () => {
@ -65,6 +65,7 @@ function Diagram() {
};
setNodes((nds) => [...nds, newNode]);
toast.success(`🟢 Node "${label}" added`);
setShowForm(false);
setNewLabel('');
setSelectedIcon('');
@ -72,8 +73,10 @@ function Diagram() {
const handleDeleteNode = () => {
if (!selectedNode) return;
setNodes((nds) => nds.filter((n) => n.id !== selectedNode.id));
setEdges((eds) => eds.filter((e) => e.source !== selectedNode.id && e.target !== selectedNode.id));
toast.error(`🗑️ Node "${selectedNode.data.label}" deleted`);
setSelectedNode(null);
};
@ -90,6 +93,7 @@ function Diagram() {
n.id === node.id ? { ...n, data: { ...n.data, label: newLabel } } : n
)
);
toast.info(`✏️ Node renamed to "${newLabel}"`);
}
};