From 3b8b9639f2e7bb3c2b222e6334f0d65931880024 Mon Sep 17 00:00:00 2001 From: Shuchang Zheng Date: Thu, 12 Dec 2024 08:30:04 -0800 Subject: [PATCH] Use popover in add parameter experience (#1377) --- .../src/components/WorkflowBlockInput.tsx | 35 +++++++-- .../components/WorkflowBlockInputTextarea.tsx | 35 +++++++-- .../editor/nodes/ActionNode/ActionNode.tsx | 75 +++++-------------- .../nodes/ExtractionNode/ExtractionNode.tsx | 45 +++-------- .../FileDownloadNode/FileDownloadNode.tsx | 59 ++++----------- .../editor/nodes/LoginNode/LoginNode.tsx | 73 ++++++------------ .../nodes/NavigationNode/NavigationNode.tsx | 60 ++++----------- .../editor/nodes/TaskNode/TaskNode.tsx | 71 +++++------------- .../nodes/ValidationNode/ValidationNode.tsx | 45 ++--------- .../nodes/WorkflowBlockParameterSelect.tsx | 10 +-- 10 files changed, 163 insertions(+), 345 deletions(-) diff --git a/skyvern-frontend/src/components/WorkflowBlockInput.tsx b/skyvern-frontend/src/components/WorkflowBlockInput.tsx index c372ee73..655b4711 100644 --- a/skyvern-frontend/src/components/WorkflowBlockInput.tsx +++ b/skyvern-frontend/src/components/WorkflowBlockInput.tsx @@ -1,19 +1,42 @@ import { PlusIcon } from "@radix-ui/react-icons"; import { cn } from "@/util/utils"; import { Input } from "./ui/input"; +import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; +import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect"; -type Props = React.ComponentProps & { - onIconClick: () => void; +type Props = Omit, "onChange"> & { + onChange: (value: string) => void; + nodeId: string; }; function WorkflowBlockInput(props: Props) { + const { nodeId, onChange, ...inputProps } = props; + return (
- + { + onChange(event.target.value); + }} + />
-
- -
+ + +
+ +
+
+ + { + onChange(`${props.value ?? ""}{{${parameterKey}}}`); + }} + /> + +
); diff --git a/skyvern-frontend/src/components/WorkflowBlockInputTextarea.tsx b/skyvern-frontend/src/components/WorkflowBlockInputTextarea.tsx index b5cf6b92..73d764a3 100644 --- a/skyvern-frontend/src/components/WorkflowBlockInputTextarea.tsx +++ b/skyvern-frontend/src/components/WorkflowBlockInputTextarea.tsx @@ -1,22 +1,45 @@ import { PlusIcon } from "@radix-ui/react-icons"; import { cn } from "@/util/utils"; import { AutoResizingTextarea } from "./AutoResizingTextarea/AutoResizingTextarea"; +import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; +import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect"; -type Props = React.ComponentProps & { - onIconClick: () => void; +type Props = Omit< + React.ComponentProps, + "onChange" +> & { + onChange: (value: string) => void; + nodeId: string; }; function WorkflowBlockInputTextarea(props: Props) { + const { nodeId, onChange, ...textAreaProps } = props; + return (
{ + onChange(event.target.value); + }} className={cn("pr-9", props.className)} />
-
- -
+ + +
+ +
+
+ + { + onChange(`${props.value ?? ""}{{${parameterKey}}}`); + }} + /> + +
); diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/ActionNode/ActionNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/ActionNode/ActionNode.tsx index 53c148ab..df0ac683 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/ActionNode/ActionNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/ActionNode/ActionNode.tsx @@ -22,7 +22,7 @@ import { Switch } from "@/components/ui/switch"; import { ClickIcon } from "@/components/icons/ClickIcon"; import { placeholders, helpTooltips } from "../../helpContent"; import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea"; -import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect"; +import { WorkflowBlockInput } from "@/components/WorkflowBlockInput"; const urlTooltip = "The URL Skyvern is navigating to. Leave this field blank to pick up from where the last block left off."; @@ -32,9 +32,6 @@ const navigationGoalTooltip = const navigationGoalPlaceholder = 'Input {{ name }} into "Name" field.'; function ActionNode({ id, data }: NodeProps) { - const [parametersPanelField, setParametersPanelField] = useState< - string | null - >(null); const { updateNodeData } = useReactFlow(); const { editable } = data; const [label, setLabel] = useNodeLabelChangeHandler({ @@ -107,14 +104,9 @@ function ActionNode({ id, data }: NodeProps) { { - setParametersPanelField("url"); - }} - onChange={(event) => { - if (!editable) { - return; - } - handleChange("url", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("url", value); }} value={inputs.url} placeholder={placeholders["action"]["url"]} @@ -129,14 +121,9 @@ function ActionNode({ id, data }: NodeProps) { { - setParametersPanelField("navigationGoal"); - }} - onChange={(event) => { - if (!editable) { - return; - } - handleChange("navigationGoal", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("navigationGoal", value); }} value={inputs.navigationGoal} placeholder={navigationGoalPlaceholder} @@ -302,16 +289,14 @@ function ActionNode({ id, data }: NodeProps) { content={helpTooltips["action"]["fileSuffix"]} /> - { - if (!editable) { - return; - } - handleChange("downloadSuffix", event.target.value); + onChange={(value) => { + handleChange("downloadSuffix", value); }} /> @@ -326,11 +311,9 @@ function ActionNode({ id, data }: NodeProps) { /> { - setParametersPanelField("totpVerificationUrl"); - }} - onChange={(event) => { - handleChange("totpVerificationUrl", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("totpVerificationUrl", value); }} value={inputs.totpVerificationUrl ?? ""} placeholder={placeholders["action"]["totpVerificationUrl"]} @@ -347,14 +330,9 @@ function ActionNode({ id, data }: NodeProps) { /> { - setParametersPanelField("totpIdentifier"); - }} - onChange={(event) => { - if (!editable) { - return; - } - handleChange("totpIdentifier", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("totpIdentifier", value); }} value={inputs.totpIdentifier ?? ""} placeholder={placeholders["action"]["totpIdentifier"]} @@ -366,25 +344,6 @@ function ActionNode({ id, data }: NodeProps) { - {typeof parametersPanelField === "string" && ( - setParametersPanelField(null)} - onAdd={(parameterKey) => { - if (parametersPanelField === null || !editable) { - return; - } - if (parametersPanelField in inputs) { - const currentValue = - inputs[parametersPanelField as keyof typeof inputs]; - handleChange( - parametersPanelField, - `${currentValue ?? ""}{{ ${parameterKey} }}`, - ); - } - }} - /> - )} ); } diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/ExtractionNode/ExtractionNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/ExtractionNode/ExtractionNode.tsx index add35e84..e722a4c6 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/ExtractionNode/ExtractionNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/ExtractionNode/ExtractionNode.tsx @@ -1,34 +1,30 @@ +import { HelpTooltip } from "@/components/HelpTooltip"; +import { ExtractIcon } from "@/components/icons/ExtractIcon"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; +import { Switch } from "@/components/ui/switch"; +import { CodeEditor } from "@/routes/workflows/components/CodeEditor"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler"; import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; import { useState } from "react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; -import { HelpTooltip } from "@/components/HelpTooltip"; -import { Input } from "@/components/ui/input"; -import { Checkbox } from "@/components/ui/checkbox"; import { dataSchemaExampleValue } from "../types"; -import { CodeEditor } from "@/routes/workflows/components/CodeEditor"; -import { Switch } from "@/components/ui/switch"; import type { ExtractionNode } from "./types"; -import { ExtractIcon } from "@/components/icons/ExtractIcon"; -import { helpTooltips, placeholders } from "../../helpContent"; -import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect"; import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea"; +import { helpTooltips, placeholders } from "../../helpContent"; function ExtractionNode({ id, data }: NodeProps) { - const [parametersPanelField, setParametersPanelField] = useState< - string | null - >(null); const { updateNodeData } = useReactFlow(); const { editable } = data; const [label, setLabel] = useNodeLabelChangeHandler({ @@ -101,14 +97,12 @@ function ExtractionNode({ id, data }: NodeProps) { /> { - setParametersPanelField("dataExtractionGoal"); - }} - onChange={(event) => { + nodeId={id} + onChange={(value) => { if (!editable) { return; } - handleChange("dataExtractionGoal", event.target.value); + handleChange("dataExtractionGoal", value); }} value={inputs.dataExtractionGoal} placeholder={placeholders["extraction"]["dataExtractionGoal"]} @@ -263,25 +257,6 @@ function ExtractionNode({ id, data }: NodeProps) { - {typeof parametersPanelField === "string" && ( - setParametersPanelField(null)} - onAdd={(parameterKey) => { - if (parametersPanelField === null || !editable) { - return; - } - if (parametersPanelField in inputs) { - const currentValue = - inputs[parametersPanelField as keyof typeof inputs]; - handleChange( - parametersPanelField, - `${currentValue ?? ""}{{ ${parameterKey} }}`, - ); - } - }} - /> - )} ); } diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/FileDownloadNode/FileDownloadNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/FileDownloadNode/FileDownloadNode.tsx index b08238b2..4e787863 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/FileDownloadNode/FileDownloadNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/FileDownloadNode/FileDownloadNode.tsx @@ -10,19 +10,18 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { Switch } from "@/components/ui/switch"; +import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea"; import { CodeEditor } from "@/routes/workflows/components/CodeEditor"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler"; import { DownloadIcon } from "@radix-ui/react-icons"; import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; import { useState } from "react"; +import { helpTooltips, placeholders } from "../../helpContent"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import { errorMappingExampleValue } from "../types"; import type { FileDownloadNode } from "./types"; -import { helpTooltips, placeholders } from "../../helpContent"; -import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect"; -import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea"; const urlTooltip = "The URL Skyvern is navigating to. Leave this field blank to pick up from where the last block left off."; @@ -32,9 +31,6 @@ const navigationGoalTooltip = const navigationGoalPlaceholder = "Tell Skyvern which file to download."; function FileDownloadNode({ id, data }: NodeProps) { - const [parametersPanelField, setParametersPanelField] = useState< - string | null - >(null); const { updateNodeData } = useReactFlow(); const { editable } = data; const [label, setLabel] = useNodeLabelChangeHandler({ @@ -109,11 +105,9 @@ function FileDownloadNode({ id, data }: NodeProps) { { - setParametersPanelField("url"); - }} - onChange={(event) => { - handleChange("url", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("url", value); }} value={inputs.url} placeholder={urlPlaceholder} @@ -126,11 +120,9 @@ function FileDownloadNode({ id, data }: NodeProps) { { - setParametersPanelField("navigationGoal"); - }} - onChange={(event) => { - handleChange("navigationGoal", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("navigationGoal", value); }} value={inputs.navigationGoal} placeholder={navigationGoalPlaceholder} @@ -302,11 +294,9 @@ function FileDownloadNode({ id, data }: NodeProps) { /> { - setParametersPanelField("totpVerificationUrl"); - }} - onChange={(event) => { - handleChange("totpVerificationUrl", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("totpVerificationUrl", value); }} value={inputs.totpVerificationUrl ?? ""} placeholder={ @@ -325,11 +315,9 @@ function FileDownloadNode({ id, data }: NodeProps) { /> { - setParametersPanelField("totpIdentifier"); - }} - onChange={(event) => { - handleChange("totpIdentifier", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("totpIdentifier", value); }} value={inputs.totpIdentifier ?? ""} placeholder={placeholders["download"]["totpIdentifier"]} @@ -341,25 +329,6 @@ function FileDownloadNode({ id, data }: NodeProps) { - {typeof parametersPanelField === "string" && ( - setParametersPanelField(null)} - onAdd={(parameterKey) => { - if (parametersPanelField === null || !editable) { - return; - } - if (parametersPanelField in inputs) { - const currentValue = - inputs[parametersPanelField as keyof typeof inputs]; - handleChange( - parametersPanelField, - `${currentValue ?? ""}{{ ${parameterKey} }}`, - ); - } - }} - /> - )} ); } diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginNode.tsx index 9b02ad42..5ac3cdad 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/LoginNode/LoginNode.tsx @@ -1,34 +1,30 @@ +import { HelpTooltip } from "@/components/HelpTooltip"; import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@/components/ui/accordion"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; +import { Switch } from "@/components/ui/switch"; +import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea"; +import { CodeEditor } from "@/routes/workflows/components/CodeEditor"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler"; +import { LockOpen1Icon } from "@radix-ui/react-icons"; import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; import { useState } from "react"; +import { helpTooltips, placeholders } from "../../helpContent"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; -import { HelpTooltip } from "@/components/HelpTooltip"; -import { Input } from "@/components/ui/input"; -import { Checkbox } from "@/components/ui/checkbox"; import { errorMappingExampleValue } from "../types"; -import { CodeEditor } from "@/routes/workflows/components/CodeEditor"; -import { Switch } from "@/components/ui/switch"; -import type { LoginNode } from "./types"; -import { LockOpen1Icon } from "@radix-ui/react-icons"; import { CredentialParameterSelector } from "./CredentialParameterSelector"; -import { helpTooltips, placeholders } from "../../helpContent"; -import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea"; -import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect"; +import type { LoginNode } from "./types"; function LoginNode({ id, data }: NodeProps) { - const [parametersPanelField, setParametersPanelField] = useState< - string | null - >(null); const { updateNodeData } = useReactFlow(); const { editable } = data; const [label, setLabel] = useNodeLabelChangeHandler({ @@ -100,11 +96,9 @@ function LoginNode({ id, data }: NodeProps) { { - setParametersPanelField("url"); - }} - onChange={(event) => { - handleChange("url", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("url", value); }} value={inputs.url} placeholder={placeholders["login"]["url"]} @@ -117,11 +111,9 @@ function LoginNode({ id, data }: NodeProps) { { - setParametersPanelField("navigationGoal"); - }} - onChange={(event) => { - handleChange("navigationGoal", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("navigationGoal", value); }} value={inputs.navigationGoal} placeholder={placeholders["login"]["navigationGoal"]} @@ -296,11 +288,9 @@ function LoginNode({ id, data }: NodeProps) { /> { - setParametersPanelField("totpVerificationUrl"); - }} - onChange={(event) => { - handleChange("totpVerificationUrl", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("totpVerificationUrl", value); }} value={inputs.totpVerificationUrl ?? ""} placeholder={placeholders["login"]["totpVerificationUrl"]} @@ -317,11 +307,9 @@ function LoginNode({ id, data }: NodeProps) { /> { - setParametersPanelField("totpIdentifier"); - }} - onChange={(event) => { - handleChange("totpIdentifier", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("totpIdentifier", value); }} value={inputs.totpIdentifier ?? ""} placeholder={placeholders["login"]["totpIdentifier"]} @@ -333,25 +321,6 @@ function LoginNode({ id, data }: NodeProps) { - {typeof parametersPanelField === "string" && ( - setParametersPanelField(null)} - onAdd={(parameterKey) => { - if (parametersPanelField === null || !editable) { - return; - } - if (parametersPanelField in inputs) { - const currentValue = - inputs[parametersPanelField as keyof typeof inputs]; - handleChange( - parametersPanelField, - `${currentValue ?? ""}{{ ${parameterKey} }}`, - ); - } - }} - /> - )} ); } diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/NavigationNode/NavigationNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/NavigationNode/NavigationNode.tsx index 0a8c71a9..9756408a 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/NavigationNode/NavigationNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/NavigationNode/NavigationNode.tsx @@ -21,16 +21,11 @@ import { Switch } from "@/components/ui/switch"; import type { NavigationNode } from "./types"; import { RobotIcon } from "@/components/icons/RobotIcon"; import { helpTooltips, placeholders } from "../../helpContent"; -import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect"; import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea"; import { WorkflowBlockInput } from "@/components/WorkflowBlockInput"; function NavigationNode({ id, data }: NodeProps) { const { updateNodeData } = useReactFlow(); - const [parametersPanelField, setParametersPanelField] = useState< - string | null - >(null); - const { editable } = data; const [label, setLabel] = useNodeLabelChangeHandler({ id, @@ -103,9 +98,9 @@ function NavigationNode({ id, data }: NodeProps) { setParametersPanelField("url")} - onChange={(event) => { - handleChange("url", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("url", value); }} value={inputs.url} placeholder={placeholders["navigation"]["url"]} @@ -120,9 +115,9 @@ function NavigationNode({ id, data }: NodeProps) { /> setParametersPanelField("navigationGoal")} - onChange={(event) => { - handleChange("navigationGoal", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("navigationGoal", value); }} value={inputs.navigationGoal} placeholder={placeholders["navigation"]["navigationGoal"]} @@ -295,15 +290,13 @@ function NavigationNode({ id, data }: NodeProps) { /> { - setParametersPanelField("downloadSuffix"); - }} + nodeId={id} type="text" placeholder={placeholders["navigation"]["downloadSuffix"]} className="nopan w-52 text-xs" value={inputs.downloadSuffix ?? ""} - onChange={(event) => { - handleChange("downloadSuffix", event.target.value); + onChange={(value) => { + handleChange("downloadSuffix", value); }} /> @@ -320,11 +313,9 @@ function NavigationNode({ id, data }: NodeProps) { /> { - setParametersPanelField("totpVerificationUrl"); - }} - onChange={(event) => { - handleChange("totpVerificationUrl", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("totpVerificationUrl", value); }} value={inputs.totpVerificationUrl ?? ""} placeholder={ @@ -343,11 +334,9 @@ function NavigationNode({ id, data }: NodeProps) { /> { - setParametersPanelField("totpIdentifier"); - }} - onChange={(event) => { - handleChange("totpIdentifier", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("totpIdentifier", value); }} value={inputs.totpIdentifier ?? ""} placeholder={placeholders["navigation"]["totpIdentifier"]} @@ -359,25 +348,6 @@ function NavigationNode({ id, data }: NodeProps) { - {typeof parametersPanelField === "string" && ( - setParametersPanelField(null)} - onAdd={(parameterKey) => { - if (parametersPanelField === null || !editable) { - return; - } - if (parametersPanelField in inputs) { - const currentValue = - inputs[parametersPanelField as keyof typeof inputs]; - handleChange( - parametersPanelField, - `${currentValue ?? ""}{{ ${parameterKey} }}`, - ); - } - }} - /> - )} ); } 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 1989fd0c..81f20ced 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx @@ -29,16 +29,12 @@ import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import { dataSchemaExampleValue, errorMappingExampleValue } from "../types"; -import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect"; import { ParametersMultiSelect } from "./ParametersMultiSelect"; import type { TaskNode } from "./types"; import { WorkflowBlockInput } from "@/components/WorkflowBlockInput"; import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea"; function TaskNode({ id, data }: NodeProps) { - const [parametersPanelField, setParametersPanelField] = useState< - string | null - >(null); const { updateNodeData } = useReactFlow(); const { editable } = data; const deleteNodeCallback = useDeleteNodeCallback(); @@ -123,11 +119,9 @@ function TaskNode({ id, data }: NodeProps) { { - setParametersPanelField("url"); - }} - onChange={(event) => { - handleChange("url", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("url", value); }} value={inputs.url} placeholder={placeholders["task"]["url"]} @@ -142,11 +136,9 @@ function TaskNode({ id, data }: NodeProps) { /> { - setParametersPanelField("navigationGoal"); - }} - onChange={(event) => { - handleChange("navigationGoal", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("navigationGoal", value); }} value={inputs.navigationGoal} placeholder={placeholders["task"]["navigationGoal"]} @@ -179,11 +171,9 @@ function TaskNode({ id, data }: NodeProps) { /> { - setParametersPanelField("dataExtractionGoal"); - }} - onChange={(event) => { - handleChange("dataExtractionGoal", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("dataExtractionGoal", value); }} value={inputs.dataExtractionGoal} placeholder={placeholders["task"]["dataExtractionGoal"]} @@ -380,15 +370,13 @@ function TaskNode({ id, data }: NodeProps) { { - setParametersPanelField("downloadSuffix"); - }} + nodeId={id} type="text" placeholder={placeholders["task"]["downloadSuffix"]} className="nopan w-52 text-xs" value={inputs.downloadSuffix ?? ""} - onChange={(event) => { - handleChange("downloadSuffix", event.target.value); + onChange={(value) => { + handleChange("downloadSuffix", value); }} /> @@ -403,11 +391,9 @@ function TaskNode({ id, data }: NodeProps) { /> { - setParametersPanelField("totpVerificationUrl"); - }} - onChange={(event) => { - handleChange("totpVerificationUrl", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("totpVerificationUrl", value); }} value={inputs.totpVerificationUrl ?? ""} placeholder={placeholders["task"]["totpVerificationUrl"]} @@ -424,11 +410,9 @@ function TaskNode({ id, data }: NodeProps) { /> { - setParametersPanelField("totpIdentifier"); - }} - onChange={(event) => { - handleChange("totpIdentifier", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("totpIdentifier", value); }} value={inputs.totpIdentifier ?? ""} placeholder={placeholders["task"]["totpIdentifier"]} @@ -440,25 +424,6 @@ function TaskNode({ id, data }: NodeProps) { - {typeof parametersPanelField === "string" && ( - setParametersPanelField(null)} - onAdd={(parameterKey) => { - if (parametersPanelField === null || !editable) { - return; - } - if (parametersPanelField in inputs) { - const currentValue = - inputs[parametersPanelField as keyof typeof inputs]; - handleChange( - parametersPanelField, - `${currentValue ?? ""}{{ ${parameterKey} }}`, - ); - } - }} - /> - )} ); } diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/ValidationNode/ValidationNode.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/ValidationNode/ValidationNode.tsx index a9000b5b..736569ef 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/ValidationNode/ValidationNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/ValidationNode/ValidationNode.tsx @@ -20,13 +20,9 @@ import { } from "@/components/ui/accordion"; import { Separator } from "@/components/ui/separator"; import { helpTooltips } from "../../helpContent"; -import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect"; import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea"; function ValidationNode({ id, data }: NodeProps) { - const [parametersPanelField, setParametersPanelField] = useState< - string | null - >(null); const { updateNodeData } = useReactFlow(); const { editable } = data; const [label, setLabel] = useNodeLabelChangeHandler({ @@ -88,14 +84,9 @@ function ValidationNode({ id, data }: NodeProps) {
{ - setParametersPanelField("completeCriterion"); - }} - onChange={(event) => { - if (!editable) { - return; - } - handleChange("completeCriterion", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("completeCriterion", value); }} value={inputs.completeCriterion} className="nopan text-xs" @@ -104,14 +95,9 @@ function ValidationNode({ id, data }: NodeProps) {
{ - setParametersPanelField("terminateCriterion"); - }} - onChange={(event) => { - if (!editable) { - return; - } - handleChange("terminateCriterion", event.target.value); + nodeId={id} + onChange={(value) => { + handleChange("terminateCriterion", value); }} value={inputs.terminateCriterion} className="nopan text-xs" @@ -195,25 +181,6 @@ function ValidationNode({ id, data }: NodeProps) {
- {typeof parametersPanelField === "string" && ( - setParametersPanelField(null)} - onAdd={(parameterKey) => { - if (parametersPanelField === null || !editable) { - return; - } - if (parametersPanelField in inputs) { - const currentValue = - inputs[parametersPanelField as keyof typeof inputs]; - handleChange( - parametersPanelField, - `${currentValue ?? ""}{{ ${parameterKey} }}`, - ); - } - }} - /> - )}
); } diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/WorkflowBlockParameterSelect.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/WorkflowBlockParameterSelect.tsx index 57572e40..9aabe56f 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/WorkflowBlockParameterSelect.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/WorkflowBlockParameterSelect.tsx @@ -2,7 +2,7 @@ import { useEdges, useNodes } from "@xyflow/react"; import { useWorkflowParametersState } from "../useWorkflowParametersState"; import { AppNode } from "."; import { getAvailableOutputParameterKeys } from "../workflowEditorUtils"; -import { Cross2Icon, PlusIcon } from "@radix-ui/react-icons"; +import { PlusIcon } from "@radix-ui/react-icons"; import { SwitchBar } from "@/components/SwitchBar"; import { useState } from "react"; import { ScrollArea } from "@/components/ui/scroll-area"; @@ -10,11 +10,10 @@ import { ScrollAreaViewport } from "@radix-ui/react-scroll-area"; type Props = { nodeId: string; - onClose: () => void; onAdd: (parameterKey: string) => void; }; -function WorkflowBlockParameterSelect({ nodeId, onClose, onAdd }: Props) { +function WorkflowBlockParameterSelect({ nodeId, onAdd }: Props) { const [content, setContent] = useState("parameters"); const [workflowParameters] = useWorkflowParametersState(); const nodes = useNodes(); @@ -29,10 +28,9 @@ function WorkflowBlockParameterSelect({ nodeId, onClose, onAdd }: Props) { ); return ( -
+

Add Parameter

-
setContent(value)} @@ -79,7 +77,7 @@ function WorkflowBlockParameterSelect({ nodeId, onClose, onAdd }: Props) { key={parameterKey} className="flex cursor-pointer justify-between rounded-md bg-slate-elevation1 px-3 py-2 text-xs hover:bg-slate-elevation2" onClick={() => { - onAdd(parameterKey); + onAdd?.(parameterKey); }} > {parameterKey}