diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/CodeBlockNode/CodeBlockNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/CodeBlockNode/CodeBlockNode.tsx index c282b8c7..cb1cd43b 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/CodeBlockNode/CodeBlockNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/CodeBlockNode/CodeBlockNode.tsx @@ -2,14 +2,23 @@ import { Label } from "@/components/ui/label"; import { CodeEditor } from "@/routes/workflows/components/CodeEditor"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; import { CodeIcon } from "@radix-ui/react-icons"; -import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; +import { + Handle, + NodeProps, + Position, + useNodes, + useReactFlow, +} from "@xyflow/react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { CodeBlockNode } from "./types"; import { useState } from "react"; +import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils"; +import { AppNode } from ".."; function CodeBlockNode({ id, data }: NodeProps) { - const { updateNodeData } = useReactFlow(); + const { updateNodeData, setNodes } = useReactFlow(); + const nodes = useNodes(); const deleteNodeCallback = useDeleteNodeCallback(); const [label, setLabel] = useState(data.label); const [inputs, setInputs] = useState({ @@ -43,6 +52,13 @@ function CodeBlockNode({ id, data }: NodeProps) { onChange={(value) => { setLabel(value); updateNodeData(id, { label: value }); + setNodes( + getUpdatedNodesAfterLabelUpdateForParameterKeys( + id, + value, + nodes as Array, + ), + ); }} /> Code Block diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/DownloadNode/DownloadNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/DownloadNode/DownloadNode.tsx index aa1d61e7..9fb2e5dc 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/DownloadNode/DownloadNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/DownloadNode/DownloadNode.tsx @@ -2,14 +2,25 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; import { DownloadIcon } from "@radix-ui/react-icons"; -import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; +import { + Handle, + NodeProps, + Position, + useNodes, + useReactFlow, +} from "@xyflow/react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { DownloadNode } from "./types"; +import { useState } from "react"; +import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils"; +import { AppNode } from ".."; function DownloadNode({ id, data }: NodeProps) { - const { updateNodeData } = useReactFlow(); + const { updateNodeData, setNodes } = useReactFlow(); + const nodes = useNodes(); const deleteNodeCallback = useDeleteNodeCallback(); + const [label, setLabel] = useState(data.label); return (
@@ -33,9 +44,19 @@ function DownloadNode({ id, data }: NodeProps) {
updateNodeData(id, { label: value })} + onChange={(value) => { + setLabel(value); + updateNodeData(id, { label: value }); + setNodes( + getUpdatedNodesAfterLabelUpdateForParameterKeys( + id, + value, + nodes as Array, + ), + ); + }} /> Download Block
diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/FileParserNode/FileParserNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/FileParserNode/FileParserNode.tsx index 9717cd03..fd64116d 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/FileParserNode/FileParserNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/FileParserNode/FileParserNode.tsx @@ -1,15 +1,24 @@ import { Input } from "@/components/ui/input"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; import { CursorTextIcon } from "@radix-ui/react-icons"; -import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; +import { + Handle, + NodeProps, + Position, + useNodes, + useReactFlow, +} from "@xyflow/react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { FileParserNode } from "./types"; import { useState } from "react"; +import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils"; +import { AppNode } from ".."; function FileParserNode({ id, data }: NodeProps) { - const { updateNodeData } = useReactFlow(); + const { updateNodeData, setNodes } = useReactFlow(); const deleteNodeCallback = useDeleteNodeCallback(); + const nodes = useNodes(); const [label, setLabel] = useState(data.label); const [inputs, setInputs] = useState({ fileUrl: data.fileUrl, @@ -42,6 +51,13 @@ function FileParserNode({ id, data }: NodeProps) { onChange={(value) => { setLabel(value); updateNodeData(id, { label: value }); + setNodes( + getUpdatedNodesAfterLabelUpdateForParameterKeys( + id, + value, + nodes as Array, + ), + ); }} /> File Parser Block diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/LoopNode/LoopNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/LoopNode/LoopNode.tsx index df524c37..fd934cbb 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/LoopNode/LoopNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/LoopNode/LoopNode.tsx @@ -14,9 +14,11 @@ import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { LoopNode } from "./types"; import { useState } from "react"; +import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils"; +import { AppNode } from ".."; function LoopNode({ id, data }: NodeProps) { - const { updateNodeData } = useReactFlow(); + const { updateNodeData, setNodes } = useReactFlow(); const nodes = useNodes(); const deleteNodeCallback = useDeleteNodeCallback(); const [label, setLabel] = useState(data.label); @@ -77,6 +79,13 @@ function LoopNode({ id, data }: NodeProps) { onChange={(value) => { setLabel(value); updateNodeData(id, { label: value }); + setNodes( + getUpdatedNodesAfterLabelUpdateForParameterKeys( + id, + value, + nodes as Array, + ), + ); }} /> Loop Block diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/SendEmailNode/SendEmailNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/SendEmailNode/SendEmailNode.tsx index c564a770..74fac1dd 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/SendEmailNode/SendEmailNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/SendEmailNode/SendEmailNode.tsx @@ -3,14 +3,23 @@ import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; import { EnvelopeClosedIcon } from "@radix-ui/react-icons"; -import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; +import { + Handle, + NodeProps, + Position, + useNodes, + useReactFlow, +} from "@xyflow/react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { SendEmailNode } from "./types"; import { useState } from "react"; +import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils"; +import { AppNode } from ".."; function SendEmailNode({ id, data }: NodeProps) { - const { updateNodeData } = useReactFlow(); + const { updateNodeData, setNodes } = useReactFlow(); + const nodes = useNodes(); const deleteNodeCallback = useDeleteNodeCallback(); const [label, setLabel] = useState(data.label); const [inputs, setInputs] = useState({ @@ -56,6 +65,13 @@ function SendEmailNode({ id, data }: NodeProps) { onChange={(value) => { setLabel(value); updateNodeData(id, { label: value }); + setNodes( + getUpdatedNodesAfterLabelUpdateForParameterKeys( + id, + value, + nodes as Array, + ), + ); }} /> Send Email Block diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx index 85128a03..05365bee 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx @@ -23,7 +23,10 @@ import { } from "@xyflow/react"; import { useState } from "react"; import { AppNode } from ".."; -import { getOutputParameterKey } from "../../workflowEditorUtils"; +import { + getOutputParameterKey, + getUpdatedNodesAfterLabelUpdateForParameterKeys, +} from "../../workflowEditorUtils"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import { TaskNodeDisplayModeSwitch } from "./TaskNodeDisplayModeSwitch"; @@ -54,7 +57,7 @@ function getPreviousNodeIds( } function TaskNode({ id, data }: NodeProps) { - const { updateNodeData } = useReactFlow(); + const { updateNodeData, setNodes } = useReactFlow(); const [displayMode, setDisplayMode] = useState("basic"); const { editable } = data; const deleteNodeCallback = useDeleteNodeCallback(); @@ -419,7 +422,13 @@ function TaskNode({ id, data }: NodeProps) { editable={editable} onChange={(value) => { setLabel(value); - updateNodeData(id, { label: value }); + setNodes( + getUpdatedNodesAfterLabelUpdateForParameterKeys( + id, + value, + nodes as Array, + ), + ); }} /> Task Block diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/TextPromptNode/TextPromptNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/TextPromptNode/TextPromptNode.tsx index 4db72d78..68cc12f6 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/TextPromptNode/TextPromptNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/TextPromptNode/TextPromptNode.tsx @@ -5,14 +5,23 @@ import { Separator } from "@/components/ui/separator"; import { CodeEditor } from "@/routes/workflows/components/CodeEditor"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; import { CursorTextIcon } from "@radix-ui/react-icons"; -import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; +import { + Handle, + NodeProps, + Position, + useNodes, + useReactFlow, +} from "@xyflow/react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { TextPromptNode } from "./types"; import { useState } from "react"; +import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils"; +import { AppNode } from ".."; function TextPromptNode({ id, data }: NodeProps) { - const { updateNodeData } = useReactFlow(); + const { updateNodeData, setNodes } = useReactFlow(); + const nodes = useNodes(); const { editable } = data; const deleteNodeCallback = useDeleteNodeCallback(); const [label, setLabel] = useState(data.label); @@ -48,6 +57,13 @@ function TextPromptNode({ id, data }: NodeProps) { onChange={(value) => { setLabel(value); updateNodeData(id, { label: value }); + setNodes( + getUpdatedNodesAfterLabelUpdateForParameterKeys( + id, + value, + nodes as Array, + ), + ); }} /> Text Prompt Block diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/UploadNode/UploadNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/UploadNode/UploadNode.tsx index eeca73b6..3c05c4a8 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/UploadNode/UploadNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/UploadNode/UploadNode.tsx @@ -2,14 +2,25 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; import { UploadIcon } from "@radix-ui/react-icons"; -import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; +import { + Handle, + NodeProps, + Position, + useNodes, + useReactFlow, +} from "@xyflow/react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { UploadNode } from "./types"; +import { useState } from "react"; +import { getUpdatedNodesAfterLabelUpdateForParameterKeys } from "../../workflowEditorUtils"; +import { AppNode } from ".."; function UploadNode({ id, data }: NodeProps) { - const { updateNodeData } = useReactFlow(); + const { updateNodeData, setNodes } = useReactFlow(); + const nodes = useNodes(); const deleteNodeCallback = useDeleteNodeCallback(); + const [label, setLabel] = useState(data.label); return (
@@ -33,9 +44,19 @@ function UploadNode({ id, data }: NodeProps) {
updateNodeData(id, { label: value })} + onChange={(value) => { + setLabel(value); + updateNodeData(id, { label: value }); + setNodes( + getUpdatedNodesAfterLabelUpdateForParameterKeys( + id, + value, + nodes as Array, + ), + ); + }} /> Upload Block
diff --git a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts index 23bf78a4..a9c1e04e 100644 --- a/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts +++ b/skyvern-frontend/src/routes/workflows/editor/workflowEditorUtils.ts @@ -14,6 +14,7 @@ import { sendEmailNodeDefaultData } from "./nodes/SendEmailNode/types"; import { taskNodeDefaultData } from "./nodes/TaskNode/types"; import { textPromptNodeDefaultData } from "./nodes/TextPromptNode/types"; import { uploadNodeDefaultData } from "./nodes/UploadNode/types"; +import type { Node } from "@xyflow/react"; export const NEW_NODE_LABEL_PREFIX = "Block "; @@ -620,6 +621,42 @@ function getOutputParameterKey(label: string) { return label + "_output"; } +function getUpdatedNodesAfterLabelUpdateForParameterKeys( + id: string, + newLabel: string, + nodes: Array, +): Array { + const labelUpdatedNode = nodes.find((node) => node.id === id); + if (!labelUpdatedNode) { + return nodes; + } + const oldLabel = labelUpdatedNode.data.label as string; + return nodes.map((node) => { + if (node.type === "task") { + return { + ...node, + data: { + ...node.data, + parameterKeys: (node.data.parameterKeys as Array).map( + (key) => + key === getOutputParameterKey(oldLabel) + ? getOutputParameterKey(newLabel) + : key, + ), + label: node.id === id ? newLabel : node.data.label, + }, + }; + } + return { + ...node, + data: { + ...node.data, + label: node.id === id ? newLabel : node.data.label, + }, + }; + }); +} + export { createNode, generateNodeData, @@ -629,4 +666,5 @@ export { generateNodeLabel, convertEchoParameters, getOutputParameterKey, + getUpdatedNodesAfterLabelUpdateForParameterKeys, };