Build and push app
This commit is contained in:
parent
3ad6d53261
commit
74315fde64
@ -10,3 +10,4 @@ COPY . .
|
||||
|
||||
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
|
||||
EXPOSE 8000
|
||||
|
||||
|
||||
Binary file not shown.
73
frontend/package-lock.json
generated
73
frontend/package-lock.json
generated
@ -12,6 +12,7 @@
|
||||
"react": "^19.1.0",
|
||||
"react-confirm-alert": "^3.0.6",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-quill-new": "^3.4.6",
|
||||
"react-toastify": "^11.0.5",
|
||||
"reactflow": "^11.11.4"
|
||||
},
|
||||
@ -1988,12 +1989,22 @@
|
||||
"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": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
|
||||
"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": {
|
||||
"version": "2.1.0",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"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": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
@ -2361,6 +2388,11 @@
|
||||
"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": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
@ -2465,6 +2497,33 @@
|
||||
"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": {
|
||||
"version": "19.1.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "11.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-11.0.5.tgz",
|
||||
|
||||
@ -14,6 +14,7 @@
|
||||
"react": "^19.1.0",
|
||||
"react-confirm-alert": "^3.0.6",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-quill-new": "^3.4.6",
|
||||
"react-toastify": "^11.0.5",
|
||||
"reactflow": "^11.11.4"
|
||||
},
|
||||
|
||||
@ -16,14 +16,18 @@ import {
|
||||
listDiagrams,
|
||||
deleteDiagramByName,
|
||||
} from '../services/api';
|
||||
import ReactQuill from 'react-quill-new';
|
||||
import 'react-quill-new/dist/quill.snow.css';
|
||||
import CustomNode from './CustomNode';
|
||||
import RouterNode from './RouterNode';
|
||||
import TextNode from './TextNode';
|
||||
import IconSelector from './IconSelector';
|
||||
import { toast } from 'react-toastify';
|
||||
import RouterNode from './RouterNode';
|
||||
|
||||
const nodeTypes = {
|
||||
custom: CustomNode,
|
||||
router: RouterNode,
|
||||
text: TextNode,
|
||||
};
|
||||
|
||||
function Diagram() {
|
||||
@ -37,6 +41,8 @@ function Diagram() {
|
||||
const [selectedIcon, setSelectedIcon] = useState('');
|
||||
const [diagramName, setDiagramName] = useState(null);
|
||||
const [diagramList, setDiagramList] = useState([]);
|
||||
const [editingTextNode, setEditingTextNode] = useState(null);
|
||||
const [tempHtml, setTempHtml] = useState('');
|
||||
const isIframe = window.self !== window.top;
|
||||
|
||||
useEffect(() => {
|
||||
@ -68,7 +74,45 @@ function Diagram() {
|
||||
}
|
||||
try {
|
||||
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 }) => ({
|
||||
id, source, target, animated: !!animated
|
||||
}));
|
||||
@ -142,17 +186,55 @@ function Diagram() {
|
||||
const onNodeClick = (_, node) => setSelectedNode(node);
|
||||
|
||||
const handleNodeDoubleClick = (_, node) => {
|
||||
const newLabel = prompt('Enter new name:', node.data.label);
|
||||
if (newLabel !== null) {
|
||||
if (node.type === 'text') {
|
||||
setNodes((nds) =>
|
||||
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 () => {
|
||||
if (!diagramName || diagramName === 'default') {
|
||||
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={() => 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={handleDeleteEdge} style={{ color: 'white', background: selectedEdge ? '#b81a1a' : '#424040' }} disabled={!selectedEdge}>🗑️ Delete Edge</button>
|
||||
<button className="btn" onClick={handleDeleteDiagram} style={{ background: 'red' }}>🗑️ Delete Diagram</button>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user