diff --git a/JSON STRUCTURES.md b/JSON STRUCTURES.md new file mode 100644 index 0000000..3189a21 --- /dev/null +++ b/JSON STRUCTURES.md @@ -0,0 +1,2 @@ +Email Input : + diff --git a/apps/web/app/lib/api.ts b/apps/web/app/lib/api.ts index 93f13b4..0953af7 100644 --- a/apps/web/app/lib/api.ts +++ b/apps/web/app/lib/api.ts @@ -6,6 +6,15 @@ import z from "zod"; import { getCredentials } from "../workflow/lib/config"; // Wrap all API calls in async functions and make sure to set withCredentials: true and add Content-Type header. export const api = { + user: { + get: async () => { + return await axios.get(`${BACKEND_URL}/user/workflows`, + { + withCredentials: true, + headers: { "Content-Type": "application/json" }, + }) + } + }, workflows: { create: async (name: string, Config: any) => await axios.post( diff --git a/apps/web/app/workflows/[id]/components/ConfigModal.tsx b/apps/web/app/workflows/[id]/components/ConfigModal.tsx index 9247e95..61018da 100644 --- a/apps/web/app/workflows/[id]/components/ConfigModal.tsx +++ b/apps/web/app/workflows/[id]/components/ConfigModal.tsx @@ -24,7 +24,7 @@ export default function ConfigModal({ const [config, setConfig] = useState>({}); const [loading, setLoading] = useState(false); const userId = useAppSelector((state) => state.user.userId) as string; - + // console.log("This is the credential Data from config from backend" , config); // Fetch credentials with hook based on node config (google, etc) if appropriate let credType: string | null = null; if (selectedNode) { diff --git a/apps/web/app/workflows/[id]/page.tsx b/apps/web/app/workflows/[id]/page.tsx index 9156602..683be30 100644 --- a/apps/web/app/workflows/[id]/page.tsx +++ b/apps/web/app/workflows/[id]/page.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useEffect, act } from "react"; +import { useState, useEffect } from "react"; import { useParams } from "next/navigation"; import { ReactFlow, @@ -35,7 +35,7 @@ export default function WorkflowCanvas() { icon: "➕", isPlaceholder: true, nodeType: "trigger", - onConfigure: () => setTriggerOpen(true), // Opens sidebar! + onConfigure: () => setTriggerOpen(true), }, }, ]); @@ -45,79 +45,105 @@ export default function WorkflowCanvas() { const [actionOpen, setActionOpen] = useState(false); const [configOpen, setConfigOpen] = useState(false); const [selectedNode, setSelectedNode] = useState(null); + const [error, setError] = useState(null); - const [error, setError] = useState("'"); const nodeTypes = { customNode: BaseNode, }; + + // Safe default position - reused everywhere below + const DEFAULT_TRIGGER_POSITION = { x: 250, y: 50 }; + const DEFAULT_ACTION_POSITION = { x: 500, y: 200 }; + + // Helper to ensure node has a valid position (defensive) + function ensurePosition(pos: any, fallback = { x: 0, y: 0 }) { + if (!pos || typeof pos.x !== "number" || typeof pos.y !== "number") { + return fallback; + } + return pos; + } + useEffect(() => { const loadWorkflows = async () => { try { const workflows = await api.workflows.get(workflowId); - console.log("This is the New Triggering trigger Data is",workflows.data.Data.Trigger.AvailableTriggerID); - - // 1. SAFEGUARD: Default to empty arrays to prevent crashes - const dbNodes = workflows.data.Data.nodes || []; - const dbEdges = workflows.data.Data.Edges || []; - const Trigger = workflows.data.Data.Trigger; - console.log("This is the trigger Data is",workflows.data.Data); - - console.log("The Data of Trigger is",Trigger) - // 2. CREATE TRIGGER NODE - // We assume the trigger is always the starting point + + // Defensive: Default to empty arrays if not present + const dbNodes = Array.isArray(workflows?.data?.Data?.nodes) + ? workflows.data.Data.nodes + : []; + const dbEdges = Array.isArray(workflows?.data?.Data?.Edges) + ? workflows.data.Data.Edges + : []; + const Trigger = workflows?.data?.Data?.Trigger; + + if (!Trigger) { + setError("No trigger found in workflow data, so start with selecting the trigger for the workflow"); + return; + } + + // Ensure trigger position + const triggerPosition = ensurePosition( + Trigger?.config?.position, + DEFAULT_TRIGGER_POSITION + ); + const triggerNode = { id: Trigger.id, type: "customNode", - // Default to top center if no position exists - position: Trigger.position || { x: 250, y: 50 }, + position: triggerPosition, data: { label: Trigger.name || Trigger.data?.label || "Trigger", - icon: Trigger.data?.icon || "⚡", // Lightning icon for trigger + icon: Trigger.data?.icon || "⚡", nodeType: "trigger", isConfigured: true, - onConfigure: () => handleNodeConfigure({ - id: Trigger.id, - // ... pass your trigger config props here - }) + onConfigure: () => + handleNodeConfigure({ + id: Trigger.id, + }), }, }; - if (!Trigger) { - + console.error("No trigger found in workflow data"); - return; - } - - // 3. TRANSFORM ACTION NODES + + // 3. Transform action nodes, ensuring position property is always valid const transformedNodes = dbNodes.map((node: any) => ({ id: node.id, type: "customNode", - position: node.position || { x: 0, y: 0 }, + position: ensurePosition(node?.position, { + x: triggerPosition.x + 350, + y: triggerPosition.y + 150, + }), data: { label: node.data?.label || node.name || "Unknown", icon: node.data?.icon || "⚙️", nodeType: "action", - // ... rest of your data mapping - onConfigure: () => handleNodeConfigure({ - id: node.id, - name: node.data?.label || node.Name, - type: "action", - actionType: node.AvailableNodeId - }) - } + onConfigure: () => + handleNodeConfigure({ + id: node.id, + name: node.data?.label || node.Name, + type: "action", + actionType: node.AvailableNodeId, + }), + }, })); - - // 4. CALCULATE PLACEHOLDER POSITION - // Find the "last" node to attach the placeholder to. - // If we have actions, use the last action. If not, use the trigger. - const lastNode = transformedNodes.length > 0 - ? transformedNodes[transformedNodes.length - 1] - : triggerNode; - - // Position placeholder 150 pixels below the last node - const placeholderPosition = { - x: lastNode.position.x, - y: lastNode.position.y + 150 + + // 4. Calculate placeholder position: use last action node, fallback to trigger + const lastNode = + transformedNodes.length > 0 + ? transformedNodes[transformedNodes.length - 1] + : triggerNode; + + const lastPosition = ensurePosition( + lastNode?.position, + transformedNodes.length > 0 + ? { x: triggerPosition.x + 350, y: triggerPosition.y + 150 } + : triggerPosition + ); + + const placeholderPosition = { + x: lastPosition.x + 550, + y: lastPosition.y, }; - + const actionPlaceholder = { id: `action-placeholder-${Date.now()}`, type: "customNode", @@ -130,271 +156,62 @@ export default function WorkflowCanvas() { onConfigure: () => setActionOpen(true), }, }; - - // 5. COMBINE NODES + + // 5. Combine nodes const finalNodes = [triggerNode, ...transformedNodes, actionPlaceholder]; - - // 6. MANAGE EDGES - // If we have DB edges, use them. - // If not, we should auto-connect Trigger -> First Node -> Placeholder - let finalEdges = [...dbEdges]; - - // Auto-connect Placeholder to the last node (visual guide) + + // 6. Manage edges + let finalEdges = Array.isArray(dbEdges) ? [...dbEdges] : []; + const placeholderEdge = { id: `e-${lastNode.id}-${actionPlaceholder.id}`, source: lastNode.id, target: actionPlaceholder.id, - type: 'default', // or your custom edge type - animated: true, // Make it dashed/animated to indicate it's temporary + type: "default", + animated: true, }; - - // Ensure Trigger is connected to first action if DB edges are empty & actions exist + if (dbEdges.length === 0 && transformedNodes.length > 0) { - const triggerEdge = { - id: `e-${triggerNode.id}-${transformedNodes[0].id}`, - source: triggerNode.id, - target: transformedNodes[0].id, - type: 'default' - }; - finalEdges.push(triggerEdge); + const triggerEdge = { + id: `e-${triggerNode.id}-${transformedNodes[0].id}`, + source: triggerNode.id, + target: transformedNodes[0].id, + type: "default", + }; + finalEdges.push(triggerEdge); } - + finalEdges.push(placeholderEdge); - + setNodes(finalNodes); setEdges(finalEdges); - - } catch (error) { - console.error("Failed to load workflow:", error); + setError(null); + } catch (err: any) { + console.error("Failed to load workflow:", err); + setError( + err?.message ?? + "Failed to load workflow. Please check your connection or reload the page." + ); } }; - + loadWorkflows(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [workflowId]); - - // useEffect(() => { - // const loadWorkflows = async () => { - // const workflows = await api.workflows.get(workflowId); - // console.log("This is the Data from workflow form DB",workflows.data.Data) - // const dbNodes = workflows.data.Data.nodes; - // const Trigger = workflows.data.Data.trigger; - // console.log("The Trigger is",Trigger) - // const dbEdges = workflows.data.Data.Edges || []; - // console.log("These are the DbNodes ",dbNodes) - // console.log("These are the DB Edges ",dbEdges) - // const transformedNodes = dbNodes.map((node: any) => ({ - // id: node.id, - // type: "customNode", // ⚠️ This was missing! - // position: node.position || { x: 0, y: 0 }, - // data: { - // label: node.data?.label || node.name || "Unknown", - // icon: node.data?.icon || "⚙️", - // nodeType: node.data?.nodeType || (node.TriggerId ? "trigger" : "action"), - // isPlaceholder: node.data?.isPlaceholder || false, - // isConfigured: node.data?.isConfigured || false, - // config: node.data?.config || node.Config || {}, - - // // ⚠️ CRITICAL: Re-attach callbacks - // onConfigure: () => handleNodeConfigure({ - // id: node.id, - // name: node.data?.label || node.Name, - // type: node.data?.nodeType || (node.TriggerId ? "trigger" : "action"), - // actionType: node.AvailableNodeId || node.AvailableTriggerID - // }) - // } - // })); - // const actionPlaceholder = { - // id: `action-placeholder-${Date.now()}`, - // type: "customNode", - // position: { x: 550, y: 200 }, - // data: { - // label: "Add Action", - // icon: "➕", - // isPlaceholder: true, - // nodeType: "action", - // config: {}, - // onConfigure: () => setActionOpen(true), - // }, - // }; - - // console.log("This Data after Tranforming",transformedNodes) - // setNodes([...transformedNodes, actionPlaceholder]); - // setEdges(dbEdges); - // } - - // loadWorkflows(); - // }, [workflowId]) - - - - // useEffect(() => { - // const loadWorkflows = async () => { - // const workflows = await api.workflows.get(workflowId); - // const dbNodes = workflows.data.Data.nodes; - // // Fix: Normalize dbEdges to always be an array - // let dbEdges = workflows.data.Data.edges; - // if (!Array.isArray(dbEdges)) { - // // Try alternative key (if backend provides Edges sometimes) - // dbEdges = workflows.data.Data.Edges; - // } - // if (!Array.isArray(dbEdges)) { - // dbEdges = []; - // } - - // // Get the trigger (from the "Trigger" key in backend response) - // const trigger = workflows.data.Data.Trigger; - - // // Transform trigger to a node if it exists and not already a node - // let triggerNode = null; - // if (trigger) { - // triggerNode = { - // id: trigger.id || "trigger-node", // fallback in case trigger has no id - // type: "customNode", - // position: trigger.config?.position || { x: 250, y: 50 }, - // data: { - // label: trigger.name || trigger.Name || "Trigger", - // icon: "⚡", - // nodeType: "trigger", - // isPlaceholder: false, - // isConfigured: true, - // config: trigger.config || trigger.Config || {}, - // onConfigure: () => handleNodeConfigure({ - // id: trigger.id, - // name: trigger.name || trigger.Name, - // type: "trigger", - // actionType: trigger.AvailableTriggerID // triggers have this, not actions - // }), - // } - // }; - // } - - // // Transform database nodes to React Flow format, excluding any possible trigger node - // const transformedNodes = dbNodes - // .filter((node: any) => !node.TriggerId) // trigger node will come separately - // .map((node: any) => ({ - // id: node.id, - // type: "customNode", - // position: node.position || node.Config?.position || { x: 0, y: 0 }, - // data: { - // label: node.Name || node.data?.label || "Unknown", - // icon: node.data?.icon || "⚙️", - // nodeType: node.TriggerId ? "trigger" : "action", - // isPlaceholder: false, // Loaded nodes are never placeholders - // isConfigured: node.data?.isConfigured || false, - // config: node.Config || node.data?.config || {}, - // onConfigure: () => handleNodeConfigure({ - // id: node.id, - // name: node.Name || node.data?.label, - // type: node.TriggerId ? "trigger" : "action", - // }) - // } - // })); - - // // Combine trigger node (if present) and transformed nodes - // let allNodes = []; - // if (triggerNode) { - // allNodes.push(triggerNode); - // } - // allNodes = [...allNodes, ...transformedNodes]; - - // // Find action nodes for logic - // const realActionNodes = allNodes.filter((n: any) => n.data.nodeType === "action"); - - // // Find (now explicit) trigger node ref - // const triggerNodeRef = allNodes.find((n: any) => n.data.nodeType === "trigger"); - // let actionPlaceholder: any; - - // if (realActionNodes.length === 0) { - // // No real actions: place after trigger - // actionPlaceholder = { - // id: "action-placeholder-end", - // type: "customNode", - // position: triggerNodeRef - // ? { x: triggerNodeRef.position.x + 300, y: triggerNodeRef.position.y + 150 } - // : { x: 550, y: 200 }, - // data: { - // label: "Add Action", - // icon: "➕", - // isPlaceholder: true, - // nodeType: "action", - // onConfigure: () => setActionOpen(true), - // } - // }; - // } else { - // // Place after last real action - // const lastAction = realActionNodes[realActionNodes.length - 1]; - // actionPlaceholder = { - // id: "action-placeholder-end", - // type: "customNode", - // position: { - // x: lastAction.position.x + 200, - // y: lastAction.position.y + 150 - // }, - // data: { - // label: "Add Action", - // icon: "➕", - // isPlaceholder: true, - // nodeType: "action", - // onConfigure: () => setActionOpen(true), - // } - // }; - // } - - // // Build up-to-date edge array - // let updatedEdges = Array.isArray(dbEdges) ? [...dbEdges] : []; - // if (realActionNodes.length > 0) { - // // Connect last action to action-placeholder - // const lastAction = realActionNodes[realActionNodes.length - 1]; - // updatedEdges = [ - // ...updatedEdges, - // { - // id: `edge-${lastAction.id}-action-placeholder-end`, - // source: lastAction.id, - // target: "action-placeholder-end", - // type: "default" - // } - // ]; - // } else if (triggerNodeRef) { - // // No actions yet, connect trigger to action-placeholder - // updatedEdges = [ - // ...updatedEdges, - // { - // id: `edge-${triggerNodeRef.id}-action-placeholder-end`, - // source: triggerNodeRef.id, - // target: "action-placeholder-end", - // type: "default" - // } - // ]; - // } - - // // Add placeholder node - // allNodes = [...allNodes, actionPlaceholder]; - - // setNodes(allNodes); - // setEdges(updatedEdges); - // }; - - // if (workflowId) loadWorkflows(); - // }, [workflowId]); - - - const handleNodeConfigure = (node: any) => { setSelectedNode(node); setConfigOpen(true); }; const handleNodesChange = (changes: NodeChange[]) => { - onNodesChange(changes); // Update UI first + onNodesChange(changes); try { changes.forEach((change) => { if (change.type === "position" && change.position) { - // Find the node in local state to check its type const changedNode = nodes.find((n) => n.id === change.id); if (changedNode?.data?.nodeType === "trigger") { - // If it's a trigger node, update via trigger API api.triggers.update({ TriggerId: change.id, Config: { @@ -406,7 +223,6 @@ export default function WorkflowCanvas() { }, }); } else { - // Otherwise, update in node table api.nodes.update({ NodeId: change.id, position: change.position, @@ -414,15 +230,42 @@ export default function WorkflowCanvas() { } } }); - } catch (error: any) { - setError(error); + setError(null); + } catch (err: any) { + setError( + err?.message ?? + "Failed to update node position. Please try again." + ); } }; const handleActionSelection = async (action: any) => { - try { - console.log("This is node Id before log", action.id); + // Defensive: Ensure at least one trigger present + const triggerNode = nodes.find( + (n) => n.data.nodeType === "trigger" && !n.data.isPlaceholder + ); + if (!triggerNode) { + setError("No trigger found. Please add a trigger before adding actions."); + return; + } + let cuont = 0; + // Calculate next available action node index (excluding placeholders) + const currentActionNodes = nodes.filter( + (n) => n.data.nodeType === "action" && !n.data.isPlaceholder + ); + const nextIndex = currentActionNodes.length; + + // Place new action node after last action (or at default offset if none) + const ACTION_NODE_DEFAULT_X = 350; + const ACTION_NODE_DEFAULT_Y = 400; + const ACTION_NODE_OFFSET_X = 320; + const newNodePosition = { + x: ACTION_NODE_DEFAULT_X + ACTION_NODE_OFFSET_X * nextIndex, + y: ACTION_NODE_DEFAULT_Y, + }; + + try { const result = await api.nodes.create({ Name: action.name, AvailableNodeId: action.id, @@ -430,16 +273,16 @@ export default function WorkflowCanvas() { CredentialsID: "", }, WorkflowId: workflowId, - Position: 1, + position: newNodePosition, + stage: cuont, }); - console.log("This is node Id before log", action.id); + const actionId = result.data.data.id; - // 2. Create action node const newNode = { id: actionId, type: "customNode", - position: { x: 350, y: 400 }, + position: newNodePosition, data: { label: action.name, icon: action.icon, @@ -454,17 +297,19 @@ export default function WorkflowCanvas() { type: "action", actionType: action.id, }), - - // onConfigure: () => console.log("Configure", actionId), }, + }; + // Place the action placeholder after the new node, prevent overlap + const actionPlaceholderPosition = { + x: ACTION_NODE_DEFAULT_X + ACTION_NODE_OFFSET_X * (nextIndex + 1) + 400, + y: ACTION_NODE_DEFAULT_Y, }; - // 3. Create NEW placeholder const actionPlaceholder = { id: `action-placeholder-${Date.now()}`, type: "customNode", - position: { x: 550, y: 200 }, + position: actionPlaceholderPosition, data: { label: "Add Action", icon: "➕", @@ -475,29 +320,18 @@ export default function WorkflowCanvas() { }, }; - // Find the current trigger node (non-placeholder) - const triggerNode = nodes.find( - (n) => n.data.nodeType === "trigger" && !n.data.isPlaceholder - ); - - if (!triggerNode) { - throw new Error("No trigger found"); - } - const triggerId = triggerNode.id; - // Remove old action placeholder and add new action + new placeholder setNodes((prevNodes) => { + // Remove any existing action placeholder nodes const filtered = prevNodes.filter( (n) => !(n.data.isPlaceholder && n.data.nodeType === "action") ); - return [...filtered, newNode, actionPlaceholder]; }); - // Determine the previous latest action node (for correct edge chaining) setEdges((prevEdges) => { - // Remove edges pointing to the old placeholder + // Remove placeholder-targeting edges const filtered = prevEdges.filter((e) => { const targetNode = nodes.find((n) => n.id === e.target); return !( @@ -506,20 +340,10 @@ export default function WorkflowCanvas() { ); }); - // Find all non-placeholder action nodes in the CURRENT state (after newNode is added) - const currentActionNodes = [ - ...nodes.filter( - (n) => n.data.nodeType === "action" && !n.data.isPlaceholder - ), - newNode, - ]; - - // Find all existing action nodes (NOT including newNode yet) const existingActionNodes = nodes.filter( (n) => n.data.nodeType === "action" && !n.data.isPlaceholder ); - // Source is the last existing action, or trigger if this is first action const sourceNodeId = existingActionNodes.length > 0 ? existingActionNodes[existingActionNodes.length - 1]!.id @@ -527,13 +351,11 @@ export default function WorkflowCanvas() { return [ ...filtered, - // Edge from previous node (trigger or prev action) to the new action { id: `e-action-${sourceNodeId}-${actionId}`, source: sourceNodeId, target: actionId, }, - // Edge from new action to new placeholder { id: `e-action-${actionId}-placeholder`, source: actionId, @@ -543,14 +365,16 @@ export default function WorkflowCanvas() { }); setActionOpen(false); - } catch (error: any) { - setError(error); + setError(null); + } catch (err: any) { + setError( + err?.message ?? + "Failed to add an action node. Please try again." + ); } }; const handleSelection = async (trigger: any) => { - console.log("THe trigger name is ", trigger.name); - try { const result = await api.triggers.create({ Name: trigger.name, @@ -560,12 +384,11 @@ export default function WorkflowCanvas() { TriggerType: trigger.type, }); const triggerId = result.data.data.id as string; - console.log("The Trigger Id is : ", triggerId); const newNode = { id: triggerId, type: "customNode", - position: { x: 250, y: 50 }, + position: DEFAULT_TRIGGER_POSITION, data: { label: trigger.name, icon: trigger.icon, @@ -573,7 +396,6 @@ export default function WorkflowCanvas() { nodeType: "trigger", isConfigured: false, config: {}, - // onConfigure: () => console.log("Configure", triggerId), onConfigure: () => handleNodeConfigure({ id: triggerId, @@ -586,7 +408,7 @@ export default function WorkflowCanvas() { const actionPlaceholder = { id: "action-holder", type: "customNode", - position: { x: 550, y: 200 }, + position: DEFAULT_ACTION_POSITION, data: { label: "Add Action", icon: "➕", @@ -606,34 +428,73 @@ export default function WorkflowCanvas() { }, ]); setTriggerOpen(false); - } catch (error: any) { - setError(error); + setError(null); + } catch (err: any) { + setError( + err?.message ?? + "Failed to add trigger. Please try again." + ); } }; - const handleSave = async () => { - const payload = { - workflowId: workflowId, - nodes: nodes, - edges: edges - }; - console.log("THe Nodes are ", payload.nodes) - console.log("The payload in handleSave is ", payload); try { - const response = await api.workflows.put({ + await api.workflows.put({ workflowId: workflowId, - nodes : nodes, - edges: edges + edges: edges, }); - // Optionally, you can show a message or update UI on success - console.log("Workflow updated successfully:", response.data); - } catch (error: any) { - setError(error); + setError(null); + } catch (err: any) { + setError( + err?.message ?? + "Failed to save workflow. Please try again." + ); } - } + }; + return (
+ {error && ( +
+ ⚠️ + {error} + +
+ )} - - {/* ADD THIS MODAL */} { - console.log("Updating the trigger"); - - // Find the trigger node based on its type being 'trigger' - const triggerNode = nodes.find((n) => n.data.nodeType === "trigger"); - console.log( - "This is the triggerNode from the Trigger Node ", - triggerNode - ); - console.log("This is the NodeId which must be Triiger", nodeId); - const isTrigger = triggerNode?.id === nodeId; - console.log("Is this a trigger or not", isTrigger); - if (isTrigger) { - const data = await api.triggers.update({ TriggerId: nodeId, Config: config }); - console.log("The Data saved to DataBase is", data); - // The 'data' is undefined because api.triggers.update does not return anything (it's missing a 'return' statement). - // To debug, log after the call and also the actual variable: - console.log("api.triggers.update response:", data); - - const res = JSON.stringify(data); - console.log("THe response is res from saving the data is ", res) - console.log("Updating the trigger"); - } else { - await api.nodes.update({ NodeId: nodeId, Config: config }); + try { + const triggerNode = nodes.find( + (n) => n.data.nodeType === "trigger" + ); + const isTrigger = triggerNode?.id === nodeId; + + if (isTrigger) { + await api.triggers.update({ + TriggerId: nodeId, + Config: config, + }); + } else { + await api.nodes.update({ + NodeId: nodeId, + Config: config, + }); + } + + setNodes((prevNodes) => + prevNodes.map((node) => + node.id === nodeId + ? { + ...node, + data: { ...node.data, config, isConfigured: true }, + } + : node + ) + ); + setError(null); + } catch (err: any) { + setError( + err?.message ?? + "Failed to save configuration. Please try again." + ); } - - setNodes((prevNodes) => - prevNodes.map((node) => - node.id === nodeId - ? { - ...node, - data: { ...node.data, config, isConfigured: true }, - } - : node - ) - ); }} /> ); } - - \ No newline at end of file diff --git a/apps/web/app/workflows/page.tsx b/apps/web/app/workflows/page.tsx new file mode 100644 index 0000000..fcae037 --- /dev/null +++ b/apps/web/app/workflows/page.tsx @@ -0,0 +1,102 @@ +"use client" +import { useEffect, useState } from "react"; +import { useAppSelector } from "../hooks/redux"; +import { api } from "../lib/api"; +import { + Card, + CardAction, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@workspace/ui/components/card" +import { Button } from "@workspace/ui/components/button" +import { useRouter } from "next/navigation"; +// Removed: import { Router } from "next/router"; + +export const UserWorkflows = () => { + const [data, setData] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const userId = useAppSelector((select) => select.user.userId) as string; + const router = useRouter(); // FIX: Call useRouter as a hook to get the router instance + + useEffect(() => { + const fetchWorkflows = async () => { + try { + setLoading(true); + setError(null); + const response = await api.user.get(); + const workflows = response.data.Data || response.data || []; + setData(Array.isArray(workflows) ? workflows : []); + console.log("The workflow data is", workflows); + } catch (error) { + setError("Failed to fetch workflows"); + console.error("Failed to fetch workflows:", error); + } finally { + setLoading(false); + } + }; + if (userId) { + fetchWorkflows(); + } + }, [userId]); + + return ( +
+

User Workflows

+ {loading ? ( +
Loading...
+ ) : error ? ( +
{error}
+ ) : data.length === 0 ? ( +
No workflows found.
+ ) : ( + // Center and grid the square cards +
+ {data.map((workflow: any) => ( + + + {workflow.name || workflow.Name || "Untitled Workflow"} + {workflow.description && ( + {workflow.description} + )} + + +
+                                    {JSON.stringify(workflow.config || workflow.Config, null, 2)}
+                                
+
+ + + +
+ ))} +
+ )} +
+ ); +}; + +export default UserWorkflows \ No newline at end of file diff --git a/apps/worker/src/tests/setup-test.ts b/apps/worker/src/tests/setup-test.ts index d7fce67..d8e8c80 100644 --- a/apps/worker/src/tests/setup-test.ts +++ b/apps/worker/src/tests/setup-test.ts @@ -51,6 +51,7 @@ async function setupTestWorkflow() { workflowId: workflow.id, AvailableNodeID: availableNode.id, position: 0, + stage : 1, config: { to: "iamvamsi0@gmail.com", subject: "Test from BuildFlow Worker", diff --git a/packages/nodes/src/registry/node-registry.ts b/packages/nodes/src/registry/node-registry.ts index 9362317..658017d 100644 --- a/packages/nodes/src/registry/node-registry.ts +++ b/packages/nodes/src/registry/node-registry.ts @@ -71,6 +71,13 @@ class NodeRegistry { static async registerAll() { await GoogleSheetNode.register(); await GmailNode.register(); + await NodeRegistry.registerTrigger({ + name: 'Webhook Trigger', + type: 'webhook', + description: 'Triggers when an HTTP webhook is received.', + config: {}, + requireAuth: false, + }); } }