From 31220072c5e4b0299217bfe66a6d781146a01109 Mon Sep 17 00:00:00 2001 From: Raditya Harya Date: Sun, 4 Feb 2024 12:58:05 +0000 Subject: [PATCH] settings dialog workflow saving/updating & handle empty workflow --- src/app/flow/page.tsx | 10 + src/app/utils/reactFlowToWorkflow.ts | 29 ++- .../settingsDialog/tabs/General.tsx | 178 ++++++++++++------ 3 files changed, 151 insertions(+), 66 deletions(-) diff --git a/src/app/flow/page.tsx b/src/app/flow/page.tsx index 63aef8d..a56a88d 100644 --- a/src/app/flow/page.tsx +++ b/src/app/flow/page.tsx @@ -135,6 +135,16 @@ function Builder({ searchParams }: { searchParams: any }) { toast.success( `Flow '${flowId}' loaded with ${nodes.length} nodes and ${edges.length} edges`, ); + } else { + setFlowState({ + id: flowId, + name: name ?? "", + description: description ?? "", + workflow: workflowData.workflow, + }); + toast.info( + `Flow '${flowId}' loaded with no nodes or edges. Please add some nodes and edges to continue.`, + ); } } }, [workflowData, workflowError]); diff --git a/src/app/utils/reactFlowToWorkflow.ts b/src/app/utils/reactFlowToWorkflow.ts index ef17e9c..044ecb0 100644 --- a/src/app/utils/reactFlowToWorkflow.ts +++ b/src/app/utils/reactFlowToWorkflow.ts @@ -3,6 +3,7 @@ import useStore from "../states/store"; import { type Node, type Edge } from "@xyflow/react"; import { validateWorkflow } from "./validateWorkflow"; import { generate } from "random-words"; +import { toast } from "sonner"; type ReactFlowToWorkflowInput = { nodes: Node[]; @@ -114,15 +115,25 @@ export default async function reactFlowToWorkflow({ connections: [], }; - nodes = filterNodes(nodes); - addNodesToWorkflow(nodes, workflowObject); - addEdgesToWorkflow(edges, workflowObject); - setNodesAsSources(workflowObject); - - removeUnnecessaryData(workflowObject.sources); - removeUnnecessaryData(workflowObject.operations); - - const { valid, errors } = await validateWorkflow(workflowObject); + let [valid, errors] = [true, {}]; + if (nodes.length > 0 && edges.length > 0) { + nodes = filterNodes(nodes); + addNodesToWorkflow(nodes, workflowObject); + addEdgesToWorkflow(edges, workflowObject); + setNodesAsSources(workflowObject); + + removeUnnecessaryData(workflowObject.sources); + removeUnnecessaryData(workflowObject.operations); + const response = await validateWorkflow(workflowObject); + valid = response.valid; + errors = response.errors; + if (!valid) { + throw new Error("Workflow is not valid"); + } + } else { + toast.error("No workflow provided"); + valid = false; + } console.log("workflow", workflowObject); diff --git a/src/components/settingsDialog/tabs/General.tsx b/src/components/settingsDialog/tabs/General.tsx index 5de07a1..4696efa 100644 --- a/src/components/settingsDialog/tabs/General.tsx +++ b/src/components/settingsDialog/tabs/General.tsx @@ -1,73 +1,137 @@ import { Copy, Info } from "lucide-react"; -import React from "react"; +import React, { useEffect } from "react"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; import useClipboard from "@/hooks/useClipboard"; +import useStore from "~/app/states/store"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import * as z from "zod"; +import { toast } from "sonner"; +import reactFlowToWorkflow from "@/app/utils/reactFlowToWorkflow"; +import { saveWorkflow } from "@/app/utils/saveWorkflow"; +import { useRouter } from "next/navigation"; + +const formSchema = z.object({ + name: z.string().min(1, { + message: "Name is required.", + }), + description: z.string().default(""), +}); const General = () => { const { copied, copyToClipboard } = useClipboard(); + const { flowState, setFlowState, nodes, edges } = useStore((state) => ({ + flowState: state.flowState, + setFlowState: state.setFlowState, + nodes: state.nodes, + edges: state.edges, + })); + + const form = useForm({ + resolver: zodResolver(formSchema), + shouldUnregister: false, + mode: "all", + }); + + const { formState, register, handleSubmit } = form; + const router = useRouter(); + + const onSubmit = async (data: any) => { + setFlowState({ + ...flowState, + name: data.name, + description: data.description, + }); + const { workflowResponse, errors } = await reactFlowToWorkflow({ + nodes, + edges, + }); + const saveResponse = await saveWorkflow(workflowResponse); + setFlowState({ + description: data.description, + name: data.name, + id: saveResponse.id, + }); + if (saveResponse) { + toast.success(flowState.id ? "Workflow updated" : "Workflow created"); + } + router.push(`/flow?id=${saveResponse.id}`); + }; + + useEffect(() => { + console.log(flowState); + if (flowState) { + form.setValue("name", flowState.name); + form.setValue("description", flowState.description); + } + }, [flowState]); return ( -
-

- Workflow Info -

-
- - -
-
- - -
-
- -
- copyToClipboard("1f77c5cb-faee-4b79-aec3-8fba9f3b7711") - } - > - 1f77c5cb-faee-4b79-aec3-8fba9f3b7711 -
- {copied ? ( -
- Copied! - -
- ) : ( -
- Click to copy - -
- )} +
+
+

+ Workflow Info +

+
+ + +
+
+ + +
+
+ +
+ copyToClipboard("1f77c5cb-faee-4b79-aec3-8fba9f3b7711") + } + > +

{flowState?.id}

+
+ {copied ? ( +
+ Copied! + +
+ ) : ( +
+ Click to copy + +
+ )} +
+

+ Use this ID to reference your workflow in the API +

-

- Use this ID to reference your workflow in the API -

+
- -
+ ); };