diff --git a/next/src/components/workflow/AbstractNode.tsx b/next/src/components/workflow/AbstractNode.tsx index 88c443b0bf..18f4cd12fd 100644 --- a/next/src/components/workflow/AbstractNode.tsx +++ b/next/src/components/workflow/AbstractNode.tsx @@ -4,8 +4,16 @@ import type { HandleType, Position } from "reactflow"; import { Handle } from "reactflow"; import clsx from "clsx"; +interface Handle { + position: Position; + type: HandleType; + className?: string; + id?: string; + text?: string; +} + interface NodeProps extends PropsWithChildren { - handles: [Position, HandleType][]; + handles: Handle[]; selected: boolean; status?: string; } @@ -24,14 +32,19 @@ function AbstractNode(props: NodeProps) { )} > {props.children} - {props.handles.map(([position, type]) => ( + {props.handles.map(({ position, type, text, className, id }, i) => ( + className={clsx( + "border-gradient !hover:border-white grid !h-fit !w-fit place-items-center !rounded-md !bg-black p-1 text-xs font-light", + className + )} + > + {text} + ))} ); diff --git a/next/src/components/workflow/BasicNode.tsx b/next/src/components/workflow/BasicNode.tsx index ac7dc7352b..7ba65e29cd 100644 --- a/next/src/components/workflow/BasicNode.tsx +++ b/next/src/components/workflow/BasicNode.tsx @@ -12,8 +12,8 @@ function BasicNode({ data, selected }: NodeProps) { selected={selected} status={data.status} handles={[ - [Position.Top, "target"], - [Position.Bottom, "source"], + { position: Position.Top, type: "target" }, + { position: Position.Bottom, type: "source" }, ]} >
diff --git a/next/src/components/workflow/Flowchart.tsx b/next/src/components/workflow/Flowchart.tsx index 15cceb56a6..dd6f0bc39a 100644 --- a/next/src/components/workflow/Flowchart.tsx +++ b/next/src/components/workflow/Flowchart.tsx @@ -1,3 +1,4 @@ +import type { ComponentProps, MouseEvent} from "react"; import { type FC, useCallback } from "react"; import type { Connection, EdgeChange, FitViewOptions, NodeChange } from "reactflow"; import ReactFlow, { @@ -11,12 +12,14 @@ import ReactFlow, { } from "reactflow"; import "reactflow/dist/style.css"; -import CustomNode from "./BasicNode"; +import BasicNode from "./BasicNode"; import CustomEdge from "./BasicEdge"; import type { EdgesModel, NodesModel } from "../../types/workflow"; +import IfNode from "./IfNode"; const nodeTypes = { - custom: CustomNode, + if: IfNode, + custom: BasicNode, }; const edgeTypes = { @@ -27,8 +30,8 @@ const fitViewOptions: FitViewOptions = { padding: 0.2, }; -interface FlowChartProps extends React.ComponentProps { - onSave?: (e: React.MouseEvent) => Promise; +interface FlowChartProps extends ComponentProps { + onSave?: (e: MouseEvent) => Promise; controls?: boolean; minimap?: boolean; diff --git a/next/src/components/workflow/IfNode.tsx b/next/src/components/workflow/IfNode.tsx new file mode 100644 index 0000000000..edd6df76aa --- /dev/null +++ b/next/src/components/workflow/IfNode.tsx @@ -0,0 +1,44 @@ +import React, { memo } from "react"; +import { type NodeProps, Position } from "reactflow"; +import type { WorkflowNode } from "../../types/workflow"; +import { getNodeBlockDefinitions } from "../../services/workflow/node-block-definitions"; +import AbstractNode from "./AbstractNode"; + +function IfNode(props: NodeProps) { + const { data, selected } = props; + const definition = getNodeBlockDefinitions().find((d) => d.type === data.block.type); + + return ( + +
+
+
{definition?.name}
+
{definition?.description}
+
+
+
+ ); +} + +export default memo(IfNode); diff --git a/next/src/hooks/useWorkflow.ts b/next/src/hooks/useWorkflow.ts index 1f5ed6dbd1..64430b648a 100644 --- a/next/src/hooks/useWorkflow.ts +++ b/next/src/hooks/useWorkflow.ts @@ -1,9 +1,10 @@ import type { Edge, Node } from "reactflow"; -import { Dispatch, SetStateAction, useEffect, useState } from "react"; +import type { Dispatch, SetStateAction} from "react"; +import { useEffect, useState } from "react"; import { nanoid } from "nanoid"; import { useMutation, useQuery } from "@tanstack/react-query"; import type { NodeBlock, Workflow, WorkflowEdge, WorkflowNode } from "../types/workflow"; -import { toReactFlowEdge, toReactFlowNode } from "../types/workflow"; +import { getNodeType, toReactFlowEdge, toReactFlowNode } from "../types/workflow"; import WorkflowApi from "../services/workflow/workflowApi"; import useSocket from "./useSocket"; import { z } from "zod"; @@ -91,7 +92,7 @@ export const useWorkflow = (workflowId: string, session: Session | null) => { ...(nodes ?? []), { id: ref, - type: "custom", + type: getNodeType(block), position: { x: 0, y: 0 }, data: { id: undefined, diff --git a/next/src/services/workflow/node-block-definitions.ts b/next/src/services/workflow/node-block-definitions.ts index 71addb315d..96adc779bb 100644 --- a/next/src/services/workflow/node-block-definitions.ts +++ b/next/src/services/workflow/node-block-definitions.ts @@ -68,8 +68,17 @@ const SlackWebhookBlockDefinition: NodeBlockDefinition = { ], }; +const IfBlockDefinition: NodeBlockDefinition = { + name: "If Block", + type: "IfBlock", + description: "Conditionally take a path", + image_url: "/tools/web.png", + input_fields: [], + output_fields: [], +}; + export const getNodeBlockDefinitions = () => { - return [UrlStatusCheckBlockDefinition, SlackWebhookBlockDefinition]; + return [UrlStatusCheckBlockDefinition, SlackWebhookBlockDefinition, IfBlockDefinition]; }; export const getNodeBlockDefinitionFromNode = (node: Node) => { diff --git a/next/src/styles/globals.css b/next/src/styles/globals.css index 029f615259..c2f78a8ffb 100644 --- a/next/src/styles/globals.css +++ b/next/src/styles/globals.css @@ -13,9 +13,10 @@ body { } .border-gradient { + /* Mark as important to override react flow styling */ background: linear-gradient(black, black) padding-box, - linear-gradient(to bottom, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0.15)) border-box; - border: 1px solid transparent; + linear-gradient(to bottom, rgba(255, 255, 255, 0.35), rgba(255, 255, 255, 0.15)) border-box !important; + border: 1px solid transparent !important; } .radial-background-1 { @@ -314,16 +315,17 @@ Not supports in Firefox and IE */ .animate-border-pulse { animation: border-pulse-animation 2s infinite; } + @keyframes moveBackground { from { - background-position: 0 0; + background-position: 0 0; } to { - background-position: 100% 0; + background-position: 100% 0; } - } +} - .bg-stars { +.bg-stars { animation: moveBackground 30s linear infinite; background: url("/stars.svg"); background-size: cover; @@ -333,4 +335,4 @@ Not supports in Firefox and IE */ left: 0; right: 0; z-index: -1; - } \ No newline at end of file +} \ No newline at end of file diff --git a/next/src/types/workflow.ts b/next/src/types/workflow.ts index eca87d63a4..66435522da 100644 --- a/next/src/types/workflow.ts +++ b/next/src/types/workflow.ts @@ -43,7 +43,7 @@ export const toReactFlowNode = (node: WorkflowNode) => id: node.id ?? node.ref, data: node, position: { x: node.pos_x, y: node.pos_y }, - type: "custom", + type: getNodeType(node.block), } as Node); export const toReactFlowEdge = (edge: WorkflowEdge) => @@ -54,3 +54,12 @@ export const toReactFlowEdge = (edge: WorkflowEdge) => ...edge, }, } as Edge); + +export const getNodeType = (block: NodeBlock) => { + switch (block.type) { + case "IfBlock": + return "if"; + default: + return "custom"; + } +};