From 7be841cfc9d4cfff5ac43ce817e8336f6afa5eec Mon Sep 17 00:00:00 2001 From: Vamsi_0 Date: Thu, 15 Jan 2026 21:33:37 +0530 Subject: [PATCH 1/2] refactor: clean up user routes and improve logging in workflow components - Simplified error handling in userRoutes for better readability. - Removed unnecessary whitespace and improved console log formatting in WorkflowCanvas and ConfigModal components. - Updated Prisma schema to enhance relationships between Credential and Trigger models, ensuring better data integrity. --- .../components/{NODES => nodes}/BaseNode.tsx | 16 +- apps/web/app/lib/api.ts | 14 +- .../app/lib/nodeConfigs/googleSheet.action.ts | 2 +- .../app/lib/nodeConfigs/webhook.trigger.ts | 80 +++--- .../workflows/[id]/components/ConfigModal.tsx | 266 +++++++++++++++--- apps/web/app/workflows/[id]/page.tsx | 33 ++- 6 files changed, 309 insertions(+), 102 deletions(-) rename apps/web/app/components/{NODES => nodes}/BaseNode.tsx (92%) diff --git a/apps/web/app/components/NODES/BaseNode.tsx b/apps/web/app/components/nodes/BaseNode.tsx similarity index 92% rename from apps/web/app/components/NODES/BaseNode.tsx rename to apps/web/app/components/nodes/BaseNode.tsx index 3eae0a9..daed121 100644 --- a/apps/web/app/components/NODES/BaseNode.tsx +++ b/apps/web/app/components/nodes/BaseNode.tsx @@ -99,21 +99,15 @@ export default function BaseNode({ id, type, data }: BaseNodeProps) { {icon || "📦"} {label} - {data.onConfigure && ( + {data.onConfigure ? ( ✓ Configured + ) : ( + + Not Configured + )} - {/* Show config summary if exists */} - {/* {config && ( -
- {config.summary - ? config.summary - : config.description - ? config.description - : "Configured"} -
- )} */} {/* Buttons */}
diff --git a/apps/web/app/lib/api.ts b/apps/web/app/lib/api.ts index a785d8e..14d28c9 100644 --- a/apps/web/app/lib/api.ts +++ b/apps/web/app/lib/api.ts @@ -2,7 +2,8 @@ import axios from "axios"; import { BACKEND_URL, NodeUpdateSchema } from "@repo/common/zod"; import { TriggerUpdateSchema } from "@repo/common/zod"; -import z from "zod" +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 = { workflows: { @@ -33,7 +34,7 @@ export const api = { headers: { "Content-Type": "application/json" }, }), update: async (data: z.infer) => { - await axios.put(`${BACKEND_URL}/user/update/trigger`, data, { + return await axios.put(`${BACKEND_URL}/user/update/trigger`, data, { withCredentials: true, headers: { "Content-Type": "application/json" }, }); @@ -57,11 +58,14 @@ export const api = { }), }, Credentials: { - getCredentials: async (type: string) => - await axios.get(`${BACKEND_URL}/user/getCredentials/${type}`, { + getCredentials: async (type: string) => { + const res = await axios.get(`${BACKEND_URL}/user/getCredentials/${type}`, { withCredentials: true, headers: { "Content-Type": "application/json" }, - }), + }); + return res.data + }, + getAllCreds: async () => await axios.get(`${BACKEND_URL}/user/getAllCreds`, { withCredentials: true, diff --git a/apps/web/app/lib/nodeConfigs/googleSheet.action.ts b/apps/web/app/lib/nodeConfigs/googleSheet.action.ts index 35b049b..b950ca9 100644 --- a/apps/web/app/lib/nodeConfigs/googleSheet.action.ts +++ b/apps/web/app/lib/nodeConfigs/googleSheet.action.ts @@ -3,7 +3,7 @@ import { NodeConfig } from "../types/node.types"; export const googleSheetActionConfig: NodeConfig = { id: "google_sheet", type: "action", - label: "Google Sheets", + label: "Google Sheet", icon: "📊", description: "Read or write data to Google Sheets", credentials: "google", // Requires Google OAuth diff --git a/apps/web/app/lib/nodeConfigs/webhook.trigger.ts b/apps/web/app/lib/nodeConfigs/webhook.trigger.ts index 10017ab..3b624ed 100644 --- a/apps/web/app/lib/nodeConfigs/webhook.trigger.ts +++ b/apps/web/app/lib/nodeConfigs/webhook.trigger.ts @@ -1,4 +1,38 @@ -// import { NodeConfig } from '../types/node.types'; +import { NodeConfig } from '../types/node.types'; +// +export const webhookTriggerConfig: NodeConfig = { + id: "webhook", + type: "trigger", + label: "Webhook", + icon: "📡", + description: "Trigger workflow on HTTP request", + fields: [ + { + name: "path", + label: "Webhook Path", + type: "text", + required: true, + placeholder: "/api/webhook/12345", + description: "The HTTP path where this webhook will listen. Must be unique per workflow." + }, + { + name: "method", + label: "HTTP Method", + type: "dropdown", + required: true, + options: [ + { label: "POST", value: "POST" }, + { label: "GET", value: "GET" } + ], + defaultValue: "POST", + description: "The HTTP method to accept (typically POST)." + } + ], + summary: "Listen for HTTP requests on a unique webhook URL.", + helpUrl: "https://docs.example.com/webhook-trigger" +}; + +// import { NodeConfig } from "../types/node.types"; // export const webhookTriggerConfig: NodeConfig = { // id: "webhook", @@ -6,44 +40,10 @@ // label: "Webhook", // icon: "📡", // description: "Trigger workflow on HTTP request", -// fields: [ -// { -// name: "path", -// label: "Webhook Path", -// type: "text", -// required: true, -// placeholder: "/api/webhook/12345", -// description: "The HTTP path where this webhook will listen. Must be unique per workflow." -// }, -// { -// name: "method", -// label: "HTTP Method", -// type: "dropdown", -// required: true, -// options: [ -// { label: "POST", value: "POST" }, -// { label: "GET", value: "GET" } -// ], -// defaultValue: "POST", -// description: "The HTTP method to accept (typically POST)." -// } -// ], -// summary: "Listen for HTTP requests on a unique webhook URL.", -// helpUrl: "https://docs.example.com/webhook-trigger" -// }; -import { NodeConfig } from "../types/node.types"; +// // NO FIELDS! URL is auto-generated +// fields: [], -export const webhookTriggerConfig: NodeConfig = { - id: "webhook", - type: "trigger", - label: "Webhook", - icon: "📡", - description: "Trigger workflow on HTTP request", - - // NO FIELDS! URL is auto-generated - fields: [], - - summary: "Receives HTTP requests to trigger workflow execution", - helpUrl: "https://docs.example.com/webhook-trigger", -}; +// summary: "Receives HTTP requests to trigger workflow execution", +// helpUrl: "https://docs.example.com/webhook-trigger", +// }; diff --git a/apps/web/app/workflows/[id]/components/ConfigModal.tsx b/apps/web/app/workflows/[id]/components/ConfigModal.tsx index 70525f9..15757b4 100644 --- a/apps/web/app/workflows/[id]/components/ConfigModal.tsx +++ b/apps/web/app/workflows/[id]/components/ConfigModal.tsx @@ -1,14 +1,15 @@ "use client"; - import { getNodeConfig } from "@/app/lib/nodeConfigs"; -import { useState } from "react"; +import { useEffect, useState } from "react"; import { HOOKS_URL } from "@repo/common/zod"; import { useAppSelector } from "@/app/hooks/redux"; +import { toast } from "sonner"; +import { api } from "@/app/lib/api"; interface ConfigModalProps { isOpen: boolean; selectedNode: any | null; onClose: () => void; - onSave: (config: any, userId: string) => Promise; + onSave: (selectedNode: string, config: any, userId: string) => Promise; } export default function ConfigModal({ @@ -17,42 +18,204 @@ export default function ConfigModal({ onClose, onSave, }: ConfigModalProps) { + const [config, setConfig] = useState>({}); + const [credentials, setCredentials] = useState([]); const [loading, setLoading] = useState(false); - const userId = useAppSelector( - (state) => state.user.userId - ) as unknown as string; - console.log("we are getting this userId from ConfigModal", userId); + const userId = useAppSelector((state) => state.user.userId) as string; + + // Fetch credentials only when required on node change + useEffect(() => { + setConfig({}); // Clear form when switching nodes + setCredentials([]); // Clear credentials on node switch + if (selectedNode) { + const nodeConfig = getNodeConfig(selectedNode.data?.nodeType); + if (nodeConfig && nodeConfig.credentials === "google") { + api.Credentials.getCredentials("google").then((res) => { + setCredentials(res.data.Data || []); + }); + } + } + // Removed console log pollution + }, [selectedNode]); + + // No modal if not needed if (!isOpen || !selectedNode) return null; + // Save callback const handleSave = async () => { setLoading(true); try { - // For now, just save empty config - await onSave({ HOOKS_URL }, userId); + await onSave(selectedNode.id, config, userId); + toast.success("Configured Successfully"); } catch (error) { console.error("Save failed:", error); + toast.error("Failed to save config"); } finally { setLoading(false); onClose(); } }; + // Field rendering helper + const renderField = (field: any) => { + const fieldValue = config[field.name] || ""; + + // Special handling for Google credential dropdown + if (field.type === "dropdown" && field.name === "credentialId") { + return ( +
+ + {/* No credentials connected */} + {credentials.length === 0 ? ( + <> + +

+ Connect your Google account to use Gmail & Sheets +

+ + ) : ( + <> + +
+ {credentials.map((cred: any) => ( + + {cred.email || cred.name} + + ))} +
+ + )} +
+ ); + } + + // Generic dropdown + if (field.type === "dropdown") { + return ( +
+ + +
+ ); + } + + // Textarea + if (field.type === "textarea") { + return ( +
+ +