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..a632549 100644 --- a/apps/web/app/components/NODES/BaseNode.tsx +++ b/apps/web/app/components/nodes/BaseNode.tsx @@ -8,6 +8,7 @@ interface BaseNodeProps { isPlaceholder?: boolean; config: any; nodeType?: "trigger" | "action"; + isConfigured?: boolean; status?: "idle" | "running" | "success" | "error"; onConfigure?: () => void; @@ -99,21 +100,15 @@ export default function BaseNode({ id, type, data }: BaseNodeProps) { {icon || "📦"} {label} - {data.onConfigure && ( + {data.isConfigured ? ( ✓ 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..97a6e7d 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.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..6d89bae 100644 --- a/apps/web/app/lib/nodeConfigs/webhook.trigger.ts +++ b/apps/web/app/lib/nodeConfigs/webhook.trigger.ts @@ -1,5 +1,5 @@ // import { NodeConfig } from '../types/node.types'; - +// // // export const webhookTriggerConfig: NodeConfig = { // id: "webhook", // type: "trigger", diff --git a/apps/web/app/workflows/[id]/components/ConfigModal.tsx b/apps/web/app/workflows/[id]/components/ConfigModal.tsx index 70525f9..d8cb98a 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,207 @@ 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.name || selectedNode.actionType); + if (nodeConfig && nodeConfig.credentials === "google") { + api.Credentials.getCredentials("google").then((res) => { + setCredentials(res || []); + }).catch((error) => { + console.error("Failed to fetch credentials:", error); + setCredentials([]); + }); + } + } + // 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 ( +
+ +