diff --git a/components/process-flow/ProcessFlowCanvas.vue b/components/process-flow/ProcessFlowCanvas.vue index 4eb570e..8e577e8 100644 --- a/components/process-flow/ProcessFlowCanvas.vue +++ b/components/process-flow/ProcessFlowCanvas.vue @@ -493,13 +493,21 @@ const onDrop = (event) => { id: `${componentData.type}-${Date.now()}`, type: componentData.type, position, + label: componentData.label, data: { ...componentData.data, label: componentData.label } }; + // Add to Vue Flow for immediate visual feedback addNodes([newNode]); + + // IMPORTANT: Also emit the node to be added to the process store + // This ensures the node persists in the application state and can be saved + emit('nodesChange', [{ type: 'add', id: newNode.id, item: newNode }], nodes.value); + + console.log('🎯 Node dropped and added:', newNode.type, newNode.id); } catch (error) { console.error('Error handling drop:', error); } diff --git a/pages/process-builder/index.vue b/pages/process-builder/index.vue index 2669203..9c64cf6 100644 --- a/pages/process-builder/index.vue +++ b/pages/process-builder/index.vue @@ -380,6 +380,21 @@ const handleConditionUpdate = (conditions) => { const onNodesChange = (changes, currentNodes) => { if (!changes || !currentNodes) return; + // Handle node additions (from drag & drop) + const addedNodes = changes.filter(change => change.type === 'add' && change.item); + if (addedNodes.length > 0) { + addedNodes.forEach(change => { + // Check if node already exists in store to prevent duplicates + const existingNode = processStore.currentProcess?.nodes?.find(n => n.id === change.item.id); + if (!existingNode) { + console.log('🔥 Adding node to store from canvas:', change.item); + processStore.addNode(change.item); + } else { + console.log('⚠️ Node already exists in store, skipping:', change.item.id); + } + }); + } + // Handle node removals const removedNodes = changes .filter(change => change.type === 'remove') diff --git a/server/api/process/[id].put.js b/server/api/process/[id].put.js index 996bea9..2bf10f4 100644 --- a/server/api/process/[id].put.js +++ b/server/api/process/[id].put.js @@ -85,6 +85,15 @@ export default defineEventHandler(async (event) => { } }); + // Debug logging to see what we received + console.log('🔥 Server received process update:', { + processId, + nodeCount: (body.nodes || []).length, + edgeCount: (body.edges || []).length, + nodes: (body.nodes || []).map(n => ({ id: n.id, type: n.type, label: n.label })), + edges: (body.edges || []).map(e => ({ id: e.id, source: e.source, target: e.target })) + }); + // Prepare process definition const processDefinition = { nodes: body.nodes || [], diff --git a/stores/processBuilder.js b/stores/processBuilder.js index 0717e5b..3e95e5d 100644 --- a/stores/processBuilder.js +++ b/stores/processBuilder.js @@ -133,7 +133,7 @@ export const useProcessBuilderStore = defineStore('processBuilder', { if (nodes.length === 0 && edges.length > 0) { const nodeMap = new Map(); - // Extract unique nodes from edge sourceNode and targetNode + // First try to extract unique nodes from edge sourceNode and targetNode if they exist edges.forEach(edge => { if (edge.sourceNode) { nodeMap.set(edge.sourceNode.id, { @@ -156,6 +156,68 @@ export const useProcessBuilderStore = defineStore('processBuilder', { } }); + // If no nodes were extracted from embedded data, create placeholder nodes from edge references + if (nodeMap.size === 0) { + const nodeIds = new Set(); + edges.forEach(edge => { + if (edge.source) nodeIds.add(edge.source); + if (edge.target) nodeIds.add(edge.target); + }); + + let nodePosition = { x: 100, y: 100 }; + const spacing = 200; + + nodeIds.forEach((nodeId, index) => { + // Determine node type from ID prefix + let nodeType = 'task'; // default + let label = nodeId; + let icon = 'schedule'; + + if (nodeId.includes('start-')) { + nodeType = 'start'; + label = 'Start Point'; + icon = 'play_circle_filled'; + } else if (nodeId.includes('end-')) { + nodeType = 'end'; + label = 'End Point'; + icon = 'stop_circle'; + } else if (nodeId.includes('form-')) { + nodeType = 'form'; + label = 'Form'; + icon = 'description'; + } else if (nodeId.includes('api-')) { + nodeType = 'api'; + label = 'API Call'; + icon = 'api'; + } else if (nodeId.includes('gateway-')) { + nodeType = 'gateway'; + label = 'Decision Point'; + icon = 'call_split'; + } else if (nodeId.includes('script-')) { + nodeType = 'script'; + label = 'Script'; + icon = 'code'; + } + + nodeMap.set(nodeId, { + id: nodeId, + type: nodeType, + label: label, + position: { + x: nodePosition.x + (index % 3) * spacing, + y: nodePosition.y + Math.floor(index / 3) * spacing + }, + data: { + label: label, + description: `Recovered ${nodeType} node`, + icon: icon + } + }); + }); + + console.warn('Process had edges but no nodes. Created placeholder nodes from edge references:', Array.from(nodeIds)); + } + // Convert to array nodes = Array.from(nodeMap.values()); @@ -259,6 +321,15 @@ export const useProcessBuilderStore = defineStore('processBuilder', { // Status changes should only happen through explicit publish/unpublish actions }; + // Debug logging to see what we're actually sending + console.log('💾 Saving process data:', { + processId: this.currentProcess.id, + nodeCount: processData.nodes.length, + edgeCount: processData.edges.length, + nodes: processData.nodes.map(n => ({ id: n.id, type: n.type, label: n.label })), + edges: processData.edges.map(e => ({ id: e.id, source: e.source, target: e.target })) + }); + const response = await $fetch(`/api/process/${this.currentProcess.id}`, { method: 'PUT', body: processData @@ -488,6 +559,13 @@ export const useProcessBuilderStore = defineStore('processBuilder', { addNode(node) { if (!this.currentProcess) return; + // Check if node already exists to prevent duplicates + const existingNode = this.currentProcess.nodes.find(n => n.id === node.id); + if (existingNode) { + console.warn('Node already exists in store:', node.id); + return existingNode; + } + const newNode = { id: node.id || uuidv4(), type: node.type,