Skip to content

feat: enhance workflow loading logic and improve node handling in CreateWorkFlow component#43

Merged
Vamsi-o merged 5 commits into
mainfrom
rendered-existing-workflow
Jan 2, 2026
Merged

feat: enhance workflow loading logic and improve node handling in CreateWorkFlow component#43
Vamsi-o merged 5 commits into
mainfrom
rendered-existing-workflow

Conversation

@TejaBudumuru3

@TejaBudumuru3 TejaBudumuru3 commented Dec 28, 2025

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Workflow diagram now auto-builds nodes and connections on load for a more accurate initial view.
    • Google Sheet form supports pre-filled selections and auto-fetch of documents/sheets from existing node data.
  • Refactor

    • Trigger data handling standardized for consistent state and diagram reconstruction.
    • Google Sheet node type parsing and config construction streamlined for more reliable behavior.
  • Other

    • Node click interactions now log lightweight details for activity insight.

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

@coderabbitai

coderabbitai Bot commented Dec 28, 2025

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

CreateWorkFlow now initializes diagram nodes/edges from full trigger and node objects via a new useEffect/loadWorkflow, standardizes node metadata and positions, adds lightweight node click logging, and expands GoogleSheetFormClient to accept an optional initialData prop for pre-populating credential/document/sheet state and auto-fetching related lists.

Changes

Cohort / File(s) Summary
Workflow init & node construction
apps/web/app/components/nodes/CreateWorkFlow.tsx
Added a useEffect that calls loadWorkflow to build initial nodes (trigger + action nodes + placeholder) and edges from existingTrigger and existingNodes, replacing prior ad-hoc node setup.
Trigger access & node metadata
apps/web/app/components/nodes/CreateWorkFlow.tsx
Switched from reading trigger name string to using the full trigger object (s.workflow.trigger); nodes now include structured metadata (label, name, icon, type, config) and computed positions.
Node interactions & Google Sheet wiring
apps/web/app/components/nodes/CreateWorkFlow.tsx
Removed an unused reducer import, adjusted console logging, added neutral node click logging, and retained Google Sheet detection/wiring that inspects node.data.type to set nodeIDType/credType and open the sheet form.
Google Sheet form signature & pre-fill
apps/web/app/components/nodes/GoogleSheetFormClient.tsx
Signature changed to GoogleSheetFormClient({ type, nodeType, initialData }); added initialData-driven state pre-population (credential, document, sheet, operation, range), nodeType parsing (nodeTypeParsed/nodeId), and useEffects to fetch documents/sheets when credentialId/spreadSheetId are provided.
Cleanup & logging
apps/web/app/components/nodes/CreateWorkFlow.tsx
Removed unused workflowReducer import, commented out a prior console.log of trigger/nodes, added neutral console.logs for node interactions.

Sequence Diagram(s)

(Skipped — changes are component initialization and form pre-population; they do not introduce a new multi-component sequential flow that meets the diagram criteria.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • Vamsi-o

Poem

🐇 I hopped through triggers, lined nodes in a row,

Edges found their places where data will go,
Sheets now greet me with pre-filled delight,
A placeholder waits for the next little sprite,
Click, build, and bounce — workflow takes flight. ✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: enhancing workflow loading logic and improving node handling in the CreateWorkFlow component, which aligns with the core modifications in the changeset.
✨ Finishing touches
  • 📝 Generate docstrings

📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80dc6b5 and 711afdb.

📒 Files selected for processing (1)
  • apps/web/app/components/nodes/CreateWorkFlow.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/app/components/nodes/CreateWorkFlow.tsx (2)
apps/web/app/hooks/redux.ts (1)
  • useAppSelector (7-7)
apps/web/app/types/workflow.types.ts (1)
  • NodeType (1-11)

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
apps/web/app/components/nodes/CreateWorkFlow.tsx (1)

295-295: Remove debug console.log statements before merging.

This and other debug logs (lines 307, 308, 312) should be removed or wrapped in a development-only check before merging to main.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 206d0ce and 123ccf9.

📒 Files selected for processing (1)
  • apps/web/app/components/nodes/CreateWorkFlow.tsx
🔇 Additional comments (1)
apps/web/app/components/nodes/CreateWorkFlow.tsx (1)

47-49: LGTM on trigger data access refactor.

Accessing the full trigger object instead of just the name is appropriate given the new workflow loading logic that needs dbId, name, and config properties.

Comment thread apps/web/app/components/nodes/CreateWorkFlow.tsx Outdated
setNodeIDType(node.id)
setCredType(node.data.type === "google_sheet" ? "google_oauth" : "")
setCredType(node.data.type.includes("google sheet") || node.data.type.includes("google_sheet") ? "google_oauth" : "")
setLoadSheet(!loadSheet)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Toggle behavior may cause unexpected UX when switching nodes.

setLoadSheet(!loadSheet) toggles the form. If a user clicks Google Sheet node A (form opens), then clicks Google Sheet node B, the form closes instead of staying open for node B.

Consider using setLoadSheet(true) to always open, with a separate close mechanism.

🔎 Proposed fix
-            setLoadSheet(!loadSheet)
+            setLoadSheet(true)

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/web/app/components/nodes/CreateWorkFlow.tsx around line 311, the current
toggle call setLoadSheet(!loadSheet) causes the sheet form to close when
switching between Google Sheet nodes; change this to setLoadSheet(true) so
selecting any Google Sheet node always opens the form, and implement/ensure a
separate explicit close handler (e.g., onCancel/onClose that calls
setLoadSheet(false)) is used to close the form instead of relying on the toggle
behavior.

@Vamsi-o Vamsi-o left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

useEffect(()=>{

  • async function loadWorkflow(){
  • function loadWorkflow(){
  • if (!existingTrigger && existingNodes.length === 0) return;
  • const START_X = 100;
    const GAP = 250;
    const Y = 200;
    // ...
    data:{
    label: existingTrigger.name,
    name: existingTrigger.name,
  •    type: existingTrigger.name.split(" - ")[0],
    
  •    type: existingTrigger.type,
       icon: '📊',
       config: existingTrigger.config,
     }
    
    // ...
    data:{
    label: node.name,
    name: node.name,
    icon: '⚙️',
  •    type: node.name.split(" - ")[0],
    
  •    type: node.type,
       config: node.config,   
     }   Make sure fix this major bug 
    

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

♻️ Duplicate comments (2)
apps/web/app/components/nodes/CreateWorkFlow.tsx (2)

305-314: Google Sheet detection improved but needs cleanup.

The case-insensitive check on line 306 is better than the previous implementation. However:

  1. Since nodeType is already lowercase, you can simplify line 307 to just check nodeType.includes('google') && nodeType.includes('sheet') or similar
  2. Remove debug console.log statements on lines 308, 309, and 313
  3. The toggle behavior on line 312 (setLoadSheet(!loadSheet)) still has the issue flagged in the previous review—it will close the form when switching between Google Sheet nodes instead of keeping it open
🔎 Suggested improvements
          if(node.type === 'action' || node.type === 'trigger'){
-            // Check by type instead of name (more reliable)
            const nodeType = node.data.type?.toLowerCase() || '';
-            if(nodeType.includes('google_sheet') || nodeType.includes('google sheet')){
-              console.log("Google Sheet node clicked")
-              console.log("Node ID:", node.id)
+            const isGoogleSheet = nodeType.includes('google') && nodeType.includes('sheet');
+            if(isGoogleSheet){
              setNodeIDType(node.id)
              setCredType("google_oauth")
-              setLoadSheet(!loadSheet)
-              console.log("Form opened")
+              setLoadSheet(true)
            }
          }

152-214: Previous review feedback not addressed.

The issues flagged in the previous review remain unresolved:

  1. The function is declared async but doesn't await anything
  2. Lines 169 and 187 still derive type from name.split(" - ")[0] instead of using existingTrigger.type and node.type
  3. No guard to prevent overwriting the diagram when existingTrigger and existingNodes are empty

Please address the feedback from the previous review.

🧹 Nitpick comments (2)
apps/web/app/components/nodes/CreateWorkFlow.tsx (2)

47-49: Approve accessing full trigger object; remove commented debug code.

Accessing the full trigger object (line 47) is correct since loadWorkflow needs properties like name, type, and config. However, the commented-out console.log on line 49 should be removed entirely rather than left as clutter.

🔎 Remove commented debug code
- // console.log(`workflow from redux, TRigger: ${existingTrigger}, Nodes: ${existingNodes}`)

295-295: Remove debug logging.

The console.log on line 295 appears to be debug code. Remove it or wrap it in a debug flag before production.

🔎 Remove debug logging
-          console.log("Node clicked:", node.id, node.type, node.data);
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 123ccf9 and 55391cc.

📒 Files selected for processing (1)
  • apps/web/app/components/nodes/CreateWorkFlow.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/app/components/nodes/CreateWorkFlow.tsx (2)
apps/web/app/hooks/redux.ts (1)
  • useAppSelector (7-7)
apps/web/app/types/workflow.types.ts (1)
  • NodeType (1-11)
🔇 Additional comments (1)
apps/web/app/components/nodes/CreateWorkFlow.tsx (1)

13-13: LGTM! Unused import removed.

Removing the unused workflowReducer import keeps the imports clean.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
apps/web/app/components/nodes/CreateWorkFlow.tsx (1)

310-318: Previous review feedback not addressed: Toggle causes form to close when switching nodes.

The type detection improvements look good (case-insensitive, checks both formats). However, line 317 still uses setLoadSheet(!loadSheet), which was flagged in a previous review. If a user clicks Google Sheet node A (form opens), then clicks Google Sheet node B, the form closes instead of staying open for node B, creating poor UX.

🔎 Proposed fix to always open the form
             setNodeIDType(node.id)
             setCredType("google_oauth")
-            setLoadSheet(!loadSheet)
+            setLoadSheet(true)
             console.log("Form opened")
🧹 Nitpick comments (2)
apps/web/app/components/nodes/CreateWorkFlow.tsx (2)

49-49: Consider removing commented-out code.

Commented debugging code can accumulate over time. Consider removing it entirely and relying on version control if you need to reference it later.


300-300: Consider removing or conditionalizing debug logs.

Debug console.log statements can clutter production logs. Consider removing this or wrapping it in a development-only check.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 55391cc and 0918a6d.

📒 Files selected for processing (1)
  • apps/web/app/components/nodes/CreateWorkFlow.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/app/components/nodes/CreateWorkFlow.tsx (2)
apps/web/app/hooks/redux.ts (1)
  • useAppSelector (7-7)
apps/web/app/types/workflow.types.ts (1)
  • NodeType (1-11)
🔇 Additional comments (2)
apps/web/app/components/nodes/CreateWorkFlow.tsx (2)

13-13: LGTM! Clean import removal.

Removing the unused workflowReducer import keeps the codebase tidy.


47-47: LGTM! Reading the full trigger object.

Accessing the complete trigger object provides the necessary data for workflow reconstruction.

Comment on lines +152 to +219
useEffect(()=>{
// Guard: only rebuild nodes/edges if there's actual stored data
if (existingNodes.length === 0 && !existingTrigger) {
return; // Keep the current placeholder state
}

function loadWorkflow(){
const START_X = 100;
const GAP = 250;
const Y = 200;

const newNodes: NodeType[] = [];
const newEdges: EdgeType[] = [];
const type = existingTrigger?.name.split(" - ")[0]
if(existingTrigger){
newNodes.push({
id: `trigger-${existingTrigger.dbId}`,
type: 'trigger' as const,
position: { x: START_X, y: Y},
data:{
label: existingTrigger.name,
name: existingTrigger.name,
type: type,
icon: '📊',
config: existingTrigger.config,
}
})
}

existingNodes.forEach((node, index)=>{
const nodeId = `action-${node.dbId}-${index}`;
const type = node.name.split(" - ")[0]
newNodes.push({
id: nodeId,
type: 'action' as const,
position: { x: START_X + (index + 1) * GAP, y: Y},
data:{
label: node.name,
name: node.name,
icon: '⚙️',
type: type,
config: node.config,
}
});
});

const placeholderIndex = newNodes.length;
newNodes.push({
id: `placeholder-${Date.now()}`,
type: 'placeholder' as const,
position: { x: START_X + placeholderIndex * GAP, y: Y},
data: { label: '+' }
});

for(let i=0; i<newNodes.length - 1; i++){
newEdges.push({
id: `edge-${i}`,
source: newNodes[i]?.id || '',
target: newNodes[i+1]?.id || ''
});
}

setNodes(newNodes);
setEdges(newEdges)
}

loadWorkflow()
},[existingNodes, existingTrigger])

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Previous review feedback not addressed: Stop deriving type from name.

The previous review (lines 152-219) specifically requested using existingTrigger.type and node.type properties from Redux instead of extracting from name.split(" - ")[0]. Lines 165, 174, 183, and 192 still use the fragile name-splitting approach, which will break if the naming convention changes.

Additionally, Line 165 executes outside the if(existingTrigger) check on line 166. Given the guard on line 154 only returns when BOTH are empty, if existingNodes has items but existingTrigger is null, line 165 will set type to undefined, which then gets assigned on line 174.

🔎 Proposed fix using Redux type properties
   function loadWorkflow(){
     const START_X = 100;
     const GAP = 250;
     const Y = 200;

     const newNodes: NodeType[] = [];
     const newEdges: EdgeType[] = [];
-    const type = existingTrigger?.name.split(" - ")[0]
     if(existingTrigger){
       newNodes.push({
         id: `trigger-${existingTrigger.dbId}`,
         type: 'trigger' as const,
         position: { x: START_X, y: Y},
         data:{
           label: existingTrigger.name,
           name: existingTrigger.name,
-          type: type,
+          type: existingTrigger.type,
           icon: '📊',
           config: existingTrigger.config,
         }
       })
     }

     existingNodes.forEach((node, index)=>{
       const nodeId = `action-${node.dbId}-${index}`;
-      const type = node.name.split(" - ")[0]
       newNodes.push({
         id: nodeId,
         type: 'action' as const,
         position: { x: START_X + (index + 1) * GAP, y: Y},
         data:{
           label: node.name,
           name: node.name,
           icon: '⚙️',
-          type: type,
+          type: node.type,
           config: node.config,
         }
       });
     });
📝 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(()=>{
// Guard: only rebuild nodes/edges if there's actual stored data
if (existingNodes.length === 0 && !existingTrigger) {
return; // Keep the current placeholder state
}
function loadWorkflow(){
const START_X = 100;
const GAP = 250;
const Y = 200;
const newNodes: NodeType[] = [];
const newEdges: EdgeType[] = [];
const type = existingTrigger?.name.split(" - ")[0]
if(existingTrigger){
newNodes.push({
id: `trigger-${existingTrigger.dbId}`,
type: 'trigger' as const,
position: { x: START_X, y: Y},
data:{
label: existingTrigger.name,
name: existingTrigger.name,
type: type,
icon: '📊',
config: existingTrigger.config,
}
})
}
existingNodes.forEach((node, index)=>{
const nodeId = `action-${node.dbId}-${index}`;
const type = node.name.split(" - ")[0]
newNodes.push({
id: nodeId,
type: 'action' as const,
position: { x: START_X + (index + 1) * GAP, y: Y},
data:{
label: node.name,
name: node.name,
icon: '⚙️',
type: type,
config: node.config,
}
});
});
const placeholderIndex = newNodes.length;
newNodes.push({
id: `placeholder-${Date.now()}`,
type: 'placeholder' as const,
position: { x: START_X + placeholderIndex * GAP, y: Y},
data: { label: '+' }
});
for(let i=0; i<newNodes.length - 1; i++){
newEdges.push({
id: `edge-${i}`,
source: newNodes[i]?.id || '',
target: newNodes[i+1]?.id || ''
});
}
setNodes(newNodes);
setEdges(newEdges)
}
loadWorkflow()
},[existingNodes, existingTrigger])
useEffect(()=>{
// Guard: only rebuild nodes/edges if there's actual stored data
if (existingNodes.length === 0 && !existingTrigger) {
return; // Keep the current placeholder state
}
function loadWorkflow(){
const START_X = 100;
const GAP = 250;
const Y = 200;
const newNodes: NodeType[] = [];
const newEdges: EdgeType[] = [];
if(existingTrigger){
newNodes.push({
id: `trigger-${existingTrigger.dbId}`,
type: 'trigger' as const,
position: { x: START_X, y: Y},
data:{
label: existingTrigger.name,
name: existingTrigger.name,
type: existingTrigger.type,
icon: '📊',
config: existingTrigger.config,
}
})
}
existingNodes.forEach((node, index)=>{
const nodeId = `action-${node.dbId}-${index}`;
newNodes.push({
id: nodeId,
type: 'action' as const,
position: { x: START_X + (index + 1) * GAP, y: Y},
data:{
label: node.name,
name: node.name,
icon: '⚙️',
type: node.type,
config: node.config,
}
});
});
const placeholderIndex = newNodes.length;
newNodes.push({
id: `placeholder-${Date.now()}`,
type: 'placeholder' as const,
position: { x: START_X + placeholderIndex * GAP, y: Y},
data: { label: '+' }
});
for(let i=0; i<newNodes.length - 1; i++){
newEdges.push({
id: `edge-${i}`,
source: newNodes[i]?.id || '',
target: newNodes[i+1]?.id || ''
});
}
setNodes(newNodes);
setEdges(newEdges)
}
loadWorkflow()
},[existingNodes, existingTrigger])
🤖 Prompt for AI Agents
In apps/web/app/components/nodes/CreateWorkFlow.tsx around lines 152 to 219, the
code still derives node types by splitting the name (lines ~165,174,183,192) and
declares the trigger type outside the existingTrigger guard which can produce
undefined; replace uses of name.split(" - ")[0] with the authoritative
existingTrigger.type and node.type values from Redux, move the trigger type
declaration inside the if(existingTrigger) block so it’s only read when
existingTrigger exists, and ensure each newNodes.data.type is assigned from
those Redux properties (existingTrigger.type for the trigger node and node.type
for action nodes) instead of parsing the name.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (3)
apps/web/app/components/nodes/CreateWorkFlow.tsx (3)

166-175: Previous review feedback not addressed: Use type property instead of parsing name.

Line 166 derives type from existingTrigger?.name.split(" - ")[0] rather than using existingTrigger.type directly from Redux. This fragile approach will break if the naming convention changes. The same issue applies to line 184 for action nodes.

🔎 Proposed fix using Redux type properties
     const newNodes: NodeType[] = [];
     const newEdges: EdgeType[] = [];
-    const type = existingTrigger?.name.split(" - ")[0]
     if(existingTrigger){
       newNodes.push({
         id: `trigger-${existingTrigger.dbId}`,
         type: 'trigger' as const,
         position: { x: START_X, y: Y},
         data:{
           label: existingTrigger.name,
           name: existingTrigger.name,
-          type: type,
+          type: existingTrigger.type,
           icon: '📊',
           config: existingTrigger.config,
         }
       })
     }

182-197: Same issue: Derive type from node.type instead of parsing name.

Line 184 uses node.name.split(" - ")[0] instead of node.type.

🔎 Proposed fix
     existingNodes.forEach((node, index)=>{
       const nodeId = `action-${node.dbId}-${index}`;
-      const type = node.name.split(" - ")[0]
       newNodes.push({
         id: nodeId,
         type: 'action' as const,
         position: { x: START_X + (index + 1) * GAP, y: Y},
         data:{
           label: node.name,
           name: node.name,
           icon: '⚙️',
-          type: type,
+          type: node.type,
           config: node.config,
         }
       });
     });

320-320: Toggle behavior may cause unexpected UX when switching nodes.

setLoadSheet(!loadSheet) toggles the form. If a user clicks Google Sheet node A (form opens), then clicks Google Sheet node B, the form closes instead of staying open for node B. Consider using setLoadSheet(true) to always open.

🔎 Proposed fix
-            setLoadSheet(!loadSheet)
+            setLoadSheet(true)
🧹 Nitpick comments (2)
apps/web/app/components/nodes/GoogleSheetFormClient.tsx (2)

40-44: Remove debug console.log statements before merging.

These logging statements appear to be debugging artifacts and should be removed or replaced with proper logging infrastructure for production code.

🔎 Proposed fix
-  console.log('initial data: ', initialData)
-  console.log('initial document: ', selectedDocument)
-  console.log('initial sheet: ', selectedSheet)
-  console.log('initial range: ', range)
-  console.log("initial operation: ",operation)

50-54: Consider defensive handling for nodeType parsing.

If nodeType doesn't contain ~, split("~")[1] returns undefined, which then becomes an empty string. This is handled, but the parsing convention couples this component tightly to the ID format used in CreateWorkFlow. Consider documenting this contract or passing nodeId as a separate prop for clarity.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0918a6d and 80dc6b5.

📒 Files selected for processing (2)
  • apps/web/app/components/nodes/CreateWorkFlow.tsx
  • apps/web/app/components/nodes/GoogleSheetFormClient.tsx
🧰 Additional context used
🧬 Code graph analysis (2)
apps/web/app/components/nodes/GoogleSheetFormClient.tsx (3)
apps/web/app/hooks/redux.ts (1)
  • useAppSelector (7-7)
apps/web/app/hooks/useCredential.ts (1)
  • useCredentials (5-33)
packages/common/src/index.ts (1)
  • BACKEND_URL (4-4)
apps/web/app/components/nodes/CreateWorkFlow.tsx (3)
apps/web/app/hooks/redux.ts (1)
  • useAppSelector (7-7)
apps/web/app/types/workflow.types.ts (1)
  • NodeType (1-11)
apps/web/app/components/nodes/GoogleSheetFormClient.tsx (1)
  • GoogleSheetFormClient (27-368)
🔇 Additional comments (2)
apps/web/app/components/nodes/CreateWorkFlow.tsx (2)

310-322: LGTM on the Google Sheet type detection fix.

The type detection logic now uses case-insensitive comparison and correctly sets credType to "google_oauth". This addresses the previous review feedback about inconsistent type detection.


153-220: Good addition of workflow loading logic with proper guard.

The useEffect correctly guards against rebuilding nodes/edges when there's no stored data (lines 155-157), and the loadWorkflow function properly constructs the node graph with positions, edges, and a trailing placeholder. The dependency array correctly includes existingNodes and existingTrigger.

Comment on lines +343 to +349
initialData={selectedNodeConfig ? {
range: selectedNodeConfig.range as string,
operation: selectedNodeConfig.operation as string,
sheetName: selectedNodeConfig.sheetName as string,
spreadSheetId: selectedNodeConfig.spreadsheetId as string,
credentialId: selectedNodeConfig.credId as string,
} : undefined}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: Property name mismatch will cause credentialId to not be pre-populated.

Line 348 reads selectedNodeConfig.credId, but GoogleSheetFormClient.handleSaveClick saves the config with credentialId (see line 194 in GoogleSheetFormClient.tsx). This mismatch means the credential will never be pre-populated when editing an existing node.

🔎 Proposed fix
       initialData={selectedNodeConfig ? {
         range: selectedNodeConfig.range as string,
         operation: selectedNodeConfig.operation as string,
         sheetName: selectedNodeConfig.sheetName as string,
         spreadSheetId: selectedNodeConfig.spreadsheetId as string,
-        credentialId: selectedNodeConfig.credId as string,
+        credentialId: selectedNodeConfig.credentialId as string,
       } : undefined}
🤖 Prompt for AI Agents
In apps/web/app/components/nodes/CreateWorkFlow.tsx around lines 343 to 349, the
initialData uses selectedNodeConfig.credId but the saved config property is
credentialId, so credentials never pre-populate; change the reference to
selectedNodeConfig.credentialId (and keep types consistent) so
initialData.spreadSheetId and initialData.credentialId match the keys used when
saving, ensuring the form pre-fills the credential field when editing.

Comment on lines +62 to +85
useEffect(() => {
const fetchInitialDocuments = async () => {
if (!initialData?.credentialId) return;

setLoading(true);
try {
const res = await fetch(`${BACKEND_URL}/node/getDocuments/${initialData.credentialId}`, {
method: 'GET',
credentials: "include",
headers: { 'Content-Type': 'application/json' },
});
const data = await res.json();
if (data.files?.length > 0) {
setDocuments(data.files);
}
} catch (error) {
console.error('Failed to fetch initial documents:', error);
} finally {
setLoading(false);
}
};

fetchInitialDocuments();
}, [initialData?.credentialId]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add HTTP response status check to prevent silent failures.

The fetch does not check res.ok before parsing JSON. If the server returns a 4xx/5xx error, res.json() may fail or return unexpected data, causing silent failures or misleading UI states.

🔎 Proposed fix
       const res = await fetch(`${BACKEND_URL}/node/getDocuments/${initialData.credentialId}`, {
         method: 'GET',
         credentials: "include",
         headers: { 'Content-Type': 'application/json' },
       });
+      if (!res.ok) {
+        throw new Error(`Failed to fetch documents: ${res.status}`);
+      }
       const data = await res.json();
📝 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 fetchInitialDocuments = async () => {
if (!initialData?.credentialId) return;
setLoading(true);
try {
const res = await fetch(`${BACKEND_URL}/node/getDocuments/${initialData.credentialId}`, {
method: 'GET',
credentials: "include",
headers: { 'Content-Type': 'application/json' },
});
const data = await res.json();
if (data.files?.length > 0) {
setDocuments(data.files);
}
} catch (error) {
console.error('Failed to fetch initial documents:', error);
} finally {
setLoading(false);
}
};
fetchInitialDocuments();
}, [initialData?.credentialId]);
useEffect(() => {
const fetchInitialDocuments = async () => {
if (!initialData?.credentialId) return;
setLoading(true);
try {
const res = await fetch(`${BACKEND_URL}/node/getDocuments/${initialData.credentialId}`, {
method: 'GET',
credentials: "include",
headers: { 'Content-Type': 'application/json' },
});
if (!res.ok) {
throw new Error(`Failed to fetch documents: ${res.status}`);
}
const data = await res.json();
if (data.files?.length > 0) {
setDocuments(data.files);
}
} catch (error) {
console.error('Failed to fetch initial documents:', error);
} finally {
setLoading(false);
}
};
fetchInitialDocuments();
}, [initialData?.credentialId]);
🤖 Prompt for AI Agents
In apps/web/app/components/nodes/GoogleSheetFormClient.tsx around lines 62 to
85, the fetch call parses res.json() without checking HTTP status which can
cause silent failures on 4xx/5xx responses; update the code to inspect res.ok
after the fetch, and if not ok read any error body (await res.text() or
res.json() guarded) and log or surface that error (and avoid calling
setDocuments), otherwise parse and use the JSON; ensure loading is cleared in
error and non-ok branches and return early on non-ok to prevent using invalid
data.

Comment on lines +88 to +111
useEffect(() => {
const fetchInitialSheets = async () => {
if (!initialData?.credentialId || !initialData?.spreadSheetId) return;

setLoading(true);
try {
const res = await fetch(`${BACKEND_URL}/node/getSheets/${initialData.credentialId}/${initialData.spreadSheetId}`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
credentials: "include",
});
const data = await res.json();
if (data.files?.data?.length > 0) {
setSheets(data.files.data);
}
} catch (error) {
console.error('Failed to fetch initial sheets:', error);
} finally {
setLoading(false);
}
};

fetchInitialSheets();
}, [initialData?.credentialId, initialData?.spreadSheetId]);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add HTTP response status check (same issue as above).

Same concern applies here—missing res.ok check before parsing. Also note the response structure differs here (data.files?.data?.length) versus the documents fetch (data.files?.length), which could indicate inconsistent API responses or a bug in one of the checks.

🔎 Proposed fix
       const res = await fetch(`${BACKEND_URL}/node/getSheets/${initialData.credentialId}/${initialData.spreadSheetId}`, {
         method: 'GET',
         headers: { 'Content-Type': 'application/json' },
         credentials: "include",
       });
+      if (!res.ok) {
+        throw new Error(`Failed to fetch sheets: ${res.status}`);
+      }
       const data = await res.json();
📝 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 fetchInitialSheets = async () => {
if (!initialData?.credentialId || !initialData?.spreadSheetId) return;
setLoading(true);
try {
const res = await fetch(`${BACKEND_URL}/node/getSheets/${initialData.credentialId}/${initialData.spreadSheetId}`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
credentials: "include",
});
const data = await res.json();
if (data.files?.data?.length > 0) {
setSheets(data.files.data);
}
} catch (error) {
console.error('Failed to fetch initial sheets:', error);
} finally {
setLoading(false);
}
};
fetchInitialSheets();
}, [initialData?.credentialId, initialData?.spreadSheetId]);
useEffect(() => {
const fetchInitialSheets = async () => {
if (!initialData?.credentialId || !initialData?.spreadSheetId) return;
setLoading(true);
try {
const res = await fetch(`${BACKEND_URL}/node/getSheets/${initialData.credentialId}/${initialData.spreadSheetId}`, {
method: 'GET',
headers: { 'Content-Type': 'application/json' },
credentials: "include",
});
if (!res.ok) {
throw new Error(`Failed to fetch sheets: ${res.status}`);
}
const data = await res.json();
if (data.files?.data?.length > 0) {
setSheets(data.files.data);
}
} catch (error) {
console.error('Failed to fetch initial sheets:', error);
} finally {
setLoading(false);
}
};
fetchInitialSheets();
}, [initialData?.credentialId, initialData?.spreadSheetId]);
🤖 Prompt for AI Agents
In apps/web/app/components/nodes/GoogleSheetFormClient.tsx around lines 88 to
111, the effect is parsing the fetch response without checking res.ok and
assumes a specific response shape (data.files?.data?.length) which differs from
other fetches; update the code to first check if (!res.ok) and handle/log the
non-2xx response (optionally parse error body) and return early, then call
res.json() only for successful responses, and normalize the payload when setting
sheets to accept both shapes (e.g., prefer data.files.data if present, otherwise
data.files) before calling setSheets; ensure setLoading is still toggled in
finally.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/app/components/nodes/CreateWorkFlow.tsx (1)

291-305: Missing node tracking state prevents pre-populating Google Sheet form when editing.

When a Google Sheet node is clicked (lines 292-304), the code sets credType and toggles loadSheet, but doesn't store which specific node was clicked or its configuration. Line 324 renders GoogleSheetFormClient without an initialData prop.

According to the AI summary, GoogleSheetFormClient now accepts an optional initialData prop to pre-populate the form with existing node configuration (credential, document, sheet, operation, range). Without tracking the clicked node's data, users cannot edit existing Google Sheet nodes—the form always opens empty.

🔎 Proposed solution

Add state to track the selected node:

   const [loadSheet, setLoadSheet] = useState<boolean>(false);
+  const [selectedNodeId, setSelectedNodeId] = useState<string>("");
+  const [selectedNodeConfig, setSelectedNodeConfig] = useState<Record<string, unknown> | null>(null);

Store the node data when clicked:

         if (node.type === "action" || node.type === "trigger") {
           if (node.data.name === "Google Sheet") {
             console.log("sheet called");
             console.log(node.id);
+            setSelectedNodeId(node.id);
+            setSelectedNodeConfig(node.data.config || null);
             setCredType(
               node.data.type === "google_sheet" ? "google_oauth" : ""
             );

Pass initialData to the form:

-      {loadSheet && <GoogleSheetFormClient type={credType} />}
+      {loadSheet && <GoogleSheetFormClient 
+        type={credType} 
+        nodeType={selectedNodeId}
+        initialData={selectedNodeConfig ? {
+          range: selectedNodeConfig.range as string,
+          operation: selectedNodeConfig.operation as string,
+          sheetName: selectedNodeConfig.sheetName as string,
+          spreadSheetId: selectedNodeConfig.spreadsheetId as string,
+          credentialId: selectedNodeConfig.credentialId as string,
+        } : undefined}
+      />}
♻️ Duplicate comments (2)
apps/web/app/components/nodes/CreateWorkFlow.tsx (2)

282-305: Consider the UX impact of toggle behavior when switching between Google Sheet nodes.

Line 302 uses setLoadSheet(!loadSheet) which toggles the form. If a user clicks Google Sheet node A (form opens), then clicks Google Sheet node B, the form will close instead of staying open for node B. This could be confusing.

Consider using setLoadSheet(true) to always open the form when clicking a Google Sheet node, with a separate explicit close mechanism.

🔎 Proposed fix
           setCredType(
             node.data.type === "google_sheet" ? "google_oauth" : ""
           );
-          setLoadSheet(!loadSheet);
+          setLoadSheet(true);
           console.log("hook called");

135-202: Critical issue from previous review remains unaddressed: Stop deriving type from name.

Lines 148 and 166 still parse the type by splitting name on " - ", which is fragile and will break if the naming convention changes. The previous review explicitly requested using the authoritative existingTrigger.type and node.type properties from Redux instead.

Additionally, line 148 declares type outside the if(existingTrigger) guard on line 149, which means when existingNodes has items but existingTrigger is null (allowed by the guard on line 137), type will be undefined and then assigned to data.type on line 157.

🔎 Proposed fix using Redux type properties
   function loadWorkflow(){
     const START_X = 100;
     const GAP = 250;
     const Y = 200;

     const newNodes: NodeType[] = [];
     const newEdges: EdgeType[] = [];
-    const type = existingTrigger?.name.split(" - ")[0]
     if(existingTrigger){
       newNodes.push({
         id: `trigger-${existingTrigger.dbId}`,
         type: 'trigger' as const,
         position: { x: START_X, y: Y},
         data:{
           label: existingTrigger.name,
           name: existingTrigger.name,
-          type: type,
+          type: existingTrigger.type,
           icon: '📊',
           config: existingTrigger.config,
         }
       })
     }

     existingNodes.forEach((node, index)=>{
       const nodeId = `action-${node.dbId}-${index}`;
-      const type = node.name.split(" - ")[0]
       newNodes.push({
         id: nodeId,
         type: 'action' as const,
         position: { x: START_X + (index + 1) * GAP, y: Y},
         data:{
           label: node.name,
           name: node.name,
           icon: '⚙️',
-          type: type,
+          type: node.type,
           config: node.config,
         }
       });
     });
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80dc6b5 and 711afdb.

📒 Files selected for processing (1)
  • apps/web/app/components/nodes/CreateWorkFlow.tsx
🧰 Additional context used
🧬 Code graph analysis (1)
apps/web/app/components/nodes/CreateWorkFlow.tsx (2)
apps/web/app/hooks/redux.ts (1)
  • useAppSelector (7-7)
apps/web/app/types/workflow.types.ts (1)
  • NodeType (1-11)

@Vamsi-o
Vamsi-o merged commit 37bf1e2 into main Jan 2, 2026
3 checks passed
@coderabbitai coderabbitai Bot mentioned this pull request Jan 27, 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.

3 participants