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 78bad11a..c282b8c7 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/CodeBlockNode/CodeBlockNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/CodeBlockNode/CodeBlockNode.tsx @@ -6,10 +6,15 @@ import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { CodeBlockNode } from "./types"; +import { useState } from "react"; function CodeBlockNode({ id, data }: NodeProps) { const { updateNodeData } = useReactFlow(); const deleteNodeCallback = useDeleteNodeCallback(); + const [label, setLabel] = useState(data.label); + const [inputs, setInputs] = useState({ + code: data.code, + }); return (
@@ -33,9 +38,12 @@ function CodeBlockNode({ id, data }: NodeProps) {
updateNodeData(id, { label: value })} + onChange={(value) => { + setLabel(value); + updateNodeData(id, { label: value }); + }} /> Code Block
@@ -50,11 +58,12 @@ function CodeBlockNode({ id, data }: NodeProps) { { if (!data.editable) { return; } + setInputs({ ...inputs, code: value }); updateNodeData(id, { code: value }); }} className="nopan" 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 db82dc1e..aa1d61e7 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/DownloadNode/DownloadNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/DownloadNode/DownloadNode.tsx @@ -49,16 +49,7 @@ function DownloadNode({ id, data }: NodeProps) {
- { - if (!data.editable) { - return; - } - updateNodeData(id, { url: event.target.value }); - }} - className="nopan" - /> +
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 f918900a..9717cd03 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/FileParserNode/FileParserNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/FileParserNode/FileParserNode.tsx @@ -5,10 +5,15 @@ import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { FileParserNode } from "./types"; +import { useState } from "react"; function FileParserNode({ id, data }: NodeProps) { const { updateNodeData } = useReactFlow(); const deleteNodeCallback = useDeleteNodeCallback(); + const [label, setLabel] = useState(data.label); + const [inputs, setInputs] = useState({ + fileUrl: data.fileUrl, + }); return (
@@ -32,9 +37,12 @@ function FileParserNode({ id, data }: NodeProps) {
updateNodeData(id, { label: value })} + onChange={(value) => { + setLabel(value); + updateNodeData(id, { label: value }); + }} /> File Parser Block
@@ -49,11 +57,12 @@ function FileParserNode({ id, data }: NodeProps) {
File URL { if (!data.editable) { return; } + setInputs({ ...inputs, fileUrl: event.target.value }); updateNodeData(id, { fileUrl: event.target.value }); }} className="nopan" 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 7a735c4b..df524c37 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/LoopNode/LoopNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/LoopNode/LoopNode.tsx @@ -13,11 +13,16 @@ import { import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { LoopNode } from "./types"; +import { useState } from "react"; function LoopNode({ id, data }: NodeProps) { const { updateNodeData } = useReactFlow(); const nodes = useNodes(); const deleteNodeCallback = useDeleteNodeCallback(); + const [label, setLabel] = useState(data.label); + const [inputs, setInputs] = useState({ + loopValue: data.loopValue, + }); const children = nodes.filter((node) => node.parentId === id); const furthestDownChild: Node | null = children.reduce( @@ -67,9 +72,12 @@ function LoopNode({ id, data }: NodeProps) {
updateNodeData(id, { label: value })} + onChange={(value) => { + setLabel(value); + updateNodeData(id, { label: value }); + }} /> Loop Block
@@ -83,11 +91,12 @@ function LoopNode({ id, data }: NodeProps) {
{ if (!data.editable) { return; } + setInputs({ ...inputs, loopValue: event.target.value }); updateNodeData(id, { loopValue: event.target.value }); }} placeholder="What value are you iterating over?" 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 721b8b3e..c564a770 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/SendEmailNode/SendEmailNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/SendEmailNode/SendEmailNode.tsx @@ -7,10 +7,27 @@ import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { SendEmailNode } from "./types"; +import { useState } from "react"; function SendEmailNode({ id, data }: NodeProps) { const { updateNodeData } = useReactFlow(); const deleteNodeCallback = useDeleteNodeCallback(); + const [label, setLabel] = useState(data.label); + const [inputs, setInputs] = useState({ + sender: data.sender, + recipients: data.recipients, + subject: data.subject, + body: data.body, + fileAttachments: data.fileAttachments, + }); + + function handleChange(key: string, value: unknown) { + if (!data.editable) { + return; + } + setInputs({ ...inputs, [key]: value }); + updateNodeData(id, { [key]: value }); + } return (
@@ -34,9 +51,12 @@ function SendEmailNode({ id, data }: NodeProps) {
updateNodeData(id, { label: value })} + onChange={(value) => { + setLabel(value); + updateNodeData(id, { label: value }); + }} /> Send Email Block
@@ -54,9 +74,9 @@ function SendEmailNode({ id, data }: NodeProps) { if (!data.editable) { return; } - updateNodeData(id, { sender: event.target.value }); + handleChange("sender", event.target.value); }} - value={data.sender} + value={inputs.sender} placeholder="example@gmail.com" className="nopan" /> @@ -68,9 +88,9 @@ function SendEmailNode({ id, data }: NodeProps) { if (!data.editable) { return; } - updateNodeData(id, { recipients: event.target.value }); + handleChange("recipients", event.target.value); }} - value={data.recipients} + value={inputs.recipients} placeholder="example@gmail.com, example2@gmail.com..." className="nopan" /> @@ -83,9 +103,9 @@ function SendEmailNode({ id, data }: NodeProps) { if (!data.editable) { return; } - updateNodeData(id, { subject: event.target.value }); + handleChange("subject", event.target.value); }} - value={data.subject} + value={inputs.subject} placeholder="What is the gist?" className="nopan" /> @@ -97,9 +117,9 @@ function SendEmailNode({ id, data }: NodeProps) { if (!data.editable) { return; } - updateNodeData(id, { body: event.target.value }); + handleChange("body", event.target.value); }} - value={data.body} + value={inputs.body} placeholder="What would you like to say?" className="nopan" /> @@ -108,12 +128,12 @@ function SendEmailNode({ id, data }: NodeProps) {
{ if (!data.editable) { return; } - updateNodeData(id, { fileAttachments: event.target.value }); + handleChange("fileAttachments", event.target.value); }} className="nopan" /> 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 45c5b8eb..8fda30b4 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx @@ -32,19 +32,41 @@ function TaskNode({ id, data }: NodeProps) { const { editable } = data; const deleteNodeCallback = useDeleteNodeCallback(); + const [label, setLabel] = useState(data.label); + const [inputs, setInputs] = useState({ + url: data.url, + navigationGoal: data.navigationGoal, + dataExtractionGoal: data.dataExtractionGoal, + dataSchema: data.dataSchema, + maxRetries: data.maxRetries, + maxStepsOverride: data.maxStepsOverride, + allowDownloads: data.allowDownloads, + errorCodeMapping: data.errorCodeMapping, + totpVerificationUrl: data.totpVerificationUrl, + totpIdentifier: data.totpIdentifier, + }); + + function handleChange(key: string, value: unknown) { + if (!editable) { + return; + } + setInputs({ ...inputs, [key]: value }); + updateNodeData(id, { [key]: value }); + } + const basicContent = ( <>
{ if (!editable) { return; } - updateNodeData(id, { url: event.target.value }); + handleChange("url", event.target.value); }} placeholder="https://" /> @@ -56,9 +78,9 @@ function TaskNode({ id, data }: NodeProps) { if (!editable) { return; } - updateNodeData(id, { navigationGoal: event.target.value }); + handleChange("navigationGoal", event.target.value); }} - value={data.navigationGoal} + value={inputs.navigationGoal} placeholder="What are you looking to do?" className="nopan" /> @@ -83,9 +105,9 @@ function TaskNode({ id, data }: NodeProps) { if (!editable) { return; } - updateNodeData(id, { url: event.target.value }); + handleChange("url", event.target.value); }} - value={data.url} + value={inputs.url} placeholder="https://" className="nopan" /> @@ -97,9 +119,9 @@ function TaskNode({ id, data }: NodeProps) { if (!editable) { return; } - updateNodeData(id, { navigationGoal: event.target.value }); + handleChange("navigationGoal", event.target.value); }} - value={data.navigationGoal} + value={inputs.navigationGoal} placeholder="What are you looking to do?" className="nopan" /> @@ -120,11 +142,9 @@ function TaskNode({ id, data }: NodeProps) { if (!editable) { return; } - updateNodeData(id, { - dataExtractionGoal: event.target.value, - }); + handleChange("dataExtractionGoal", event.target.value); }} - value={data.dataExtractionGoal} + value={inputs.dataExtractionGoal} placeholder="What outputs are you looking to get?" className="nopan" /> @@ -133,27 +153,25 @@ function TaskNode({ id, data }: NodeProps) {
{ if (!editable) { return; } - updateNodeData(id, { - dataSchema: checked ? "{}" : "null", - }); + handleChange("dataSchema", checked ? "{}" : "null"); }} />
- {data.dataSchema !== "null" && ( + {inputs.dataSchema !== "null" && (
{ if (!editable) { return; } - updateNodeData(id, { dataSchema: value }); + handleChange("dataSchema", value); }} className="nowheel nopan" /> @@ -176,14 +194,12 @@ function TaskNode({ id, data }: NodeProps) { placeholder="0" className="nopan w-44" min="0" - value={data.maxRetries ?? 0} + value={inputs.maxRetries ?? 0} onChange={(event) => { if (!editable) { return; } - updateNodeData(id, { - maxRetries: Number(event.target.value), - }); + handleChange("maxRetries", Number(event.target.value)); }} />
@@ -196,14 +212,15 @@ function TaskNode({ id, data }: NodeProps) { placeholder="0" className="nopan w-44" min="0" - value={data.maxStepsOverride ?? 0} + value={inputs.maxStepsOverride ?? 0} onChange={(event) => { if (!editable) { return; } - updateNodeData(id, { - maxStepsOverride: Number(event.target.value), - }); + handleChange( + "maxStepsOverride", + Number(event.target.value), + ); }} />
@@ -213,12 +230,12 @@ function TaskNode({ id, data }: NodeProps) {
{ if (!editable) { return; } - updateNodeData(id, { allowDownloads: checked }); + handleChange("allowDownloads", checked); }} />
@@ -229,28 +246,26 @@ function TaskNode({ id, data }: NodeProps) { Error Messages { if (!editable) { return; } - updateNodeData(id, { - errorCodeMapping: checked ? "{}" : "null", - }); + handleChange("errorCodeMapping", checked ? "{}" : "null"); }} />
- {data.errorCodeMapping !== "null" && ( + {inputs.errorCodeMapping !== "null" && (
{ if (!editable) { return; } - updateNodeData(id, { errorCodeMapping: value }); + handleChange("errorCodeMapping", value); }} className="nowheel nopan" /> @@ -273,11 +288,9 @@ function TaskNode({ id, data }: NodeProps) { if (!editable) { return; } - updateNodeData(id, { - totpVerificationUrl: event.target.value, - }); + handleChange("totpVerificationUrl", event.target.value); }} - value={data.totpVerificationUrl ?? ""} + value={inputs.totpVerificationUrl ?? ""} placeholder="https://" className="nopan" /> @@ -291,9 +304,9 @@ function TaskNode({ id, data }: NodeProps) { if (!editable) { return; } - updateNodeData(id, { totpIdentifier: event.target.value }); + handleChange("totpIdentifier", event.target.value); }} - value={data.totpIdentifier ?? ""} + value={inputs.totpIdentifier ?? ""} placeholder="Identifier" className="nopan" /> @@ -327,9 +340,12 @@ function TaskNode({ id, data }: NodeProps) {
updateNodeData(id, { label: value })} + onChange={(value) => { + setLabel(value); + updateNodeData(id, { label: value }); + }} /> 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 3a1764d3..4db72d78 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/TextPromptNode/TextPromptNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/TextPromptNode/TextPromptNode.tsx @@ -9,11 +9,17 @@ import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { TextPromptNode } from "./types"; +import { useState } from "react"; function TextPromptNode({ id, data }: NodeProps) { const { updateNodeData } = useReactFlow(); const { editable } = data; const deleteNodeCallback = useDeleteNodeCallback(); + const [label, setLabel] = useState(data.label); + const [inputs, setInputs] = useState({ + prompt: data.prompt, + jsonSchema: data.jsonSchema, + }); return (
@@ -37,9 +43,12 @@ function TextPromptNode({ id, data }: NodeProps) {
updateNodeData(id, { label: value })} + onChange={(value) => { + setLabel(value); + updateNodeData(id, { label: value }); + }} /> Text Prompt Block
@@ -57,9 +66,10 @@ function TextPromptNode({ id, data }: NodeProps) { if (!editable) { return; } + setInputs({ ...inputs, prompt: event.target.value }); updateNodeData(id, { prompt: event.target.value }); }} - value={data.prompt} + value={inputs.prompt} placeholder="What do you want to generate?" className="nopan" /> @@ -69,26 +79,31 @@ function TextPromptNode({ id, data }: NodeProps) {
{ if (!editable) { return; } + setInputs({ + ...inputs, + jsonSchema: checked ? "{}" : "null", + }); updateNodeData(id, { jsonSchema: checked ? "{}" : "null", }); }} />
- {data.jsonSchema !== "null" && ( + {inputs.jsonSchema !== "null" && (
{ if (!editable) { return; } + setInputs({ ...inputs, jsonSchema: value }); updateNodeData(id, { jsonSchema: value }); }} className="nowheel nopan" 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 6280d0b8..eeca73b6 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/UploadNode/UploadNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/UploadNode/UploadNode.tsx @@ -49,16 +49,7 @@ function UploadNode({ id, data }: NodeProps) {
- { - if (!data.editable) { - return; - } - updateNodeData(id, { path: event.target.value }); - }} - className="nopan" - /> +