Build and push app

This commit is contained in:
dvirlabs 2026-03-24 08:37:03 +02:00
parent 3ad6d53261
commit 74315fde64
5 changed files with 196 additions and 6 deletions

View File

@ -10,3 +10,4 @@ COPY . .
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"] CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
EXPOSE 8000 EXPOSE 8000

View File

@ -12,6 +12,7 @@
"react": "^19.1.0", "react": "^19.1.0",
"react-confirm-alert": "^3.0.6", "react-confirm-alert": "^3.0.6",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-quill-new": "^3.4.6",
"react-toastify": "^11.0.5", "react-toastify": "^11.0.5",
"reactflow": "^11.11.4" "reactflow": "^11.11.4"
}, },
@ -1988,12 +1989,22 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
},
"node_modules/fast-deep-equal": { "node_modules/fast-deep-equal": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true "dev": true
}, },
"node_modules/fast-diff": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz",
"integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw=="
},
"node_modules/fast-json-stable-stringify": { "node_modules/fast-json-stable-stringify": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@ -2247,6 +2258,22 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/lodash-es": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
},
"node_modules/lodash.clonedeep": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
"integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ=="
},
"node_modules/lodash.isequal": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
"integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==",
"deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead."
},
"node_modules/lodash.merge": { "node_modules/lodash.merge": {
"version": "4.6.2", "version": "4.6.2",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
@ -2361,6 +2388,11 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/parchment": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz",
"integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A=="
},
"node_modules/parent-module": { "node_modules/parent-module": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@ -2465,6 +2497,33 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/quill": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz",
"integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==",
"dependencies": {
"eventemitter3": "^5.0.1",
"lodash-es": "^4.17.21",
"parchment": "^3.0.0",
"quill-delta": "^5.1.0"
},
"engines": {
"npm": ">=8.2.3"
}
},
"node_modules/quill-delta": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz",
"integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==",
"dependencies": {
"fast-diff": "^1.3.0",
"lodash.clonedeep": "^4.5.0",
"lodash.isequal": "^4.5.0"
},
"engines": {
"node": ">= 12.0.0"
}
},
"node_modules/react": { "node_modules/react": {
"version": "19.1.0", "version": "19.1.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
@ -2498,6 +2557,20 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
}, },
"node_modules/react-quill-new": {
"version": "3.4.6",
"resolved": "https://registry.npmjs.org/react-quill-new/-/react-quill-new-3.4.6.tgz",
"integrity": "sha512-S2kEAwoKRo+xUIAEpb94fwiPe2QU3FmwIfQ+7Lkchf+izPa2nRu1mr4i4QxyVYg8TjHDryDUiOEYZuFEV45QFA==",
"dependencies": {
"lodash-es": "^4.17.21",
"quill": "~2.0.2"
},
"peerDependencies": {
"quill-delta": "^5.1.0",
"react": "^16 || ^17 || ^18 || ^19",
"react-dom": "^16 || ^17 || ^18 || ^19"
}
},
"node_modules/react-toastify": { "node_modules/react-toastify": {
"version": "11.0.5", "version": "11.0.5",
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",

View File

@ -14,6 +14,7 @@
"react": "^19.1.0", "react": "^19.1.0",
"react-confirm-alert": "^3.0.6", "react-confirm-alert": "^3.0.6",
"react-dom": "^19.1.0", "react-dom": "^19.1.0",
"react-quill-new": "^3.4.6",
"react-toastify": "^11.0.5", "react-toastify": "^11.0.5",
"reactflow": "^11.11.4" "reactflow": "^11.11.4"
}, },

View File

@ -16,14 +16,18 @@ import {
listDiagrams, listDiagrams,
deleteDiagramByName, deleteDiagramByName,
} from '../services/api'; } from '../services/api';
import ReactQuill from 'react-quill-new';
import 'react-quill-new/dist/quill.snow.css';
import CustomNode from './CustomNode'; import CustomNode from './CustomNode';
import RouterNode from './RouterNode';
import TextNode from './TextNode';
import IconSelector from './IconSelector'; import IconSelector from './IconSelector';
import { toast } from 'react-toastify'; import { toast } from 'react-toastify';
import RouterNode from './RouterNode';
const nodeTypes = { const nodeTypes = {
custom: CustomNode, custom: CustomNode,
router: RouterNode, router: RouterNode,
text: TextNode,
}; };
function Diagram() { function Diagram() {
@ -37,6 +41,8 @@ function Diagram() {
const [selectedIcon, setSelectedIcon] = useState(''); const [selectedIcon, setSelectedIcon] = useState('');
const [diagramName, setDiagramName] = useState(null); const [diagramName, setDiagramName] = useState(null);
const [diagramList, setDiagramList] = useState([]); const [diagramList, setDiagramList] = useState([]);
const [editingTextNode, setEditingTextNode] = useState(null);
const [tempHtml, setTempHtml] = useState('');
const isIframe = window.self !== window.top; const isIframe = window.self !== window.top;
useEffect(() => { useEffect(() => {
@ -68,7 +74,45 @@ function Diagram() {
} }
try { try {
const data = await fetchDiagramByName(diagramName); const data = await fetchDiagramByName(diagramName);
setNodes(data.nodes || []); const enhancedNodes = (data.nodes || []).map((n) => {
if (n.type === 'text') {
return {
...n,
draggable: true, // ADD THIS HERE
data: {
...(n.data || {}),
editing: false,
onChange: (val) =>
setNodes((nds) =>
nds.map((node) =>
node.id === n.id ? { ...node, data: { ...node.data, value: val } } : node
)
),
onSave: () =>
setNodes((nds) =>
nds.map((node) =>
node.id === n.id
? {
...node,
draggable: true, // RE-ENABLE ON SAVE
data: { ...node.data, editing: false },
}
: node
)
),
},
};
}
return {
...n,
draggable: true // optionally apply for all node types too
};
});
setNodes(enhancedNodes);
const sanitizedEdges = (data.edges || []).map(({ id, source, target, animated }) => ({ const sanitizedEdges = (data.edges || []).map(({ id, source, target, animated }) => ({
id, source, target, animated: !!animated id, source, target, animated: !!animated
})); }));
@ -142,17 +186,55 @@ function Diagram() {
const onNodeClick = (_, node) => setSelectedNode(node); const onNodeClick = (_, node) => setSelectedNode(node);
const handleNodeDoubleClick = (_, node) => { const handleNodeDoubleClick = (_, node) => {
const newLabel = prompt('Enter new name:', node.data.label); if (node.type === 'text') {
if (newLabel !== null) {
setNodes((nds) => setNodes((nds) =>
nds.map((n) => nds.map((n) =>
n.id === node.id ? { ...n, data: { ...n.data, label: newLabel } } : n n.id === node.id
? {
...n,
draggable: false, // <-- disable dragging
data: {
...n.data,
editing: true,
onChange: (val) =>
setNodes((prev) =>
prev.map((x) =>
x.id === n.id ? { ...x, data: { ...x.data, value: val } } : x
)
),
onSave: () =>
setNodes((prev) =>
prev.map((x) =>
x.id === n.id
? {
...x,
draggable: true, // <-- re-enable dragging on save
data: { ...x.data, editing: false },
}
: x
)
),
},
}
: n
) )
); );
toast.info(`✏️ Node renamed to "${newLabel}"`); } else {
const newLabel = prompt('Enter new name:', node.data.label);
if (newLabel !== null) {
setNodes((nds) =>
nds.map((n) =>
n.id === node.id ? { ...n, data: { ...n.data, label: newLabel } } : n
)
);
toast.info(`✏️ Node renamed to "${newLabel}"`);
}
} }
}; };
const handleDeleteDiagram = async () => { const handleDeleteDiagram = async () => {
if (!diagramName || diagramName === 'default') { if (!diagramName || diagramName === 'default') {
toast.warn("❌ Cannot delete 'default' diagram or nothing selected."); toast.warn("❌ Cannot delete 'default' diagram or nothing selected.");
@ -258,6 +340,39 @@ function Diagram() {
<button className="btn" onClick={handleSave} style={{ background: 'green' }}>💾 Save</button> <button className="btn" onClick={handleSave} style={{ background: 'green' }}>💾 Save</button>
)} )}
<button className="btn" onClick={() => setShowForm(true)} style={{ background: 'blue' }}> Add Node</button> <button className="btn" onClick={() => setShowForm(true)} style={{ background: 'blue' }}> Add Node</button>
<button
className="btn"
onClick={() => {
const id = String(nodes.length + 1);
const newNode = {
id,
type: 'text',
position: { x: 400, y: 300 },
data: {
value: '',
editing: true,
onChange: (val) =>
setNodes((nds) =>
nds.map((n) =>
n.id === id ? { ...n, data: { ...n.data, value: val } } : n
)
),
onSave: () =>
setNodes((nds) =>
nds.map((n) =>
n.id === id ? { ...n, data: { ...n.data, editing: false } } : n
)
),
},
};
setNodes((nds) => [...nds, newNode]);
toast.success(`📝 Text node added`);
}}
style={{ background: '#845ec2' }}
>
Add Text
</button>
<button className="btn" onClick={handleDeleteNode} style={{ color: 'white', background: selectedNode ? '#b81a1a' : '#424040' }} disabled={!selectedNode}>🗑 Delete Node</button> <button className="btn" onClick={handleDeleteNode} style={{ color: 'white', background: selectedNode ? '#b81a1a' : '#424040' }} disabled={!selectedNode}>🗑 Delete Node</button>
<button className="btn" onClick={handleDeleteEdge} style={{ color: 'white', background: selectedEdge ? '#b81a1a' : '#424040' }} disabled={!selectedEdge}>🗑 Delete Edge</button> <button className="btn" onClick={handleDeleteEdge} style={{ color: 'white', background: selectedEdge ? '#b81a1a' : '#424040' }} disabled={!selectedEdge}>🗑 Delete Edge</button>
<button className="btn" onClick={handleDeleteDiagram} style={{ background: 'red' }}>🗑 Delete Diagram</button> <button className="btn" onClick={handleDeleteDiagram} style={{ background: 'red' }}>🗑 Delete Diagram</button>