Skip to content

Conversation

@Vamsi-o
Copy link
Contributor

@Vamsi-o Vamsi-o commented Jan 19, 2026

Summary by CodeRabbit

Release Notes

  • New Features
    • Added Save button to persist workflow configurations including nodes and edges
    • Workflows now automatically load and display previously saved data upon opening
    • Improved validation and error handling for workflow creation and updates with enhanced error messages

✏️ Tip: You can customize this high-level summary in your review settings.

- Introduced workflowUpdateSchema for validating workflow updates.
- Updated userRoutes to handle workflow updates, including error handling and user authentication checks.
- Refactored API calls in the frontend to support PUT requests for updating workflows.
- Improved workflow loading logic in WorkflowCanvas to manage nodes and edges effectively.
- Added a save button in WorkflowCanvas for persisting workflow changes.
- Removed commented-out code for the getCredentials route in userRoutes for clarity.
- Improved error handling in userRoutes to provide more informative messages.
- Updated WorkflowCanvas to handle trigger nodes and action placeholders more effectively.
- Enhanced workflow loading logic to safeguard against missing data and ensure proper node and edge management.
Copilot AI review requested due to automatic review settings January 19, 2026 12:39
@coderabbitai
Copy link

coderabbitai bot commented Jan 19, 2026

📝 Walkthrough

Walkthrough

This PR implements full-stack workflow update functionality by adding a workflowUpdateSchema for validation, implementing the PUT /workflow/update backend route handler, exposing a corresponding frontend API method, and updating the workflow editor page to load workflows on mount and persist changes via a new Save button with proper error handling.

Changes

Cohort / File(s) Summary
Backend Workflow Routes
apps/http-backend/src/routes/userRoutes/userRoutes.ts
Removed large commented-out GET block; restructured create/workflow route with enhanced error handling and explicit error messages; implemented full PUT workflow/update handler with validation via workflowUpdateSchema, authentication, existence check, update call, and proper response formatting.
Frontend API Layer
apps/web/app/lib/api.ts
Added new put(data) method in workflows for PUT requests to /user/workflow/update; refactored workflows.get with explicit return; minor spacing adjustment in Credentials.getCredentials.
Frontend Workflow Editor
apps/web/app/workflows/[id]/page.tsx
Added useEffect to load and transform workflow data into Trigger and action nodes with auto-wiring; implemented handleSave function to persist workflows via API; added Save button to UI; updated ConfigModal.onSave to handle trigger persistence; enhanced handleActionSelection for dynamic action node creation and edge reconfiguration; expanded logging for debugging.
Common Schema Validation
packages/common/src/index.ts
Exported new workflowUpdateSchema with optional nodes/edges and required workflowId for workflow update validation.
Database Model
packages/db/prisma/schema.prisma
Added optional Edges field (Json type) to Workflow model to support persisting workflow edges.

Sequence Diagram

sequenceDiagram
    participant User
    participant Browser as Browser (UI)
    participant API as Frontend API
    participant Backend as Backend Handler
    participant DB as Database

    User->>Browser: Click Save Button
    activate Browser
    Browser->>API: put({nodes, edges, workflowId})
    deactivate Browser
    
    activate API
    API->>Backend: PUT /user/workflow/update
    deactivate API
    
    activate Backend
    Backend->>Backend: Validate with workflowUpdateSchema
    Backend->>Backend: Check user auth & workflow exists
    Backend->>DB: Update Workflow (nodes, edges)
    deactivate Backend
    
    activate DB
    DB->>DB: Update Edges field
    DB-->>Backend: Confirmation
    deactivate DB
    
    Backend-->>API: {status: 200, workflow: {...}}
    activate API
    API-->>Browser: Response data
    deactivate API
    
    activate Browser
    Browser->>Browser: Log success / update state
    deactivate Browser
Loading

Additionally, on page load:

sequenceDiagram
    participant Page as Page Mount
    participant API as Frontend API
    participant Backend as Backend Handler
    participant DB as Database

    Page->>API: GET /user/workflow/:id
    activate API
    API->>Backend: GET request
    deactivate API
    
    activate Backend
    Backend->>DB: Fetch Workflow
    deactivate Backend
    
    activate DB
    DB-->>Backend: {nodes, edges, Trigger, ...}
    deactivate DB
    
    Backend-->>API: Workflow data
    activate API
    API-->>Page: Data
    deactivate API
    
    activate Page
    Page->>Page: Transform nodes (Trigger + Actions + Placeholder)
    Page->>Page: Auto-wire edges from Trigger → First Action
    Page->>Page: Render nodes and edges
    deactivate Page
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Poem

🐰 A workflow that saves with a hop and a bound,
With edges and nodes all shiny and sound,
Press Save and it dances to database's tune,
The update cascade completes oh so soon! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Fix/workflow' is vague and generic, lacking specificity about what aspect of the workflow is being fixed or improved. Replace with a more descriptive title that summarizes the main change, such as 'Add workflow update endpoint and persistence' or 'Implement workflow save functionality with validation'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request adds functionality to save and load workflow edges (connections between nodes) in the workflow canvas. The changes enable persisting the visual graph structure of workflows to the database.

Changes:

  • Added Edges JSON field to the Workflow database schema
  • Created validation schema for workflow updates with nodes and edges
  • Implemented workflow loading logic that reconstructs trigger nodes, action nodes, and edges from database
  • Added save button and handler to persist workflow changes

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 19 comments.

Show a summary per file
File Description
packages/db/prisma/schema.prisma Added Edges field to Workflow model to store edge data
packages/common/src/index.ts Added workflowUpdateSchema for validating workflow update requests
apps/web/app/workflows/[id]/page.tsx Implemented workflow loading on mount, save functionality, and UI save button
apps/web/app/lib/api.ts Added PUT method for workflow updates
apps/http-backend/src/routes/userRoutes/userRoutes.ts Implemented workflow update endpoint with validation and authorization

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +53 to +57
export const workflowUpdateSchema = z.object({
nodes : z.any().optional(),
edges : z.any().optional(),
workflowId : z.string()
})
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using z.any() for validation defeats the purpose of schema validation and allows any data type to pass through. This should be replaced with a proper schema that validates the expected structure of nodes and edges, such as z.array() or a specific object schema that matches the React Flow node/edge structure.

Suggested change
export const workflowUpdateSchema = z.object({
nodes : z.any().optional(),
edges : z.any().optional(),
workflowId : z.string()
})
const ReactFlowNodeSchema = z
.object({
id: z.string(),
type: z.string().optional(),
data: z.unknown().optional(),
position: z
.object({
x: z.number(),
y: z.number(),
})
.optional(),
selected: z.boolean().optional(),
draggable: z.boolean().optional(),
})
.passthrough();
const ReactFlowEdgeSchema = z
.object({
id: z.string(),
source: z.string(),
target: z.string(),
type: z.string().optional(),
label: z.string().optional(),
data: z.unknown().optional(),
})
.passthrough();
export const workflowUpdateSchema = z.object({
nodes: z.array(ReactFlowNodeSchema).optional(),
edges: z.array(ReactFlowEdgeSchema).optional(),
workflowId: z.string(),
});

Copilot uses AI. Check for mistakes.
Comment on lines +621 to +622
console.log("THe Nodes are ", payload.nodes)
console.log("The payload in handleSave is ", payload);
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Multiple debug console.log statements have been added throughout the code (lines 57, 63, 65, 621, 622). These should be removed before merging to production as they clutter the console output and are not useful for end users.

Copilot uses AI. Check for mistakes.
Comment on lines +244 to +259

if (!req.user) {
return res.status(statusCodes.BAD_REQUEST).json({
message: "User is not logged in ",
});
}
const Data = req.body;
const ParsedData = WorkflowSchema.safeParse(Data);
const UserID = req.user.id;
// const UserID = "343c9a0a-9c3f-40d0-81de-9a5969e03f92";
// Ensure that the required fields are present in the parsed data and create the workflow properly.
if (!ParsedData.success) {
return res.status(statusCodes.BAD_REQUEST).json({
message: "Incorrect Workflow Inputs",
});
} try {
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try block wraps only a portion of the code, leaving the validation logic (lines 244-259) outside error handling. This creates inconsistent error handling behavior. Either move the entire function logic inside the try block or handle errors for the validation section separately.

Copilot uses AI. Check for mistakes.

// 1. SAFEGUARD: Default to empty arrays to prevent crashes
const dbNodes = workflows.data.Data.nodes || [];
const dbEdges = workflows.data.Data.Edges || [];
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a case inconsistency issue: the database schema uses "Edges" (PascalCase), but the frontend code references both "Edges" (line 61) and "edges" (lowercase in the API call). This inconsistency, combined with the schema naming issue, could lead to data not being properly saved or retrieved. The casing should be standardized across the entire stack.

Suggested change
const dbEdges = workflows.data.Data.Edges || [];
const dbEdges = workflows.data.Data.edges || [];

Copilot uses AI. Check for mistakes.
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);
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The console.log statement has a grammatical error with improper spacing in "the trigger Data is" (double space). Consider revising to: "Trigger Data:".

Suggested change
console.log("This is the trigger Data is",workflows.data.Data);
console.log("Trigger Data:", workflows.data.Data);

Copilot uses AI. Check for mistakes.
status WorkflowStatus?
isEmpty Boolean? @default(true)
nodes Node[]
Edges Json?
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field name "Edges" uses PascalCase, which is inconsistent with the other fields in the model that use camelCase (e.g., "isEmpty", "nodes", "user"). For consistency with Prisma conventions and the existing schema, this should be renamed to "edges".

Suggested change
Edges Json?
edges Json? @map("Edges")

Copilot uses AI. Check for mistakes.
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);
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The console.log statement contains a grammatical error with improper spacing in "the New Triggering" (double space) and awkward phrasing. Consider revising to: "New Trigger Data - AvailableTriggerID:".

Suggested change
console.log("This is the New Triggering trigger Data is",workflows.data.Data.Trigger.AvailableTriggerID);
console.log("New Trigger Data - AvailableTriggerID:", workflows.data.Data.Trigger.AvailableTriggerID);

Copilot uses AI. Check for mistakes.
} catch (e: any) {
console.log("Internal server error from creating aworkflow", e);
return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({
message: "Internal Server Error from CCreating Workflow",
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message contains a spelling error: "CCreating" should be "Creating" (duplicate C).

Suggested change
message: "Internal Server Error from CCreating Workflow",
message: "Internal Server Error from Creating Workflow",

Copilot uses AI. Check for mistakes.
Comment on lines +401 to +438
error: parsedData.error
})
}
const workflowId = parsedData.data.workflowId;
if (!req.user) {
return res.status(statusCodes.UNAUTHORIZED).json({
message: "User Not Authenticated"
})
}
const userId = req.user.id
try {
const workflowValid = await prismaClient.workflow.findFirst({
where: { id: workflowId, userId: userId }
})
if (!workflowValid) {
return res.status(statusCodes.BAD_REQUEST).json({
message: "Didn't find the workflow"
})
}
const update = await prismaClient.workflow.update({
where: { id: workflowId, userId: userId },
data: {
// Nodes: parsedData.data.nodes,
Edges: parsedData.data.edges
}
})
return res.status(statusCodes.ACCEPTED).json({
message: "Workflow Updated Succesfuly",
Data: update
})
} catch (error: any) {
console.log("Error updating workflow:", error);
return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({
message: "Internal Server Error from Updating workflow",
error: error instanceof Error ? error.message : "Unknown error"
})
}
})
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid automated semicolon insertion (94% of all statements in the enclosing script have an explicit semicolon).

Suggested change
error: parsedData.error
})
}
const workflowId = parsedData.data.workflowId;
if (!req.user) {
return res.status(statusCodes.UNAUTHORIZED).json({
message: "User Not Authenticated"
})
}
const userId = req.user.id
try {
const workflowValid = await prismaClient.workflow.findFirst({
where: { id: workflowId, userId: userId }
})
if (!workflowValid) {
return res.status(statusCodes.BAD_REQUEST).json({
message: "Didn't find the workflow"
})
}
const update = await prismaClient.workflow.update({
where: { id: workflowId, userId: userId },
data: {
// Nodes: parsedData.data.nodes,
Edges: parsedData.data.edges
}
})
return res.status(statusCodes.ACCEPTED).json({
message: "Workflow Updated Succesfuly",
Data: update
})
} catch (error: any) {
console.log("Error updating workflow:", error);
return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({
message: "Internal Server Error from Updating workflow",
error: error instanceof Error ? error.message : "Unknown error"
})
}
})
error: parsedData.error,
});
}
const workflowId = parsedData.data.workflowId;
if (!req.user) {
return res.status(statusCodes.UNAUTHORIZED).json({
message: "User Not Authenticated",
});
}
const userId = req.user.id;
try {
const workflowValid = await prismaClient.workflow.findFirst({
where: { id: workflowId, userId: userId },
});
if (!workflowValid) {
return res.status(statusCodes.BAD_REQUEST).json({
message: "Didn't find the workflow",
});
}
const update = await prismaClient.workflow.update({
where: { id: workflowId, userId: userId },
data: {
// Nodes: parsedData.data.nodes,
Edges: parsedData.data.edges,
},
});
return res.status(statusCodes.ACCEPTED).json({
message: "Workflow Updated Succesfuly",
Data: update,
});
} catch (error: any) {
console.log("Error updating workflow:", error);
return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({
message: "Internal Server Error from Updating workflow",
error: error instanceof Error ? error.message : "Unknown error",
});
}
});

Copilot uses AI. Check for mistakes.
nodes : z.any().optional(),
edges : z.any().optional(),
workflowId : z.string()
})
Copy link

Copilot AI Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid automated semicolon insertion (90% of all statements in the enclosing script have an explicit semicolon).

Suggested change
})
});

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@apps/http-backend/src/routes/userRoutes/userRoutes.ts`:
- Around line 420-426: The update handler currently calls
prismaClient.workflow.update and only saves Edges while Nodes are ignored;
either persist incoming nodes or remove them from the payload. To fix, inspect
parsedData.data.nodes in the same route handling the workflow update (or
validate via workflowUpdateSchema) and choose one: (A) Persist nodes by using
the Node model with the workflowId—e.g., delete or upsert existing Node records
for the given workflowId and bulk create/update from parsedData.data.nodes after
prismaClient.workflow.update, referencing prismaClient.node and
workflowId/Nodes; or (B) strip nodes from the update payload and route node
changes to the existing /update/node endpoint instead, returning a clear
response indicating nodes must be updated via that endpoint. Ensure you
reference prismaClient.workflow.update, parsedData.data.nodes,
prismaClient.node, and the /update/node route when implementing the chosen
approach.

In `@apps/web/app/workflows/`[id]/page.tsx:
- Around line 53-87: The code creates and uses Trigger and builds triggerNode
before verifying Trigger exists, causing potential runtime crashes; move the
null/undefined check for Trigger (from workflows.data.Data.Trigger) immediately
after retrieving workflows (before any access to Trigger.id, Trigger.position,
Trigger.name, or Trigger.data), return early and call console.error if missing,
then proceed to construct triggerNode and other logic; also remove the stray '+'
character before the console.error and ensure functions referenced
(handleNodeConfigure, api.workflows.get, triggerNode) are only invoked after the
null check.
- Around line 170-173: The effect uses handleNodeConfigure inside its closures
but doesn't list it as a dependency; refactor handleNodeConfigure into a stable
memoized function using React's useCallback (e.g., wrap the existing
handleNodeConfigure that calls setSelectedNode and setConfigOpen with
useCallback and an appropriate dependency array) and then add
handleNodeConfigure to the effect's dependency list (so the effect becomes
dependent on [workflowId, handleNodeConfigure]); this ensures the effect and
loadWorkflows closures reference a stable function and avoids stale-closure
bugs.
🧹 Nitpick comments (11)
packages/db/prisma/schema.prisma (1)

91-91: Consider using camelCase for consistency with other data fields.

The Edges field uses PascalCase, but similar data fields like config (line 86) and status (line 88) use camelCase. PascalCase in this model is typically reserved for relation fields (e.g., Trigger, Node).

Suggested change
-  Edges       Json?
+  edges       Json?

Note: If you adopt this change, remember to update the backend code that references Edges to use edges instead.

packages/common/src/index.ts (2)

53-57: Consider adding type validation for nodes and edges instead of z.any().

Using z.any() bypasses Zod's type-safety benefits. If malformed data is passed, errors will only surface at runtime during database operations or UI rendering rather than at validation time.

Suggested improvement
 export const workflowUpdateSchema = z.object({
-  nodes : z.any().optional(),
-  edges : z.any().optional(),
-  workflowId : z.string()
-})
+  nodes: z.array(z.object({
+    id: z.string(),
+    type: z.string().optional(),
+    position: z.object({ x: z.number(), y: z.number() }).optional(),
+    data: z.any().optional(),
+  })).optional(),
+  edges: z.array(z.object({
+    id: z.string(),
+    source: z.string(),
+    target: z.string(),
+    type: z.string().optional(),
+  })).optional(),
+  workflowId: z.string(),
+});

2-2: Unused import.

The number import from "zod/v4" is not used anywhere in this file.

Remove unused import
-import { number } from "zod/v4";
apps/http-backend/src/routes/userRoutes/userRoutes.ts (2)

393-409: Authentication check should precede validation.

The auth check (lines 405-409) happens after body validation (lines 396-403). This inconsistency with other routes (e.g., /create/workflow at line 245) means unauthenticated requests waste server resources on validation before rejection.

Reorder checks
 router.put("/workflow/update", userMiddleware, async (req: AuthRequest, res: Response) => {
+  if (!req.user) {
+    return res.status(statusCodes.UNAUTHORIZED).json({
+      message: "User Not Authenticated"
+    })
+  }
+  const userId = req.user.id
 
   const data = req.body;
   const parsedData = workflowUpdateSchema.safeParse(data);
 
   if (!parsedData.success) {
     return res.status(statusCodes.BAD_REQUEST).json({
       message: "Incorrect Input",
       error: parsedData.error
     })
   }
   const workflowId = parsedData.data.workflowId;
-  if (!req.user) {
-    return res.status(statusCodes.UNAUTHORIZED).json({
-      message: "User Not Authenticated"
-    })
-  }
-  const userId = req.user.id
   try {

255-281: Formatting issues affect readability.

The closing brace for the validation check and try block start are on the same line (line 259), and there are extra blank lines at the end (lines 281-283).

Improved formatting
     if (!ParsedData.success) {
       return res.status(statusCodes.BAD_REQUEST).json({
         message: "Incorrect Workflow Inputs",
       });
-    } try {
+    }
+    try {
       const createWorkflow = await prismaClient.workflow.create({
         // ...
       });
       return res.status(statusCodes.CREATED).json({
         message: "Workflow Created Successfully",
         Data: createWorkflow,
       });
     } catch (e: any) {
       console.log("Internal server error from creating aworkflow", e);
       return res.status(statusCodes.INTERNAL_SERVER_ERROR).json({
         message: "Internal Server Error from CCreating Workflow",
         error: e instanceof Error ? e.message : "Unknown error"
       });
     }
-    }
-
-  
+  }
 );
apps/web/app/lib/api.ts (1)

25-32: Consider typing the data parameter.

Using any loses type safety. Since workflowUpdateSchema is already defined, you can leverage its inferred type.

Add type safety
+import { workflowUpdateSchema } from "@repo/common/zod";
+import z from "zod";
+
 // In the workflows object:
-    put: async (data: any) => {
+    put: async (data: z.infer<typeof workflowUpdateSchema>) => {
       return await axios.put(`${BACKEND_URL}/user/workflow/update`,
         data,
         {
           withCredentials: true,
           headers: { "Content-Type": "application/json" },
         })
     }
apps/web/app/workflows/[id]/page.tsx (5)

176-380: Remove commented-out code.

This ~200 line block of commented-out code clutters the file and reduces maintainability. Git history preserves previous implementations if ever needed.


615-634: Unused payload variable and missing user feedback.

The payload object is created (lines 616-620) and logged but not used in the API call (lines 624-628) which reconstructs the same object. Also, users receive no visual feedback on save success/failure.

Use payload variable and add feedback
   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({
-        workflowId: workflowId,
-        nodes : nodes,
-        edges: edges
-      });
-      // Optionally, you can show a message or update UI on success
+      const response = await api.workflows.put(payload);
       console.log("Workflow updated successfully:", response.data);
+      // TODO: Add success toast/notification
     } catch (error: any) {
       setError(error);
+      // TODO: Add error toast/notification
     }
   }

648-654: Mixed styling approach and potential z-index issues.

The Save button mixes Tailwind classes with inline styles. Placing it inside <ReactFlow> may cause z-index conflicts with the Controls component.

Move button outside ReactFlow and use consistent styling
       </ReactFlow>
+      <button
+        onClick={handleSave}
+        className="fixed bottom-4 right-40 border bg-white text-black font-bold p-4 z-50 shadow-lg px-12 rounded-2xl"
+      >
+        Save
+      </button>

       {/* Inside ReactFlow, remove: */}
-        <button
-          onClick={handleSave}
-          className=" bottom-4  border  bg-white text-black font-bold p-4 z-50 shadow-lg  px-12 rounded-2xl"
-          style={{ position: 'fixed', bottom: '1rem', right: '10rem' }}
-        >
-          Save
-        </button>

679-687: Consider removing debug console.log statements.

Multiple debug log statements exist throughout this file. Consider removing or replacing with a proper logging utility before merging to production.


3-3: Unused import act.

The act import from React is not used in this file.

Remove unused import
-import { useState, useEffect, act } from "react";
+import { useState, useEffect } from "react";

Comment on lines +420 to +426
const update = await prismaClient.workflow.update({
where: { id: workflowId, userId: userId },
data: {
// Nodes: parsedData.data.nodes,
Edges: parsedData.data.edges
}
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# Find workflowUpdateSchema definition
fd -t f -e ts -e js | xargs grep -l "workflowUpdateSchema" | head -20

Repository: Dev-Pross/BuildFlow

Length of output: 145


🏁 Script executed:

# Check the imports and full context of userRoutes.ts around line 420
cat -n apps/http-backend/src/routes/userRoutes/userRoutes.ts | sed -n '1,30p'

Repository: Dev-Pross/BuildFlow

Length of output: 1245


🏁 Script executed:

# Look for Prisma schema to understand Workflow model structure
fd schema.prisma

Repository: Dev-Pross/BuildFlow

Length of output: 95


🏁 Script executed:

# Check frontend code reference
test -f apps/web/app/workflows/[id]/page.tsx && cat -n apps/web/app/workflows/[id]/page.tsx | sed -n '610,630p' || echo "File not found at expected path"

Repository: Dev-Pross/BuildFlow

Length of output: 767


🏁 Script executed:

# Find workflowUpdateSchema definition in common package
fd -t f -e ts -e js packages/common | xargs grep -A 10 "workflowUpdateSchema" | head -50

Repository: Dev-Pross/BuildFlow

Length of output: 428


🏁 Script executed:

# Read the Prisma schema to understand Workflow model
cat -n packages/db/prisma/schema.prisma | grep -A 30 "model Workflow"

Repository: Dev-Pross/BuildFlow

Length of output: 2532


🏁 Script executed:

# Get more context on workflowUpdateSchema - check full definition
rg "workflowUpdateSchema.*=" packages/common -A 15

Repository: Dev-Pross/BuildFlow

Length of output: 869


🏁 Script executed:

# Get the full context of the workflow update route (lines 393-438)
cat -n apps/http-backend/src/routes/userRoutes/userRoutes.ts | sed -n '393,440p'

Repository: Dev-Pross/BuildFlow

Length of output: 1844


🏁 Script executed:

# Check if there's a separate node update endpoint or how nodes are meant to be updated
rg "prismaClient.node" apps/http-backend/src/routes/userRoutes/userRoutes.ts -A 5 -B 2

Repository: Dev-Pross/BuildFlow

Length of output: 647


🏁 Script executed:

# Look at the Node model in Prisma schema
cat -n packages/db/prisma/schema.prisma | grep -A 20 "^model Node"

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Find the Node model definition in the Prisma schema
rg "^model Node" packages/db/prisma/schema.prisma -A 20

Repository: Dev-Pross/BuildFlow

Length of output: 2174


🏁 Script executed:

# Check if there's a separate endpoint for updating nodes
rg "router.put.*node\|router.patch.*node" apps/http-backend/src/routes/userRoutes/userRoutes.ts -i

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Look for how nodes are persisted - check for any node update logic in the workflow update or elsewhere
rg "updateMany.*node\|createMany.*node" apps/http-backend/src/routes/userRoutes/ -i

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Look at the workflow create endpoint to see how nodes are handled there
cat -n apps/http-backend/src/routes/userRoutes/userRoutes.ts | sed -n '244,290p'

Repository: Dev-Pross/BuildFlow

Length of output: 1892


🏁 Script executed:

# Check if there's a workflow creation logic that handles nodes
rg "prismaClient.node.create\|prismaClient.node.updateMany" apps/http-backend/src/routes/userRoutes/userRoutes.ts -B 5 -A 10

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Look for any workflow-related route that might handle node updates
rg "router\.(post|put|patch)" apps/http-backend/src/routes/userRoutes/userRoutes.ts | grep -i "node\|workflow"

Repository: Dev-Pross/BuildFlow

Length of output: 235


🏁 Script executed:

# Check how nodes are actually created - look for the node creation logic when a workflow is created
rg "prismaClient.node.create" apps/http-backend/src/routes/userRoutes/userRoutes.ts -B 10 -A 5

Repository: Dev-Pross/BuildFlow

Length of output: 796


🏁 Script executed:

# Look for any endpoint that handles node creation in bulk
rg "createMany\|updateMany" apps/http-backend/src/routes/userRoutes/userRoutes.ts -B 5 -A 5

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Check all endpoints in userRoutes to see if there's a separate node update mechanism
rg "^router\.(post|put|patch|delete)" apps/http-backend/src/routes/userRoutes/userRoutes.ts | head -30

Repository: Dev-Pross/BuildFlow

Length of output: 311


🏁 Script executed:

# Let me check what the complete workflow update route does and if there's any node handling elsewhere
cat -n apps/http-backend/src/routes/userRoutes/userRoutes.ts | sed -n '393,450p'

Repository: Dev-Pross/BuildFlow

Length of output: 2185


🏁 Script executed:

# Check if there's any documentation or comments about the intended behavior
rg "workflow.*update\|update.*workflow" apps/http-backend/src/routes/userRoutes/userRoutes.ts -i -B 3 -A 3

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Look at the workspace - see if there's another file handling node updates
fd -t f -e ts "routes" apps/http-backend/src | head -20

Repository: Dev-Pross/BuildFlow

Length of output: 161


🏁 Script executed:

# Check the nodes.routes.ts file to understand node update logic
cat -n apps/http-backend/src/routes/nodes.routes.ts

Repository: Dev-Pross/BuildFlow

Length of output: 4017


🏁 Script executed:

# Also check how the frontend handles node updates - are they sent separately?
rg "api.nodes\|api.workflows" apps/web/app/workflows/[id]/page.tsx -B 2 -A 5

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Search for API endpoints in the web app - check the api client implementation
fd -t f -e ts -e js "api" apps/web | grep -E "(client|api)" | head -20

Repository: Dev-Pross/BuildFlow

Length of output: 86


🏁 Script executed:

# Look for workflow update API calls in the web app
rg "workflows.put\|api.put.*workflow" apps/web -B 3 -A 5

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Check if nodes are being updated separately through a different endpoint
rg "nodes.put\|nodes.update\|api.put.*node" apps/web -B 3 -A 5

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Check the API client implementation
cat -n apps/web/app/lib/api.ts

Repository: Dev-Pross/BuildFlow

Length of output: 3532


🏁 Script executed:

# Look at the entire handleSave function to understand the full flow
cat -n apps/web/app/workflows/[id]/page.tsx | sed -n '615,650p'

Repository: Dev-Pross/BuildFlow

Length of output: 1356


Nodes in the update payload are being ignored and not persisted.

The workflowUpdateSchema accepts nodes, and the frontend sends them (lines 618-627 in the page component), but the workflow update route only saves Edges while the Nodes field remains commented out (line 423).

The current Prisma schema stores nodes as separate Node records with a workflowId relation, not as a JSON field on Workflow. If nodes are sent in this update request, they should either be:

  1. Persisted by updating individual Node records in the database, or
  2. Removed from the payload if nodes are managed through a separate endpoint (/update/node)

Without handling the nodes data, any node updates from the UI are lost.

🤖 Prompt for AI Agents
In `@apps/http-backend/src/routes/userRoutes/userRoutes.ts` around lines 420 -
426, The update handler currently calls prismaClient.workflow.update and only
saves Edges while Nodes are ignored; either persist incoming nodes or remove
them from the payload. To fix, inspect parsedData.data.nodes in the same route
handling the workflow update (or validate via workflowUpdateSchema) and choose
one: (A) Persist nodes by using the Node model with the workflowId—e.g., delete
or upsert existing Node records for the given workflowId and bulk create/update
from parsedData.data.nodes after prismaClient.workflow.update, referencing
prismaClient.node and workflowId/Nodes; or (B) strip nodes from the update
payload and route node changes to the existing /update/node endpoint instead,
returning a clear response indicating nodes must be updated via that endpoint.
Ensure you reference prismaClient.workflow.update, parsedData.data.nodes,
prismaClient.node, and the /update/node route when implementing the chosen
approach.

Comment on lines +53 to +87
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
const triggerNode = {
id: Trigger.id,
type: "customNode",
// Default to top center if no position exists
position: Trigger.position || { x: 250, y: 50 },
data: {
label: Trigger.name || Trigger.data?.label || "Trigger",
icon: Trigger.data?.icon || "⚡", // Lightning icon for trigger
nodeType: "trigger",
isConfigured: true,
onConfigure: () => handleNodeConfigure({
id: Trigger.id,
// ... pass your trigger config props here
})
},
};
if (!Trigger) {
+ console.error("No trigger found in workflow data");
return;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Critical: Null check for Trigger happens after it's already used.

The code accesses Trigger properties (lines 57, 62, 65, 68-83) before the null check at line 84. If Trigger is null/undefined, this will cause a runtime crash.

Additionally, line 85 has a stray + character: + console.error.

🐛 Proposed fix - move null check before usage
   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. CHECK TRIGGER EXISTS FIRST
+        if (!Trigger) {
+          console.error("No trigger found in workflow data");
+          return;
+        }
+
+        console.log("Trigger Data:", Trigger);
+
-        // 2. CREATE TRIGGER NODE
+        // 3. CREATE TRIGGER NODE
         // We assume the trigger is always the starting point
         const triggerNode = {
           id: Trigger.id,
           // ... rest of triggerNode creation
         };
-        if (!Trigger) {
-          +  console.error("No trigger found in workflow data");
-            return;
-          }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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
const triggerNode = {
id: Trigger.id,
type: "customNode",
// Default to top center if no position exists
position: Trigger.position || { x: 250, y: 50 },
data: {
label: Trigger.name || Trigger.data?.label || "Trigger",
icon: Trigger.data?.icon || "⚡", // Lightning icon for trigger
nodeType: "trigger",
isConfigured: true,
onConfigure: () => handleNodeConfigure({
id: Trigger.id,
// ... pass your trigger config props here
})
},
};
if (!Trigger) {
+ console.error("No trigger found in workflow data");
return;
}
useEffect(() => {
const loadWorkflows = async () => {
try {
const workflows = await api.workflows.get(workflowId);
// 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;
// 2. CHECK TRIGGER EXISTS FIRST
if (!Trigger) {
console.error("No trigger found in workflow data");
return;
}
console.log("Trigger Data:", Trigger);
// 3. CREATE TRIGGER NODE
// We assume the trigger is always the starting point
const triggerNode = {
id: Trigger.id,
type: "customNode",
// Default to top center if no position exists
position: Trigger.position || { x: 250, y: 50 },
data: {
label: Trigger.name || Trigger.data?.label || "Trigger",
icon: Trigger.data?.icon || "⚡", // Lightning icon for trigger
nodeType: "trigger",
isConfigured: true,
onConfigure: () => handleNodeConfigure({
id: Trigger.id,
// ... pass your trigger config props here
})
},
};
🤖 Prompt for AI Agents
In `@apps/web/app/workflows/`[id]/page.tsx around lines 53 - 87, The code creates
and uses Trigger and builds triggerNode before verifying Trigger exists, causing
potential runtime crashes; move the null/undefined check for Trigger (from
workflows.data.Data.Trigger) immediately after retrieving workflows (before any
access to Trigger.id, Trigger.position, Trigger.name, or Trigger.data), return
early and call console.error if missing, then proceed to construct triggerNode
and other logic; also remove the stray '+' character before the console.error
and ensure functions referenced (handleNodeConfigure, api.workflows.get,
triggerNode) are only invoked after the null check.

Comment on lines +170 to +173
};

loadWorkflows();
}, [workflowId]);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '150,190p'

Repository: Dev-Pross/BuildFlow

Length of output: 1803


🏁 Script executed:

cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '1,50p'

Repository: Dev-Pross/BuildFlow

Length of output: 1859


🏁 Script executed:

cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '80,175p'

Repository: Dev-Pross/BuildFlow

Length of output: 3889


🏁 Script executed:

rg -n "handleNodeConfigure" apps/web/app/workflows/\[id\]/page.tsx -A 5

Repository: Dev-Pross/BuildFlow

Length of output: 1998


🏁 Script executed:

cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '60,175p'

Repository: Dev-Pross/BuildFlow

Length of output: 4934


🏁 Script executed:

cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '54,75p'

Repository: Dev-Pross/BuildFlow

Length of output: 1279


🏁 Script executed:

cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '50,60p'

Repository: Dev-Pross/BuildFlow

Length of output: 576


🏁 Script executed:

cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '380,390p'

Repository: Dev-Pross/BuildFlow

Length of output: 361


🏁 Script executed:

cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '23,48p'

Repository: Dev-Pross/BuildFlow

Length of output: 1046


🏁 Script executed:

rg -n "eslint" apps/web/app/workflows/\[id\]/page.tsx

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Check if there's a .eslintrc or eslint config in the repo
find apps/web -name ".eslintrc*" -o -name "eslint.config.*" | head -5

Repository: Dev-Pross/BuildFlow

Length of output: 88


🏁 Script executed:

cat apps/web/eslint.config.js

Repository: Dev-Pross/BuildFlow

Length of output: 201


🏁 Script executed:

# Check the exhaustive-deps rule configuration
rg -n "react-hooks" apps/web/

Repository: Dev-Pross/BuildFlow

Length of output: 45


🏁 Script executed:

# Check if there are any existing ESLint rule files or configs at the workspace level
find . -path ./node_modules -prune -o -name ".eslintrc*" -print | grep -v node_modules | head -10

Repository: Dev-Pross/BuildFlow

Length of output: 77


🏁 Script executed:

cat .eslintrc.js

Repository: Dev-Pross/BuildFlow

Length of output: 373


🏁 Script executed:

# Let's verify the actual behavior by checking how the effect works
# Check what happens with the callbacks in the nodes
cat -n apps/web/app/workflows/\[id\]/page.tsx | sed -n '53,110p'

Repository: Dev-Pross/BuildFlow

Length of output: 2764


handleNodeConfigure should be added to the dependency array or wrapped in useCallback.

The function is captured in closures within the effect (lines 78 and 99) and should be listed as a dependency. While the current code works because handleNodeConfigure only calls stable setters, wrapping it with useCallback would follow best practices and prevent potential issues if the function logic changes.

const handleNodeConfigure = useCallback((node: any) => {
  setSelectedNode(node);
  setConfigOpen(true);
}, []);

// Then add handleNodeConfigure to the dependency array:
}, [workflowId, handleNodeConfigure]);
🤖 Prompt for AI Agents
In `@apps/web/app/workflows/`[id]/page.tsx around lines 170 - 173, The effect uses
handleNodeConfigure inside its closures but doesn't list it as a dependency;
refactor handleNodeConfigure into a stable memoized function using React's
useCallback (e.g., wrap the existing handleNodeConfigure that calls
setSelectedNode and setConfigOpen with useCallback and an appropriate dependency
array) and then add handleNodeConfigure to the effect's dependency list (so the
effect becomes dependent on [workflowId, handleNodeConfigure]); this ensures the
effect and loadWorkflows closures reference a stable function and avoids
stale-closure bugs.

@Vamsi-o Vamsi-o merged commit 3a4cc9f into main Jan 19, 2026
8 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Jan 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants