Skip to content

Commit

Permalink
feat+refactor(ui): Prevent node drag from triggering dnd flow save + …
Browse files Browse the repository at this point in the history
…add schemas for types and add to types/schemas
  • Loading branch information
daryllimyt committed Mar 10, 2024
1 parent 39aea58 commit 981efd4
Show file tree
Hide file tree
Showing 6 changed files with 67 additions and 72 deletions.
47 changes: 19 additions & 28 deletions frontend/src/components/canvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,11 @@ import ReactFlow, {
import "reactflow/dist/style.css"

import { useParams } from "next/navigation"
import { useSession } from "@/providers/session"
import { ActionType } from "@/types"

import { saveFlow } from "@/lib/flow"
import { ActionMetadata, WorkflowResponse } from "@/types/schemas"
import { updateDndFlow } from "@/lib/flow"
import { useToast } from "@/components/ui/use-toast"
import ActionNode, { ActionNodeData } from "@/components/action-node"

Expand All @@ -34,30 +36,6 @@ const defaultEdgeOptions = {
style: { strokeWidth: 2 },
}

type ActionMetadata = {
id: string
workflow_id: string
title: string
description: string
}

interface ActionResponse {
id: string
title: string
description: string
status: string
inputs: { [key: string]: any } | null
}

interface WorkflowResponse {
id: string
title: string
description: string
status: string
actions: { [key: string]: ActionResponse[] }
object: { [key: string]: any } | null
}

const WorkflowCanvas: React.FC = () => {
const reactFlowWrapper = useRef<HTMLDivElement>(null)
const [nodes, setNodes, onNodesChange] = useNodesState([])
Expand All @@ -68,6 +46,7 @@ const WorkflowCanvas: React.FC = () => {
const { setViewport } = useReactFlow()
const params = useParams<{ id: string }>()
const workflowId = params.id
const session = useSession()

const { toast } = useToast()

Expand Down Expand Up @@ -141,7 +120,7 @@ const WorkflowCanvas: React.FC = () => {
(params: Edge | Connection) => {
setEdges((eds) => addEdge(params, eds))
},
[toast, edges, setEdges]
[edges, setEdges]
)

const onDragOver = useCallback(
Expand All @@ -152,6 +131,7 @@ const WorkflowCanvas: React.FC = () => {
[nodes]
)

// Adding a new node
const onDrop = useCallback(
async (event: React.DragEvent) => {
event.preventDefault()
Expand Down Expand Up @@ -222,11 +202,21 @@ const WorkflowCanvas: React.FC = () => {
[edges, setEdges]
)

// Saving react flow instance state
useEffect(() => {
if (workflowId && reactFlowInstance) {
saveFlow(workflowId, reactFlowInstance)
updateDndFlow(session, workflowId, reactFlowInstance)
}
}, [nodes, edges])
}, [edges])

const onNodesDragStop = useCallback(
(event: React.MouseEvent, node: Node, nodes: Node[]) => {
if (workflowId && reactFlowInstance) {
updateDndFlow(session, workflowId, reactFlowInstance)
}
},
[workflowId, reactFlowInstance]
)

return (
<div ref={reactFlowWrapper} style={{ height: "100%" }}>
Expand All @@ -241,6 +231,7 @@ const WorkflowCanvas: React.FC = () => {
onInit={setReactFlowInstance}
onNodesChange={onNodesChange}
onNodesDelete={onNodesDelete}
onNodeDragStop={onNodesDragStop}
defaultEdgeOptions={defaultEdgeOptions}
nodeTypes={nodeTypes}
fitViewOptions={{ maxZoom: 1 }}
Expand Down
33 changes: 15 additions & 18 deletions frontend/src/components/forms/workflow.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useSession } from "@/providers/session"
import { zodResolver } from "@hookform/resolvers/zod"
import { useMutation } from "@tanstack/react-query"
import axios from "axios"
import { CircleIcon, Save } from "lucide-react"
import { useForm } from "react-hook-form"
import { z } from "zod"

import { updateWorkflow } from "@/lib/flow"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import {
Expand All @@ -23,13 +24,16 @@ import {
TooltipContent,
TooltipTrigger,
} from "@/components/ui/tooltip"
import { toast } from "@/components/ui/use-toast"

// Define formSchema for type safety
const workflowFormSchema = z.object({
title: z.string(),
description: z.string(),
})

type WorkflowForm = z.infer<typeof workflowFormSchema>

interface WorkflowFormProps {
workflowId: string
workflowTitle: string
Expand All @@ -43,31 +47,20 @@ export function WorkflowForm({
workflowDescription,
workflowStatus,
}: WorkflowFormProps): React.JSX.Element {
const form = useForm<z.infer<typeof workflowFormSchema>>({
const session = useSession()
console.log("WorkflowForm", session)
const form = useForm<WorkflowForm>({
resolver: zodResolver(workflowFormSchema),
defaultValues: {
title: workflowTitle || "",
description: workflowDescription || "",
},
})

// Submit form and update Workflow
async function updateWorkflow(
workflowId: string,
values: z.infer<typeof workflowFormSchema>
) {
const response = await axios.post(
`http://localhost:8000/workflows/${workflowId}`,
values
)
return response.data // Adjust based on what your API returns
}

function useUpdateWorkflow(workflowId: string) {
const mutation = useMutation({
mutationFn: (values: z.infer<typeof workflowFormSchema>) =>
updateWorkflow(workflowId, values),
// Configure your mutation behavior here
mutationFn: (values: WorkflowForm) =>
updateWorkflow(session, workflowId, values),
onSuccess: (data, variables, context) => {
console.log("Workflow update successful", data)
},
Expand All @@ -80,8 +73,12 @@ export function WorkflowForm({
}

const { mutate } = useUpdateWorkflow(workflowId)
function onSubmit(values: z.infer<typeof workflowFormSchema>) {
function onSubmit(values: WorkflowForm) {
mutate(values)
toast({
title: "Saved workflow",
description: "Workflow updated successfully.",
})
}

return (
Expand Down
14 changes: 1 addition & 13 deletions frontend/src/components/workspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import * as React from "react"
import { WorkflowBuilderProvider } from "@/providers/builder"
import { useSessionContext } from "@/providers/session"
import {
Blend,
BookText,
Expand Down Expand Up @@ -38,8 +37,6 @@ import { ActionTiles } from "@/components/action-tiles"
import { WorkflowCanvas } from "@/components/canvas"
import { WorkflowPanel } from "@/components/panel"

import { Skeleton } from "./ui/skeleton"

interface WorkspaceProps {
defaultLayout: number[] | undefined
defaultCollapsed?: boolean
Expand All @@ -51,11 +48,6 @@ export function Workspace({
defaultCollapsed = false,
navCollapsedSize,
}: WorkspaceProps) {
const { session, isLoading } = useSessionContext()
if (!session) {
throw new Error("Invalid session")
}

const sidePanelRef = React.useRef<ImperativePanelHandle>(null)
const [isCollapsed, setIsCollapsed] = React.useState(defaultCollapsed)

Expand Down Expand Up @@ -88,13 +80,9 @@ export function Workspace({
)}`
}

if (isLoading) {
return <Skeleton className="h-9 w-96" />
}

return (
<ReactFlowProvider>
<WorkflowBuilderProvider session={session}>
<WorkflowBuilderProvider>
<TooltipProvider delayDuration={0}>
<ResizablePanelGroup
direction="horizontal"
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/flow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { z } from "zod"
import { WorkflowMetadata, workflowMetadataSchema } from "@/types/schemas"
import { getAuthenticatedClient } from "@/lib/api"

export async function saveFlow(
export async function updateDndFlow(
maybeSession: Session | null,
workflowId: string,
reactFlowInstance: ReactFlowInstance
Expand Down
24 changes: 12 additions & 12 deletions frontend/src/providers/builder.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import React, {
createContext,
ReactNode,
SetStateAction,
useCallback,
useContext,
} from "react"
import { useParams } from "next/navigation"
import { type Session } from "@supabase/supabase-js"
import { useSession } from "@/providers/session"
import { Node, useReactFlow } from "reactflow"

import { saveFlow } from "@/lib/flow"
import { updateDndFlow } from "@/lib/flow"

interface ReactFlowContextType {
setNodes: React.Dispatch<SetStateAction<Node[]>>
Expand All @@ -19,25 +20,24 @@ const ReactFlowInteractionsContext = createContext<
>(undefined)

interface ReactFlowInteractionsProviderProps {
session: Session
children: ReactNode
}

export const WorkflowBuilderProvider: React.FC<
ReactFlowInteractionsProviderProps
> = ({ session, children }) => {
> = ({ children }) => {
const maybeSession = useSession()
const reactFlowInstance = useReactFlow()
const params = useParams<{ id: string }>()
const workflowId = params.id

const setReactFlowNodes = (nodes: Node[] | ((nodes: Node[]) => Node[])) => {
if (!session) {
console.error("Invalid session: cannot set nodes")
return
}
reactFlowInstance.setNodes(nodes)
saveFlow(session, workflowId, reactFlowInstance)
}
const setReactFlowNodes = useCallback(
(nodes: Node[] | ((nodes: Node[]) => Node[])) => {
reactFlowInstance.setNodes(nodes)
updateDndFlow(maybeSession, workflowId, reactFlowInstance)
},
[maybeSession, workflowId, reactFlowInstance]
)

return (
<ReactFlowInteractionsContext.Provider
Expand Down
19 changes: 19 additions & 0 deletions frontend/src/types/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,29 @@ export const actionResponseSchema = z.object({

export type ActionResponse = z.infer<typeof actionResponseSchema>

export const actionMetadataSchema = z.object({
id: z.string(),
workflow_id: z.string(),
title: z.string(),
description: z.string(),
})
export type ActionMetadata = z.infer<typeof actionMetadataSchema>

export const workflowMetadataSchema = z.object({
id: z.string(),
title: z.string(),
description: z.string(),
status: z.enum(["online", "offline"]),
})
export type WorkflowMetadata = z.infer<typeof workflowMetadataSchema>

export const workflowResponseSchema = z.object({
id: z.string(),
title: z.string(),
description: z.string(),
status: z.string(),
actions: z.record(z.array(actionResponseSchema)),
object: z.record(z.any()).nullable(),
})

export type WorkflowResponse = z.infer<typeof workflowResponseSchema>

0 comments on commit 981efd4

Please sign in to comment.