From 9f60d5891fe96a838b9e277edb52aad3f14e2bc6 Mon Sep 17 00:00:00 2001 From: BUDUMURU SRINIVAS SAI SARAN TEJA Date: Thu, 26 Mar 2026 21:07:06 +0530 Subject: [PATCH] Remove obsolete components and refactor API interactions for workflow management - Deleted unused components: CreateWorkFlow, GoogleSheetFormClient, and actions.ts. - Refactored API calls to streamline workflow data fetching and management. - Updated workflow slice to handle backend data structure changes. - Improved sidebar functionality for workflow selection and management. --- .../app/components/nodes/CreateWorkFlow.tsx | 360 --------------- .../nodes/GoogleSheetFormClient.tsx | 421 ------------------ apps/web/app/components/nodes/actions.ts | 106 ----- apps/web/app/components/ui/Logs.tsx | 0 apps/web/app/components/ui/app-sidebar.tsx | 117 ++--- apps/web/app/hooks/useCredential.ts | 4 +- apps/web/app/hooks/useTriggers.ts | 8 +- apps/web/app/lib/api.ts | 11 +- apps/web/app/workflow/lib/config.ts | 286 ------------ apps/web/app/workflow/page.tsx | 27 -- apps/web/app/workflows/[id]/page.tsx | 44 +- apps/web/store/slices/workflowSlice.ts | 56 +++ 12 files changed, 135 insertions(+), 1305 deletions(-) delete mode 100644 apps/web/app/components/nodes/CreateWorkFlow.tsx delete mode 100644 apps/web/app/components/nodes/GoogleSheetFormClient.tsx delete mode 100644 apps/web/app/components/nodes/actions.ts create mode 100644 apps/web/app/components/ui/Logs.tsx delete mode 100644 apps/web/app/workflow/lib/config.ts delete mode 100644 apps/web/app/workflow/page.tsx diff --git a/apps/web/app/components/nodes/CreateWorkFlow.tsx b/apps/web/app/components/nodes/CreateWorkFlow.tsx deleted file mode 100644 index 7eb41bf..0000000 --- a/apps/web/app/components/nodes/CreateWorkFlow.tsx +++ /dev/null @@ -1,360 +0,0 @@ -"use client"; -import { useEffect, useState } from "react"; -import "@xyflow/react/dist/style.css"; -import { ReactFlow } from "@xyflow/react"; -import PlaceholderNode from "./PlaceHolder"; -import { TriggerNode } from "./TriggerNode"; - -import { TriggerSideBar } from "./TriggerSidebar"; -import ActionSideBar from "../Actions/ActionSidebar"; -import ActionNode from "../Actions/ActionNode"; -import { GoogleSheetFormClient } from "./GoogleSheetFormClient"; -import { useDispatch } from "react-redux"; -import { workflowActions } from "@/store/slices/workflowSlice"; -import { createWorkflow, getEmptyWorkflow, getworkflowData } from "@/app/workflow/lib/config"; -import { useAppSelector } from '@/app/hooks/redux'; - - - -interface NodeType { - id: string; - type: "placeholder" | "trigger" | "action"; - position: { x: number; y: number }; - data: { - label: string; - name?: string; - icon?: string; - type?: string; - config?: Record; - }; -} - -interface EdgeType { - id: string; - source: string; - target: string; -} - -export const CreateWorkFlow = () => { - const [sidebarOpen, setSidebarOpen] = useState(false); - const [actionSidebarOpen, setActionSidebarOpen] = useState(false); - const [credType, setCredType] = useState(""); - const [nodeIDType, setNodeIDType] = useState('') - const [loadSheet, setLoadSheet] = useState(false) - const [selectedNodeConfig, setSelectedNodeConfig] = useState(undefined); - const dispatch = useDispatch(); - const userId = useAppSelector(s=>s.user.userId) - const workflowId = useAppSelector(s=>s.workflow.workflow_id) - const existingTrigger = useAppSelector(s=>s.workflow.trigger) - const existingNodes = useAppSelector(s=>s.workflow.nodes) - console.log(`workflow from redux, TRigger: ${existingTrigger?.AvailableTriggerID}, Nodes: ${existingNodes}`) - console.log('redux workflow from createWorkflow: ',workflowId) - - const [nodes, setNodes] = useState([ - { - id: "1", - type: "placeholder", - position: { x: 100, y: 200 }, - data: { - label: "+", - }, - }, - ]); - - const [edges, setEdges] = useState([]); - - const nodeTypes = { - placeholder: PlaceholderNode, - trigger: TriggerNode, - action: ActionNode, - }; - - // Handle trigger selection - const handleSelectTrigger = (trigger: { - id: string; - name: string; - type: string; - icon?: string; - }) => { - const timestamp = Date.now(); - const placeholderId = `placeholder-${timestamp}`; - const triggerNodeId = `trigger~${trigger.id}`; - const edgeId = `e-${triggerNodeId}-${placeholderId}`; - - setNodes((currentNodes) => { - const placeholderNode = currentNodes.find( - (n) => n.type === "placeholder" - ); - if (!placeholderNode) return currentNodes; - - const triggerNode: NodeType = { - id: triggerNodeId, - type: "trigger", - position: placeholderNode.position, - data: { - label: trigger.name, - name: trigger.name, - icon: trigger.icon || "⚡", - type: trigger.type, - }, - }; - - const newPlaceholder: NodeType = { - id: placeholderId, - type: "placeholder", - position: { - x: placeholderNode.position.x + 250, - y: placeholderNode.position.y, - }, - data: { label: "+" }, - }; - - return [triggerNode, newPlaceholder]; - }); - - setEdges([ - { - id: edgeId, - source: triggerNodeId, - target: placeholderId, - }, - ]); - }; - - useEffect(()=>{ - async function getEmptyWorkflowID(){ - const workflow = await getEmptyWorkflow() - - if(workflow){ - const {id, isEmpty} = workflow - dispatch(workflowActions.setWorkflowId(id)) - dispatch(workflowActions.setWorkflowStatus(isEmpty)) - } - else{ - if (!userId) return - const newWorkflow = await createWorkflow() - dispatch(workflowActions.setWorkflowId(newWorkflow.id)) - dispatch(workflowActions.setWorkflowStatus(newWorkflow.isEmpty)) - } - } - async function getWorkflowData(){ - if(!workflowId) return - const workflow = await getworkflowData(workflowId) - if(workflow.success){ - console.log("workflow data called") - dispatch(workflowActions.setWorkflowStatus(false)) - dispatch(workflowActions.setWorkflowNodes(workflow.data.nodes)) - dispatch(workflowActions.setWorkflowTrigger(workflow.data.Trigger)) - // console.log(`workfklow from redux: ${workflow.data}`) - } - } - // if(!workflowId) getEmptyWorkflowID() - getWorkflowData(); - },[dispatch, userId, workflowId]) - - 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){ - console.log("trigger id from redux: ", existingTrigger) - newNodes.push({ - id: `trigger~${existingTrigger.id}`, - 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.id}~${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 { - const timestamp = Date.now(); - const newPlaceholderId = `placeholder-${timestamp}`; - const actionNodeId = `action~${action.id}`; - - setNodes((currentNodes) => { - const placeholderNode = currentNodes.find( - (n) => n.type === "placeholder" - ); - if (!placeholderNode) return currentNodes; - - const previousNodeId = edges.find( - (e) => e.target === placeholderNode.id - )?.source; - - const actionNode: NodeType = { - id: actionNodeId, - type: "action", - position: placeholderNode.position, - data: { - label: action.name, - name: action.name, - icon: action.icon || "⚙️", - type: action.type, - }, - }; - - const newPlaceholder: NodeType = { - id: newPlaceholderId, - type: "placeholder", - position: { - x: placeholderNode.position.x + 250, - y: placeholderNode.position.y, - }, - data: { label: "+" }, - }; - - const otherNodes = currentNodes.filter( - (n) => n.id !== placeholderNode.id - ); - return [...otherNodes, actionNode, newPlaceholder]; - }); - - setEdges((currentEdges) => { - const edgeToPlaceholder = currentEdges.find((e) => - e.target.startsWith("placeholder") - ); - - const updatedEdges = currentEdges.map((e) => { - if (edgeToPlaceholder && e.id === edgeToPlaceholder.id) { - return { ...e, target: actionNodeId }; - } - return e; - }); - - return [ - ...updatedEdges, - { - id: `e-${actionNodeId}-${newPlaceholderId}`, - source: actionNodeId, - target: newPlaceholderId, - }, - ]; - }); - }; - - return ( -
- { - console.log("Node clicked:", node.id, node.type, node.data); - if (node.type === "placeholder") { - const hasTrigger = nodes.some((n) => n.type === "trigger"); - if (hasTrigger) { - setActionSidebarOpen(true); - } else { - setSidebarOpen(true); - } - } - 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) - setNodeIDType(node.id) - setCredType("google_oauth") - console.log("config from node: ", node.data.config) - setSelectedNodeConfig(node.data.config) - setLoadSheet(!loadSheet) - console.log("Form opened") - } - } - }} - /> - - setSidebarOpen(false)} - onSelectTrigger={handleSelectTrigger} - /> - - setActionSidebarOpen(false)} - onSelectAction={handleSelectAction} - /> - - {loadSheet && - - } -
- ); -}; - -export default CreateWorkFlow; diff --git a/apps/web/app/components/nodes/GoogleSheetFormClient.tsx b/apps/web/app/components/nodes/GoogleSheetFormClient.tsx deleted file mode 100644 index 4e29a97..0000000 --- a/apps/web/app/components/nodes/GoogleSheetFormClient.tsx +++ /dev/null @@ -1,421 +0,0 @@ -'use client'; - -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@workspace/ui/components/select'; -import { Button } from '@workspace/ui/components/button'; -import { Input } from '@workspace/ui/components/input'; -import { Label } from '@workspace/ui/components/label'; -import React, { useEffect, useState, useTransition } from 'react'; -import Link from 'next/link'; -import { toast } from 'sonner'; -import { handleSaveConfig, handleUpdateConfig } from './actions'; -import { useCredentials } from '@/app/hooks/useCredential'; -import { BACKEND_URL } from '@repo/common/zod'; -import { useAppSelector } from '@/app/hooks/redux'; -import { useDispatch } from 'react-redux'; -import { workflowActions } from '@/store/slices/workflowSlice'; - -interface GoogleSheetFormClientProps { - type: string; - nodeType: string; - avlNode?: string ; - position: number; - initialData?: { - range?: string; - operation?: string; - sheetName?: string; - spreadSheetId?: string; - credentialId?: string; - }; -} - -export function GoogleSheetFormClient({ type, nodeType, avlNode, position, initialData }: GoogleSheetFormClientProps) { - const [selectedCredential, setSelectedCredential] = useState(initialData?.credentialId || ''); - const [documents, setDocuments] = useState>([]); - const [selectedDocument, setSelectedDocument] = useState(initialData?.spreadSheetId || ''); - const [sheets, setSheets] = useState>([]); - const [selectedSheet, setSelectedSheet] = useState(initialData?.sheetName || ''); - const [operation, setOperation] = useState(initialData?.operation || 'read_rows'); - const [range, setRange] = useState(initialData?.range || 'A1:Z100'); - const [loading, setLoading] = useState(false); - const [isPending, startTransition] = useTransition(); - const [result, setResult] = useState(null); - const [credId, setCredId] = useState(initialData?.credentialId || ''); - - const dispatch = useDispatch() - // const [authUrl, setAuthUrl] = useState() - // 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) - - const userId = useAppSelector(s=>s.user.userId) || "" - const workflowId = useAppSelector(s=>s.workflow.workflow_id) || '' - // console.log(userId, 'id from client') - const credType = type - const nodeTypeParsed = nodeType.split("~")[0] || "" - // console.log('checking nodeType: ', nodeTypeParsed); - - const nodeId = nodeType.split("~")[1] || "" - // console.log('checking node id: ',nodeId) - const {cred: response, authUrl} = useCredentials(credType) - // console.log('response from form client', typeof(response)) - - // console.log(response," response from client after hook") - // console.log(authUrl," authurl") - - // Fetch documents when there's initial 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' }, - }); - 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]); - - // Fetch sheets when there's initial 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", - }); - 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]); - - const openAuthWindow = (url: string) => { - if (!url) return; - const width = 520; - const height = 650; - const screenLeft = (window as any).screenLeft ?? window.screenX ?? 0; - const screenTop = (window as any).screenTop ?? window.screenY ?? 0; - const screenWidth = window.innerWidth ?? document.documentElement.clientWidth ?? screen.width; - const screenHeight = window.innerHeight ?? document.documentElement.clientHeight ?? screen.height; - const left = Math.round(screenLeft + (screenWidth - width) / 2); - const top = Math.round(screenTop + (screenHeight - height) / 2); - const features = `popup=yes,toolbar=no,location=no,directories=no,status=no,menubar=no,scrollbars=yes,resizable=yes,copyhistory=no,width=${width},height=${height},top=${top},left=${left}`; - const win = window.open(url, 'google-oauth', features); - if (!win) { - window.location.href = url; - return; - } - try { win.focus?.(); } catch {} - }; - // Fetch documents when credential is selected - const handleCredentialChange = async (credentialId: string) => { - setSelectedCredential(credentialId); - setDocuments([]); - setSheets([]); - // Don't clear document/sheet if we have initial values - we'll try to restore them - if (!initialData?.spreadSheetId) setSelectedDocument(''); - if (!initialData?.sheetName) setSelectedSheet(''); - - if (!credentialId || credentialId === 'create-new') return; - setCredId(credentialId) - setLoading(true); - try { - const response = await fetch(`${BACKEND_URL}/node/getDocuments/${credentialId}`, { - method: 'GET', - credentials:"include", - headers: { 'Content-Type': 'application/json' }, - }); - const data = await response.json(); - - if (data.files.length > 0) { - setDocuments(data.files || []); - - // Restore previously selected document if it exists in the list - if (initialData?.spreadSheetId) { - const docExists = data.files.some((doc: any) => doc.id === initialData.spreadSheetId); - if (docExists) { - setSelectedDocument(initialData.spreadSheetId); - // Also fetch sheets for this document to restore sheet selection - try { - const sheetsRes = await fetch(`${BACKEND_URL}/node/getSheets/${credentialId}/${initialData.spreadSheetId}`, { - method: 'GET', - headers: { 'Content-Type': 'application/json' }, - credentials: "include", - }); - const sheetsData = await sheetsRes.json(); - if (sheetsData.files?.data?.length > 0) { - setSheets(sheetsData.files.data); - // Restore previously selected sheet if it exists - if (initialData?.sheetName) { - const sheetExists = sheetsData.files.data.some((s: any) => s.name === initialData.sheetName); - if (sheetExists) { - setSelectedSheet(initialData.sheetName); - } - } - } - } catch (err) { - console.error('Failed to restore sheets:', err); - } - } - } - } - } catch (error) { - console.error('Failed to fetch documents:', error); - } finally { - setLoading(false); - } - }; - - // Fetch sheet tabs when document is selected - const handleDocumentChange = async (documentId: string) => { - setSelectedDocument(documentId); - setSheets([]); - setSelectedSheet(''); - - if (!documentId) return; - - setLoading(true); - try { - const response = await fetch(`${BACKEND_URL}/node/getSheets/${credId}/${documentId}`, { - method: 'GET', - headers: { 'Content-Type': 'application/json' }, - credentials:"include", - }); - const data = await response.json(); - // console.log(data," from clint") - if (data.files.data.length > 0) { - setSheets(data.files.data || []); - } - } catch (error) { - console.error('Failed to fetch sheet tabs:', error); - } finally { - setLoading(false); - } - }; - - const handleSaveClick = () => { - const config = { - userId:userId, - node_Trigger:nodeId, - workflowId, - type:nodeTypeParsed, - position: position, - name: `Google sheet - ${nodeTypeParsed}`, - credentialId: selectedCredential, - spreadsheetId: selectedDocument, - sheetName: selectedSheet, - operation, - range - }; - - startTransition(async () => { - console.log('Sending config:', config); - let res: any - if(initialData){ - const updateConfig = { - type: nodeTypeParsed, - credentialId: selectedCredential, - spreadsheetId: selectedDocument, - sheetName: selectedSheet, - operation: operation, - range: range, - id: nodeId - } - res = await handleUpdateConfig(updateConfig) - } - else { - res = await handleSaveConfig(config); - dispatch(workflowActions.addWorkflowNode(res.data.data)) - } - console.log('Full response:', res); - // console.log('Success:', res.success); - // console.log('Output:', res.output); - // console.log('Error:', res.error); - // setResult(res); - if (res.success) { - toast.success("Configuration saved!"); - } else { - toast.error(res.data); - } - }); - } - - return ( -
- {/* 1. Credentials Select */} -
- - -
- - {/* 2. Documents (Sheets from Drive) */} -
- - -
- - {/* 3. Sheets from selected document */} -
- - -
- - {/* 4. Operation */} -
- - -
- - {/* 5. Range for operation */} -
- - setRange(e.target.value)} - placeholder="e.g., A1:Z100 or Sheet1!A1:B10" - className="mt-2" - disabled={!selectedSheet} - /> -

- Specify the range in A1 notation (e.g., A1:Z100) -

-
- - {/* Submit/Save Button */} -
- {/* */} - - - {result?.success === false &&

{result.error}

} - {result?.authUrl && ( - - Connect Google - - )} -
-
- ); -} diff --git a/apps/web/app/components/nodes/actions.ts b/apps/web/app/components/nodes/actions.ts deleted file mode 100644 index 9bd6cb4..0000000 --- a/apps/web/app/components/nodes/actions.ts +++ /dev/null @@ -1,106 +0,0 @@ - -import { createNode, createTrigger, updateNode, updateTrigger } from '@/app/workflow/lib/config'; - -interface SaveConfigFormData { - // userId: string; - type: string - credentialId: string; - spreadsheetId: string; - sheetName: string; - operation: string; - range: string; - position?: number; - name: string; - node_Trigger: string; - workflowId: string; - -} - -interface updateConfigData{ - type: string - credentialId: string; - spreadsheetId: string; - sheetName: string; - operation: string; - range: string; - id: string -} - -export async function handleSaveConfig(formData: SaveConfigFormData) { - // const executor = new GoogleSheetsNodeExecutor(); - const context = { - // userId: formData.userId, - name: formData.name, - workflowId: formData.workflowId, - node_Trigger: formData.node_Trigger, - config: { - credId: formData.credentialId, - operation: formData.operation, - spreadsheetId: formData.spreadsheetId, - range: formData.range, - sheetName: formData.sheetName, - }, - }; - if(formData.type === 'trigger') - { - const trigger = await createTrigger(context) - console.log('triggger created using config backend: ',trigger) - - return{ - success: trigger.success, - data: trigger.data - } - } - else{ - const node = await createNode({...context, position: formData.position ? formData.position : 0}) - console.log('NOde created using config backend: ',node); - return { - success:node.success, - data: node.data - } - } - - - // const result = await executor.execute(context); - - // // Return plain object (not class instance) - // return { - // success: result.success, - // output: result.output || null, - // error: result.error || null, - // authUrl: result.authUrl || null, - // requiresAuth: result.requiresAuth || false, - // }; -} - -export async function handleUpdateConfig(formData: updateConfigData){ - const data = { - id: formData.id, - config: { - credId: formData.credentialId, - spreadsheetId: formData.spreadsheetId, - sheetName: formData.sheetName, - operation: formData.operation, - range: formData.range - } - } - if(formData.type === 'trigger') - { - const trigger = await updateTrigger(data) - console.log('triggger updated using config backend: ',trigger) - - return{ - success: trigger.success, - data: trigger.data - } - } - else{ - const node = await updateNode(data) - console.log('NOde updated using config backend: ',node); - return { - success:node.success, - data: node.data - } - } - -} \ No newline at end of file diff --git a/apps/web/app/components/ui/Logs.tsx b/apps/web/app/components/ui/Logs.tsx new file mode 100644 index 0000000..e69de29 diff --git a/apps/web/app/components/ui/app-sidebar.tsx b/apps/web/app/components/ui/app-sidebar.tsx index 9f1043f..1cc38c9 100644 --- a/apps/web/app/components/ui/app-sidebar.tsx +++ b/apps/web/app/components/ui/app-sidebar.tsx @@ -24,15 +24,17 @@ import { DropdownMenuItem, DropdownMenuTrigger } from '@workspace/ui/components/dropdown-menu' -import { ChevronDown, ChevronUp, Key, LogOut, LucideLayoutDashboard, PlusCircle, User2, WorkflowIcon } from 'lucide-react' +import { ChevronDown, ChevronUp, Key, LogOut, LucideLayoutDashboard, LucidePlus, PlusCircle, User2, WorkflowIcon } from 'lucide-react' import { useEffect, useState } from 'react' -import { createWorkflow, getAllCredentials, getAllWorkflows, getEmptyWorkflow, getworkflowData } from '@/app/workflow/lib/config' +// import { createWorkflow, getAllCredentials, getAllWorkflows, getworkflowData } from '@/app/workflow/lib/config' import { useAppDispatch, useAppSelector } from '@/app/hooks/redux' import { userAction } from '@/store/slices/userSlice' -import { workflowActions, workflowReducer } from '@/store/slices/workflowSlice' +import { workflowActions } from '@/store/slices/workflowSlice' import { toast } from 'sonner' import { signOut } from 'next-auth/react' import { useRouter } from 'next/navigation' +import { api } from '@/app/lib/api' +import { CardDemo } from './Design/WorkflowCard' export function AppSidebar() { @@ -41,61 +43,52 @@ export function AppSidebar() { console.log('redux workflow from sidebar: ',flow) const dispatch = useAppDispatch() const router = useRouter() - const [selectedWorkflow, setSelectedWorkflow] = useState(flow.workflow_id) - const [workflow, setWorkflow] = useState>() + const reduxWorkflowId = flow.data.workflowId + const [selectedWorkflow, setSelectedWorkflow] = useState(reduxWorkflowId) + type WorkflowSummary = { id: string; name: string; description?: string | null } + const [workflows, setWorkflows] = useState() const [creds, setCreds] = useState>() + const [createOpen, setCreateOpen] = useState(false); - async function getWorkflows(){ - const flows = await getAllWorkflows(); - if(flows) setWorkflow(flows) - } useEffect(()=>{ + + async function getWorkflows(){ + const flows = await api.workflows.getAll(); + if(flows) setWorkflows(flows.data.Data) + } async function getCreds(){ - const credentials = await getAllCredentials(); - if(credentials) setCreds(credentials) + const credentials = await api.Credentials.getAllCreds(); + console.log(`--- 60 ${JSON.stringify(credentials)}`) + if(credentials.data) setCreds(credentials.data.data) } async function getWorkflowData(){ - console.log("workflow data called") - if(!flow.workflow_id) return - const workflow = await getworkflowData(flow.workflow_id) - if(workflow.success){ - console.log("workflow data fetchedsuceesully: ", workflow.data) - dispatch(workflowActions.setWorkflowStatus(false)) - dispatch(workflowActions.setWorkflowNodes(workflow.data.nodes)) - dispatch(workflowActions.setWorkflowTrigger(workflow.data.Trigger)) + if(!selectedWorkflow) return + const workflow = await api.workflows.get(selectedWorkflow); + if(workflow.data){ + console.log("workflow data fetchedsuceesully: ", workflow.data.Data) + // dispatch(workflowActions.addWorkflowNode(workflow.data.nodes)) + dispatch( + workflowActions.setWorkflowFromBackend({ + workflowId: selectedWorkflow, + data: workflow.data.Data, + }) + ) + // dispatch(workflowActions.setWorkflowTrigger(workflow.data.Trigger)) // console.log(`workfklow from redux: ${workflow.data}`) } } - // async function setEmptyFlow(){ - // const workflow = await getEmptyWorkflow() - // if(workflow){ - // const {id, isEmpty} = workflow - // dispatch(workflowActions.setWorkflowId(id)) - // dispatch(workflowActions.setWorkflowStatus(isEmpty)) - // } - // else{ - // const newWorkflow = await createWorkflow() - // dispatch(workflowActions.clearWorkflow()) - // setSelectedWorkflow(null) - // dispatch(workflowActions.setWorkflowId(newWorkflow.id)) - // dispatch(workflowActions.setWorkflowStatus(newWorkflow.isEmpty)) - // toast.success("Workflow created") - // getWorkflows() - // } - // } - if(!creds) getCreds() - if(!workflow) { + if(!workflows) { getWorkflows() - createNewWorkflow() } getWorkflowData() - },[flow.workflow_id, dispatch]) + },[selectedWorkflow, dispatch]) - const workflowHandler = (wId: string)=>{ - dispatch(workflowActions.setWorkflowId(wId)) + const workflowHandler = (workflow: WorkflowSummary) => { + setSelectedWorkflow(workflow.id) + router.push(`/workflows/${workflow.id}`) } const credHandler = (cId: string)=> { // console.log(cId); @@ -109,27 +102,9 @@ export function AppSidebar() { router.push('/login') } - const createNewWorkflow = async()=>{ - const workflow = await getEmptyWorkflow() - - if(workflow){ - const {id, isEmpty} = workflow - dispatch(workflowActions.setWorkflowId(id)) - dispatch(workflowActions.setWorkflowStatus(isEmpty)) - toast.info("You are in empty workflow") - } - else{ - const newWorkflow = await createWorkflow() - dispatch(workflowActions.clearWorkflow()) - setSelectedWorkflow(null) - dispatch(workflowActions.setWorkflowId(newWorkflow.id)) - dispatch(workflowActions.setWorkflowStatus(newWorkflow.isEmpty)) - toast.success("Workflow created") - getWorkflows() - } - } // console.log(`workflow form ${workflow}`) return ( + <> Logo @@ -139,11 +114,11 @@ export function AppSidebar() { - - - Create Workflow - - + + setCreateOpen(true)}> + Create Workflow + + {/* WORKFLOWS LIST */} @@ -157,15 +132,15 @@ export function AppSidebar() { - {workflow ? (workflow.length === 0 ? ( + {workflows ? (workflows.length === 0 ? ( Create Workflow - ) : (workflow.map((i:any) => ( - workflowHandler(i.id)} key={i.id}> - + ) : (workflows.map((i) => ( + workflowHandler(i)} key={i.id}> + {i.name} ) @@ -249,5 +224,7 @@ export function AppSidebar() { + { createOpen && setCreateOpen(false)}/>} + ) } \ No newline at end of file diff --git a/apps/web/app/hooks/useCredential.ts b/apps/web/app/hooks/useCredential.ts index 2590aa7..56a397a 100644 --- a/apps/web/app/hooks/useCredential.ts +++ b/apps/web/app/hooks/useCredential.ts @@ -1,7 +1,7 @@ "use client"; import { useEffect, useState } from "react"; -import { getCredentials } from "../workflow/lib/config"; import { BACKEND_URL } from "@repo/common/zod"; +import { api } from "../lib/api"; export const useCredentials = (type: string, workflowId?: string): any => { const [cred, setCred] = useState([]); @@ -17,7 +17,7 @@ export const useCredentials = (type: string, workflowId?: string): any => { return; } - const response = await getCredentials(type); + const response = await api.Credentials.getCredentials(type) const data = JSON.stringify(response) console.log("This is the log from usecredentials" , data) // Backend should ONLY return stored credentials diff --git a/apps/web/app/hooks/useTriggers.ts b/apps/web/app/hooks/useTriggers.ts index 4528c60..1be8805 100644 --- a/apps/web/app/hooks/useTriggers.ts +++ b/apps/web/app/hooks/useTriggers.ts @@ -1,7 +1,7 @@ "use client"; import { useEffect, useState } from "react"; import type { AvailableTrigger } from "../types/workflow.types"; -import { getAvailableTriggers } from "../workflow/lib/config"; +import { api } from "../lib/api"; export function useTriggers(shouldFetch: boolean) { const [triggers, setTriggers] = useState([]); @@ -16,9 +16,9 @@ export function useTriggers(shouldFetch: boolean) { setError(null); try { - const response = await getAvailableTriggers(); - setTriggers(response.Data); - console.log(response.Data); + const response = await api.triggers.getAvailable(); + setTriggers(response.data.Data); + console.log(response.data.Data); console.log(JSON.stringify(response)); } catch (err) { setError("Error while fetching triggers"); diff --git a/apps/web/app/lib/api.ts b/apps/web/app/lib/api.ts index 1a48687..c20312f 100644 --- a/apps/web/app/lib/api.ts +++ b/apps/web/app/lib/api.ts @@ -3,7 +3,6 @@ import axios from "axios"; import { BACKEND_URL, NodeSchema, NodeUpdateSchema, workflowUpdateSchema } from "@repo/common/zod"; import { TriggerUpdateSchema } from "@repo/common/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 = { user: { @@ -39,6 +38,14 @@ export const api = { headers: { "Content-Type": "application/json" }, }) }, + getAll: async () =>{ + return await axios.get(`${BACKEND_URL}/user/workflows`, + { + withCredentials: true, + headers: { "Content-Type": "application/json" }, + } + ) + }, execute: async (data: any) => { return await axios.post(`${BACKEND_URL}/user/executeWorkflow`, data, { withCredentials: true, @@ -87,7 +94,7 @@ export const api = { withCredentials: true, headers: { "Content-Type": "application/json" }, }); - return res.data.Data; + return res.data.data; }, getAllCreds: async () => diff --git a/apps/web/app/workflow/lib/config.ts b/apps/web/app/workflow/lib/config.ts deleted file mode 100644 index bbf7f57..0000000 --- a/apps/web/app/workflow/lib/config.ts +++ /dev/null @@ -1,286 +0,0 @@ -import { BACKEND_URL } from "@repo/common/zod"; -import { GoogleSheetsNodeExecutor } from "@repo/nodes"; -import axios from "axios" -const date = new Date() -export const getAvailableTriggers = async () => { - try { - const response = await axios.get( - `${BACKEND_URL}/user/getAvailableTriggers`, - { - withCredentials: true, - } - ); - console.log(response.data.Data); - console.log(response.data); - - const Data = JSON.stringify(response.data.Data); - console.log(Data); - console.log("This is the name from the", response.data.Data); - console.log("This is the config from the", response.data.Data); - - return response.data; - } catch (error) { - console.error("Error fetching available triggers:", error); - throw error; - } -}; - -export const getCredentials = async(type: string)=>{ - try{ - - const response = await axios.get( - `${BACKEND_URL}/user/getCredentials/${type}`, - { - withCredentials: true, - } - ); - console.log("response from config: ",response); - - const Data = JSON.stringify(response.data.Data); - return response.data.data; - } - catch(e){ - console.error("Error fetching credentials:", e); - throw e; - } -} - -export const getAllCredentials = async()=>{ - try{ - const res = await axios.get(`${BACKEND_URL}/user/getAllCreds`, { - withCredentials: true - }) - const data = res.data.data - console.log("credentails: ",data) - return data - } - catch(e){ - console.error("Error fetching credentials:", e); - throw e; - } -} - - -export const getAllWorkflows = async()=>{ - try{ - const res = await axios.get(`${BACKEND_URL}/user/workflows`,{ - withCredentials: true - }) - const data = res.data.Data - console.log("workflows: ",data) - return data - }catch(e){ - console.error("Error fetching credentials:", e); - throw e; - } -} - -export const createWorkflow = async()=>{ - try{ - const response = await axios.post(`${BACKEND_URL}/user/create/workflow`, - { - Name:`workflow-${date.toString()}`, - // UserId: userId, - Config: {} - },{ - headers: { - 'Content-Type': 'application/json' - }, - withCredentials: true - } - ) - - const workflow = response.data - console.log('new workflow: ',workflow) - return workflow.Data - }catch(e){ - console.error("Error in creating workflow:", e); - throw e; - } -} - -interface Triggercontext{ - // userId: formData.userId, - name: string, - workflowId: string, - node_Trigger: string, - config: { - credId: string, - operation: string, - spreadsheetId: string, - range: string, - sheetName: string, - }, - }; -export const createTrigger = async(context: Triggercontext)=>{ - try{ - // console.log('Trigger context: ', context) - const response = await axios.post(`${BACKEND_URL}/user/create/trigger`, - { - Name: context.name, - AvailableTriggerID: context.node_Trigger, - Config: context.config, - WorkflowId: context.workflowId, - TriggerType:"" - }, - { - headers: {"Content-Type": "application/json"}, - withCredentials:true - }) - - const trigger = (response.data) - console.log('trigger created: ', trigger); - return { - success: true, - data: trigger - } - }catch(e){ - console.error("Error in creating Trigger:", e); - return { - success: false, - data: e instanceof Error ? e.message : `unknown error ${e}` - } - } -} - -interface updateContext{ - config: { - credId: string, - operation: string, - spreadsheetId: string, - range: string, - sheetName: string, - }, - id: string -} - -export const updateTrigger = async(context: updateContext)=>{ - try{ - const res = await axios.put(`${BACKEND_URL}/user/update/trigger`, - { - TriggerId: context.id, - Config: context.config - },{ - headers: {"Content-Type": "application/json"}, - withCredentials: true - } - ) - const trigger = (res.data) - console.log('trigger created: ', trigger); - return { - success: true, - data: trigger - } - }catch(e){ - console.error("Error in updating Trigger:", e); - return { - success: false, - data: e instanceof Error ? e.message : `unknown error ${e}` - } - } -} - -export const updateNode = async(context: updateContext)=>{ - try{ - const res = await axios.put(`${BACKEND_URL}/user/update/node`, - { - NodeId: context.id, - Config: context.config - }, - { - withCredentials: true, - headers: {"Content-Type": "application/json"} - } - ) - const node = (res.data) - console.log('node updated: ', node); - return { - success: true, - data: node - } - }catch(e){ - console.error("Error in updating Node:", e); - return { - success: false, - data: e instanceof Error ? e.message : `unknown error ${e}` - } - } -} - -interface Nodecontext{ - // userId: formData.userId, - name: string, - workflowId: string, - node_Trigger: string, - position? :number, - config: { - credId: string, - operation: string, - spreadsheetId: string, - range: string, - sheetName: string, - }, - }; -export const createNode = async(context: Nodecontext)=>{ - try{ - console.log('Node context: ', context) - - const response = await axios.post(`${BACKEND_URL}/user/create/node`,{ - - Name: context.name, - AvailableNodeId: context.node_Trigger, - WorkflowId: context.workflowId, - Config: context.config, - Position: context.position ? context.position : 0 - },{ - headers: {"Content-Type": "application/json"}, - withCredentials:true - }) - - const node = (response.data) - console.log('Node created: ', node); - return { - success: true, - data: node - } - - }catch(e){ - console.error("Error in creating Node:", e); - return {success: false, - data: e instanceof Error ? e.message : `unknown error ${e}` - }; - } -} - -export const getEmptyWorkflow = async()=>{ - try{ - const res = await axios.get(`${BACKEND_URL}/user/empty/workflow`,{ - withCredentials: true - }) - - const workflow = res.data.Data - return workflow - } - catch(e){ - console.error("Error in fetching workflows:", e); - throw e; - } -} - -export const getworkflowData = async(workflowId: string)=>{ - try{ - const res = await axios.get(`${BACKEND_URL}/user/workflow/${workflowId}`,{ - withCredentials: true - }) - console.log('workflow data includes trigger and nodes: ', res.data.Data ) - return { - success: true, - data: res.data.Data - } - }catch(e){ - console.error("Error in creating Node:", e); - return {success: false, - data: e instanceof Error ? e.message : `unknown error ${e}` - }; - } -} \ No newline at end of file diff --git a/apps/web/app/workflow/page.tsx b/apps/web/app/workflow/page.tsx deleted file mode 100644 index d5b4a5e..0000000 --- a/apps/web/app/workflow/page.tsx +++ /dev/null @@ -1,27 +0,0 @@ -"use client"; - -import { SidebarProvider, SidebarTrigger } from "@workspace/ui/components/sidebar"; -import CreateWorkFlow from "../components/nodes/CreateWorkFlow"; -import { AppSidebar } from "../components/ui/app-sidebar"; -// import WorkFlow from "../components/nodes/WorkFlow"; -// import CreateWorkFlow from "../components/workflow"; - -export default function Workf() { - - return ( - <> -
-
- - - - {/* {children} */} - -
-
- -
-
- - ); -} \ No newline at end of file diff --git a/apps/web/app/workflows/[id]/page.tsx b/apps/web/app/workflows/[id]/page.tsx index 397d2d3..9f3f5ac 100644 --- a/apps/web/app/workflows/[id]/page.tsx +++ b/apps/web/app/workflows/[id]/page.tsx @@ -26,6 +26,8 @@ import { useAutoSave } from "@/app/hooks/useAutoSave"; import { workflowActions } from "@/store/slices/workflowSlice"; import { setNodeOutput, setNodeLoading, selectAllOutputs } from "@/store/slices/nodeOutputSlice"; import { resolveConfigVariables } from "@repo/common/zod"; +import { SidebarProvider } from "@workspace/ui/components/sidebar"; +import { AppSidebar } from "@/app/components/ui/app-sidebar"; export default function WorkflowCanvas() { const params = useParams(); const workflowId = params.id as string; @@ -342,31 +344,12 @@ export default function WorkflowCanvas() { } // store updating - dispatch(workflowActions.setWorkflow({ + dispatch( + workflowActions.setWorkflowFromBackend({ workflowId, - name: workflows.data.Data.name, - description: workflows.data.Data.description, - trigger: Trigger ? { - TriggerId: Trigger.id, - name: Trigger.name, - type: Trigger.type, - icon: Trigger.icon, - Config: Trigger.config || {}, - position: Trigger.Position || DEFAULT_TRIGGER_POSITION, - AvailableTriggerID: Trigger.AvailableTriggerID - } : null, - nodes: dbNodes.map((n: any) => ({ - NodeId: n.id, - name: n.name, - type: n.type, - icon: n.icon, - Config: n.config || {}, - position: n.position || { x: 0, y: 0 }, - stage: n.stage, - AvailableNodeID: n.AvailableNodeId - })), - edges: dbEdges - })) + data: workflows.data.Data, + }) + ) // Ensure trigger position const triggerPosition = ensurePosition( Trigger?.Position, @@ -773,7 +756,7 @@ export default function WorkflowCanvas() { // console.log("THis log from page.tsx about the nodeConfig", selectedNode) return ( -
+
{error && (
)} - + + + + {/* {children} */} + +
+
- + + ) { + const { workflowId, data } = action.payload; + + // Backend casing is inconsistent (e.g. Trigger vs trigger, Edges vs edges), + // so normalize the payload here so the rest of the app can rely on Redux's shape. + const backendData = data ?? {}; + const backendNodes = Array.isArray(backendData?.nodes) + ? backendData.nodes + : []; + const backendEdges = Array.isArray(backendData?.Edges) + ? backendData.Edges + : []; + const backendTrigger = backendData?.Trigger ?? null; + + state.data = { + workflowId, + name: backendData?.name ?? null, + description: backendData?.description ?? null, + trigger: backendTrigger + ? { + TriggerId: backendTrigger?.id ?? "", + name: backendTrigger?.name ?? "", + type: backendTrigger?.type ?? "", + icon: backendTrigger?.icon ?? null, + Config: backendTrigger?.config || {}, + position: + backendTrigger?.Position || DEFAULT_TRIGGER_POSITION, + AvailableTriggerID: + backendTrigger?.AvailableTriggerID ?? "", + } + : null, + nodes: backendNodes.map((n: any) => ({ + NodeId: n?.id ?? "", + name: n?.name ?? "", + type: n?.type ?? "", + icon: n?.icon ?? null, + Config: n?.config || {}, + position: n?.position || { x: 0, y: 0 }, + stage: n?.stage ?? 0, + AvailableNodeID: n?.AvailableNodeId ?? "", + })), + edges: backendEdges.map((e: any) => ({ + id: e?.id ?? "", + source: e?.source ?? "", + target: e?.target ?? "", + })), + }; + state.isChanged = { trigger: false, nodes: false, edges: false }; + state.changedNodeIds = []; + }, + setWorkflow(state, action: PayloadAction){ state.data = action.payload; state.isChanged = { trigger: false, nodes: false, edges: false };