diff --git a/apps/http-backend/src/index.ts b/apps/http-backend/src/index.ts
index 681174a..b0d364a 100644
--- a/apps/http-backend/src/index.ts
+++ b/apps/http-backend/src/index.ts
@@ -2,14 +2,16 @@
// import axios from "axios";
import { prismaClient } from "@repo/db/client";
import cookieParser from 'cookie-parser'
-import { NodeRegistry } from "@repo/nodes/nodeClinet";
+
+
+import { NodeRegistry } from "@repo/nodes/nodeClient";
import express from "express";
import { userRouter } from "./routes/userRoutes/userRoutes.js";
import cors from "cors"
const app = express()
-const allowedOrigins = ['http://localhost:3000'];
+const allowedOrigins = ['http://localhost:3000' , 'http://localhost:3001' ];
app.use(cors({
origin: allowedOrigins,
credentials: true,
diff --git a/apps/http-backend/tsconfig.tsbuildinfo b/apps/http-backend/tsconfig.tsbuildinfo
index e49b868..f8e5606 100644
--- a/apps/http-backend/tsconfig.tsbuildinfo
+++ b/apps/http-backend/tsconfig.tsbuildinfo
@@ -1 +1 @@
-{"root":["./src/index.ts","./src/routes/nodes.routes.ts","./src/routes/userroutes/usermiddleware.ts","./src/routes/userroutes/userroutes.ts"],"version":"5.7.3"}
\ No newline at end of file
+{"root":["./src/index.ts","./src/routes/nodes.routes.ts","./src/routes/userRoutes/userMiddleware.ts","./src/routes/userRoutes/userRoutes.ts"],"version":"5.7.3"}
\ No newline at end of file
diff --git a/apps/web/app/components/Actions/ActionNode.tsx b/apps/web/app/components/Actions/ActionNode.tsx
new file mode 100644
index 0000000..eb41040
--- /dev/null
+++ b/apps/web/app/components/Actions/ActionNode.tsx
@@ -0,0 +1,26 @@
+import { Handle, Position } from "@xyflow/react";
+
+interface ActionNodeProps {
+ data: {
+ label: string;
+ name?: string;
+ icon?: string;
+ type?: string;
+ };
+}
+
+export const ActionNode = ({ data }: ActionNodeProps) => {
+ return (
+
+
+
+ {data.icon || "⚙️"}
+ {data.name}
+ {data.type}
+
+
+
+ );
+};
+
+export default ActionNode;
\ No newline at end of file
diff --git a/apps/web/app/components/Actions/ActionSidebar.tsx b/apps/web/app/components/Actions/ActionSidebar.tsx
new file mode 100644
index 0000000..8a38629
--- /dev/null
+++ b/apps/web/app/components/Actions/ActionSidebar.tsx
@@ -0,0 +1,79 @@
+import {
+ Sheet,
+ SheetContent,
+ SheetHeader,
+ SheetTitle,
+} from "@workspace/ui/components/sheet";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@workspace/ui/components/select";
+import { useActions } from "@/app/hooks/useActions";
+
+interface SideBarProps {
+ isOpen: boolean;
+ onClose: () => void;
+ onSelectAction: (action: { id: string; name: string; type: string; icon?: string }) => void;
+}
+
+const getAvailableActions = (actions: any): Array => {
+ if (Array.isArray(actions)) return actions;
+ if (actions && Array.isArray(actions.Data)) return actions.Data;
+ return [];
+};
+
+export const ActionSideBar = ({ isOpen, onClose, onSelectAction }: SideBarProps) => {
+ const { actions, loading, error } = useActions({ shouldFetch: true });
+ const availableActions = getAvailableActions(actions);
+
+ return (
+ !open && onClose()}>
+
+
+ Select an Action
+
+
+ {loading ? (
+ Loading...
+ ) : error ? (
+ Error fetching actions
+ ) : (
+
+ )}
+
+
+ );
+};
+
+export default ActionSideBar;
diff --git a/apps/web/app/components/nodes/CreateWorkFlow.tsx b/apps/web/app/components/nodes/CreateWorkFlow.tsx
index 577b17c..7a8cded 100644
--- a/apps/web/app/components/nodes/CreateWorkFlow.tsx
+++ b/apps/web/app/components/nodes/CreateWorkFlow.tsx
@@ -4,151 +4,205 @@ 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";
+
interface NodeType {
- id: string;
- type: "placeholder" | "trigger";
- position: { x: number; y: number };
- data: {
- label: string;
- name?: string;
- icon?: string;
- type?: string;
- config?: Record;
- };
- }
- interface EdgeType {
- id: string;
- source: string;
- target: string;
- }
-
+ 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 [sidebarOpen, setSidebarOpen] = useState(false);
+ const [actionSidebarOpen, setActionSidebarOpen] = useState(false);
+
const [nodes, setNodes] = useState([
{
id: "1",
type: "placeholder",
- position: { x: 0, y: 0 },
+ position: { x: 100, y: 200 },
data: {
- label: "Select This one",
- icon: "→",
+ label: "+",
},
- },
+ },
]);
+
const [edges, setEdges] = useState([]);
- const nodeType = {
+ const nodeTypes = {
placeholder: PlaceholderNode,
trigger: TriggerNode,
+ action: ActionNode,
};
-// const handleSelectTrigger = (trigger: { id: string; name: string; type: string; icon?: string }) => {
-// const edgeId = `e-trigger-${trigger.id}-placeholder-${Date.now()}`;
-// const placeholderId = `placeholder-${Date.now()}`;
-
-// setNodes((currentNodes) => {
-// const placeholderNode = currentNodes.find((n) => n.type === "placeholder");
-// if (!placeholderNode) return currentNodes;
-
-// const triggerNode: NodeType = {
-// id: `trigger-${trigger.id}`,
-// 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 + 200,
-// y: placeholderNode.position.y,
-// },
-// data: {
-// label: "+",
-// },
-// };
-
-// return [triggerNode, newPlaceholder];
-// });
-
-// // TypeScript expects you to define a type for edges.
-// // We'll use a generic structure: id (string), source (string), target (string)
-// setEdges([
-// {
-// id: edgeId,
-// source: `trigger-${trigger.id}`,
-// target: placeholderId,
-// }
-// ]);
-// };
-
-
-const handleSelectTrigger = (trigger: { id: string; name: string; type: string; icon?: string }) => {
- const timestamp = Date.now(); // ✅ Call ONCE
+
+ // 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");
+ const placeholderNode = currentNodes.find(
+ (n) => n.type === "placeholder"
+ );
if (!placeholderNode) return currentNodes;
-
+
const triggerNode: NodeType = {
- id: triggerNodeId, // ✅ Use variable
+ id: triggerNodeId,
type: "trigger",
- position: placeholderNode.position,
+ position: placeholderNode.position,
data: {
label: trigger.name,
name: trigger.name,
icon: trigger.icon || "⚡",
- type: trigger.type,
+ type: trigger.type,
},
};
-
- const newPlaceholder: NodeType = {
- id: placeholderId, // ✅ Use variable
+
+ const newPlaceholder: NodeType = {
+ id: placeholderId,
type: "placeholder",
position: {
- x: placeholderNode.position.x + 200,
+ x: placeholderNode.position.x + 250,
y: placeholderNode.position.y,
},
data: { label: "+" },
};
-
+
return [triggerNode, newPlaceholder];
});
-
+
setEdges([
{
- id: edgeId,
- source: triggerNodeId, // ✅ Matches trigger node
- target: placeholderId, // ✅ Matches placeholder node
- }
+ id: edgeId,
+ source: triggerNodeId,
+ target: placeholderId,
+ },
]);
};
-return (
+
+ const handleSelectAction = (action: {
+ id: string;
+ name: string;
+ type: string;
+ icon?: string;
+ }) => {
+ const timestamp = Date.now();
+ const newPlaceholderId = `placeholder-${timestamp}`;
+ const actionNodeId = `action-${action.id}-${timestamp}`;
+
+ 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 (
{
if (node.type === "placeholder") {
- setSideBarOpen(true);
+ const hasTrigger = nodes.some((n) => n.type === "trigger");
+ if (hasTrigger) {
+ setActionSidebarOpen(true);
+ } else {
+ setSidebarOpen(true);
+ }
}
}}
- >
-
setSideBarOpen(false)}
+ />
+
+ setSidebarOpen(false)}
onSelectTrigger={handleSelectTrigger}
/>
+
+ setActionSidebarOpen(false)}
+ onSelectAction={handleSelectAction}
+ />
);
};
diff --git a/apps/web/app/hooks/useActions.tsx b/apps/web/app/hooks/useActions.tsx
new file mode 100644
index 0000000..e6c5948
--- /dev/null
+++ b/apps/web/app/hooks/useActions.tsx
@@ -0,0 +1,34 @@
+import { BACKEND_URL } from "@repo/common/zod";
+import axios from "axios";
+import { useEffect, useState } from "react";
+import { AvailabeAction } from "../types/workflow.types";
+
+interface userActionsPrps {
+ shouldFetch: boolean;
+}
+export const useActions = ({ shouldFetch }: userActionsPrps) => {
+ const [actions, setActions] = useState([]);
+ const [loading, setLoading] = useState(false);
+ const [error, setError] = useState("");
+
+ useEffect(() => {
+ if (!shouldFetch) return;
+ const fetchData = async () => {
+ try {
+ setLoading(true);
+ const Data = await axios.get(`${BACKEND_URL}/user/getAvailableNodes`, {
+ withCredentials: true,
+ });
+ console.log(JSON.stringify(Data.data));
+ setActions(Data.data);
+ setLoading(false);
+ } catch (e: any) {
+ setError(e);
+ console.log("Error while fetching Data from Triggers", e);
+ }
+ };
+ fetchData();
+ }, [shouldFetch]);
+
+ return { actions, loading, error };
+};
diff --git a/apps/web/app/page.tsx b/apps/web/app/page.tsx
index b1367ba..354a5ce 100644
--- a/apps/web/app/page.tsx
+++ b/apps/web/app/page.tsx
@@ -1,14 +1,12 @@
import { getServerSession } from "next-auth";
import { AuthOptions } from "@/app/api/auth/utils/auth";
-
export default async function Home() {
const session = await getServerSession(AuthOptions);
-
- return (
+ return (
- Hello world
+ Hello world
Hello world
@@ -41,4 +39,4 @@ export default async function Home() {
);
-}
+}
\ No newline at end of file
diff --git a/apps/web/app/types/workflow.types.ts b/apps/web/app/types/workflow.types.ts
index 130b2a2..21636db 100644
--- a/apps/web/app/types/workflow.types.ts
+++ b/apps/web/app/types/workflow.types.ts
@@ -25,5 +25,6 @@ export interface AvailabeAction {
id: string;
name: string;
type: string;
- config: JSON;
+ config: Record;
+
}
diff --git a/package.json b/package.json
index 43902b0..a193973 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,7 @@
"turbo": "^2.5.5",
"typescript": "5.7.3"
},
- "packageManager": "pnpm@10.24.0",
+ "packageManager": "pnpm@10.25.0",
"engines": {
"node": ">=20"
}