diff --git a/skyvern-frontend/src/routes/workflows/components/SourceParameterKeySelector.tsx b/skyvern-frontend/src/routes/workflows/components/SourceParameterKeySelector.tsx new file mode 100644 index 00000000..d035a5f9 --- /dev/null +++ b/skyvern-frontend/src/routes/workflows/components/SourceParameterKeySelector.tsx @@ -0,0 +1,46 @@ +import { useNodes } from "@xyflow/react"; +import { useWorkflowParametersState } from "../editor/useWorkflowParametersState"; +import { AppNode } from "../editor/nodes"; +import { getOutputParameterKey } from "../editor/workflowEditorUtils"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +type Props = { + value?: string; + onChange: (value: string) => void; +}; + +function SourceParameterKeySelector({ value, onChange }: Props) { + const [workflowParameters] = useWorkflowParametersState(); + const nodes = useNodes(); + const contextParameterKeys = workflowParameters + .filter((parameter) => parameter.parameterType !== "credential") + .map((parameter) => parameter.key); + const outputParameterKeys = nodes + .filter((node) => node.type !== "nodeAdder") + .map((node) => getOutputParameterKey(node.data.label)); + + return ( + + ); +} + +export { SourceParameterKeySelector }; diff --git a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx index 7446109c..b05cbaa8 100644 --- a/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/FlowRenderer.tsx @@ -17,13 +17,13 @@ import { useEffect, useState } from "react"; import { AWSSecretParameter, BitwardenSensitiveInformationParameter, - ContextParameter, WorkflowApiResponse, WorkflowParameterValueType, } from "../types/workflowTypes"; import { BitwardenLoginCredentialParameterYAML, BlockYAML, + ContextParameterYAML, ParameterYAML, WorkflowCreateYAMLRequest, WorkflowParameterYAML, @@ -65,7 +65,11 @@ import { ReloadIcon } from "@radix-ui/react-icons"; function convertToParametersYAML( parameters: ParametersState, -): Array { +): Array< + | WorkflowParameterYAML + | BitwardenLoginCredentialParameterYAML + | ContextParameterYAML +> { return parameters.map((parameter) => { if (parameter.parameterType === "workflow") { return { @@ -77,6 +81,13 @@ function convertToParametersYAML( ? {} : { default_value: parameter.defaultValue }), }; + } else if (parameter.parameterType === "context") { + return { + parameter_type: "context", + key: parameter.key, + description: parameter.description || null, + source_parameter_key: parameter.sourceParameterKey, + }; } else { return { parameter_type: "bitwarden_login_credential", @@ -99,7 +110,7 @@ export type ParametersState = Array< key: string; parameterType: "workflow"; dataType: WorkflowParameterValueType; - description?: string; + description?: string | null; defaultValue: unknown; } | { @@ -107,7 +118,13 @@ export type ParametersState = Array< parameterType: "credential"; collectionId: string; urlParameterKey: string; - description?: string; + description?: string | null; + } + | { + key: string; + parameterType: "context"; + sourceParameterKey: string; + description?: string | null; } >; @@ -225,15 +242,10 @@ function FlowRenderer({ (parameter) => { return ( parameter.parameter_type === "aws_secret" || - parameter.parameter_type === "bitwarden_sensitive_information" || - parameter.parameter_type === "context" + parameter.parameter_type === "bitwarden_sensitive_information" ); }, - ) as Array< - | AWSSecretParameter - | BitwardenSensitiveInformationParameter - | ContextParameter - >; + ) as Array; const echoParameters = convertEchoParameters(filteredParameters); diff --git a/skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx b/skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx index 32968fbc..710a0bdd 100644 --- a/skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/WorkflowEditor.tsx @@ -51,7 +51,8 @@ function WorkflowEditor() { .filter( (parameter) => parameter.parameter_type === "workflow" || - parameter.parameter_type === "bitwarden_login_credential", + parameter.parameter_type === "bitwarden_login_credential" || + parameter.parameter_type === "context", ) .map((parameter) => { if (parameter.parameter_type === "workflow") { @@ -60,6 +61,14 @@ function WorkflowEditor() { parameterType: "workflow", dataType: parameter.workflow_parameter_type, defaultValue: parameter.default_value, + description: parameter.description, + }; + } else if (parameter.parameter_type === "context") { + return { + key: parameter.key, + parameterType: "context", + sourceParameterKey: parameter.source.key, + description: parameter.description, }; } else { return { @@ -67,6 +76,7 @@ function WorkflowEditor() { parameterType: "credential", collectionId: parameter.bitwarden_collection_id, urlParameterKey: parameter.url_parameter_key, + description: parameter.description, }; } })} 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 4fccbaec..dd68b446 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/CodeBlockNode/CodeBlockNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/CodeBlockNode/CodeBlockNode.tsx @@ -1,29 +1,21 @@ import { Label } from "@/components/ui/label"; import { CodeEditor } from "@/routes/workflows/components/CodeEditor"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; +import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler"; import { CodeIcon } from "@radix-ui/react-icons"; -import { - Handle, - NodeProps, - Position, - useNodes, - useReactFlow, -} from "@xyflow/react"; +import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; +import { useState } from "react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { CodeBlockNode } from "./types"; -import { useState } from "react"; -import { - getUniqueLabelForExistingNode, - getUpdatedNodesAfterLabelUpdateForParameterKeys, -} from "../../workflowEditorUtils"; -import { AppNode } from ".."; function CodeBlockNode({ id, data }: NodeProps) { - const { updateNodeData, setNodes } = useReactFlow(); - const nodes = useNodes(); + const { updateNodeData } = useReactFlow(); const deleteNodeCallback = useDeleteNodeCallback(); - const [label, setLabel] = useState(data.label); + const [label, setLabel] = useNodeLabelChangeHandler({ + id, + initialValue: data.label, + }); const [inputs, setInputs] = useState({ code: data.code, }); @@ -52,22 +44,7 @@ function CodeBlockNode({ id, data }: NodeProps) { { - const existingLabels = nodes.map((n) => n.data.label); - const labelWithoutWhitespace = value.replace(/\s+/g, "_"); - const newLabel = getUniqueLabelForExistingNode( - labelWithoutWhitespace, - existingLabels, - ); - setLabel(newLabel); - setNodes( - getUpdatedNodesAfterLabelUpdateForParameterKeys( - id, - newLabel, - nodes as Array, - ), - ); - }} + onChange={setLabel} titleClassName="text-base" inputClassName="text-base" /> 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 c67feda6..5a18721e 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/DownloadNode/DownloadNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/DownloadNode/DownloadNode.tsx @@ -1,29 +1,19 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; 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, - useNodes, - useReactFlow, -} from "@xyflow/react"; +import { Handle, NodeProps, Position } from "@xyflow/react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { DownloadNode } from "./types"; -import { useState } from "react"; -import { - getUniqueLabelForExistingNode, - getUpdatedNodesAfterLabelUpdateForParameterKeys, -} from "../../workflowEditorUtils"; -import { AppNode } from ".."; function DownloadNode({ id, data }: NodeProps) { - const { setNodes } = useReactFlow(); - const nodes = useNodes(); + const [label, setLabel] = useNodeLabelChangeHandler({ + id, + initialValue: data.label, + }); const deleteNodeCallback = useDeleteNodeCallback(); - const [label, setLabel] = useState(data.label); return (
@@ -49,22 +39,7 @@ function DownloadNode({ id, data }: NodeProps) { { - const existingLabels = nodes.map((n) => n.data.label); - const labelWithoutWhitespace = value.replace(/\s+/g, "_"); - const newLabel = getUniqueLabelForExistingNode( - labelWithoutWhitespace, - existingLabels, - ); - setLabel(newLabel); - setNodes( - getUpdatedNodesAfterLabelUpdateForParameterKeys( - id, - newLabel, - nodes as Array, - ), - ); - }} + onChange={setLabel} titleClassName="text-base" inputClassName="text-base" /> 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 bc5bcf57..e292b512 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/FileParserNode/FileParserNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/FileParserNode/FileParserNode.tsx @@ -1,31 +1,23 @@ import { Input } from "@/components/ui/input"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; +import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler"; import { CursorTextIcon } from "@radix-ui/react-icons"; -import { - Handle, - NodeProps, - Position, - useNodes, - useReactFlow, -} from "@xyflow/react"; +import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; +import { useState } from "react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { FileParserNode } from "./types"; -import { useState } from "react"; -import { - getUniqueLabelForExistingNode, - getUpdatedNodesAfterLabelUpdateForParameterKeys, -} from "../../workflowEditorUtils"; -import { AppNode } from ".."; function FileParserNode({ id, data }: NodeProps) { - const { updateNodeData, setNodes } = useReactFlow(); + const { updateNodeData } = useReactFlow(); const deleteNodeCallback = useDeleteNodeCallback(); - const nodes = useNodes(); - const [label, setLabel] = useState(data.label); const [inputs, setInputs] = useState({ fileUrl: data.fileUrl, }); + const [label, setLabel] = useNodeLabelChangeHandler({ + id, + initialValue: data.label, + }); return (
@@ -51,22 +43,7 @@ function FileParserNode({ id, data }: NodeProps) { { - const existingLabels = nodes.map((n) => n.data.label); - const labelWithoutWhitespace = value.replace(/\s+/g, "_"); - const newLabel = getUniqueLabelForExistingNode( - labelWithoutWhitespace, - existingLabels, - ); - setLabel(newLabel); - setNodes( - getUpdatedNodesAfterLabelUpdateForParameterKeys( - id, - newLabel, - nodes as Array, - ), - ); - }} + onChange={setLabel} titleClassName="text-base" inputClassName="text-base" /> 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 ec67066b..47ef6cf7 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/LoopNode/LoopNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/LoopNode/LoopNode.tsx @@ -1,6 +1,7 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; +import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler"; import { UpdateIcon } from "@radix-ui/react-icons"; import type { Node } from "@xyflow/react"; import { @@ -10,21 +11,20 @@ import { useNodes, useReactFlow, } from "@xyflow/react"; +import { useState } from "react"; +import { AppNode } from ".."; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { LoopNode } from "./types"; -import { useState } from "react"; -import { - getUniqueLabelForExistingNode, - getUpdatedNodesAfterLabelUpdateForParameterKeys, -} from "../../workflowEditorUtils"; -import { AppNode } from ".."; function LoopNode({ id, data }: NodeProps) { - const { updateNodeData, setNodes } = useReactFlow(); + const { updateNodeData } = useReactFlow(); const nodes = useNodes(); + const [label, setLabel] = useNodeLabelChangeHandler({ + id, + initialValue: data.label, + }); const deleteNodeCallback = useDeleteNodeCallback(); - const [label, setLabel] = useState(data.label); const [inputs, setInputs] = useState({ loopValue: data.loopValue, }); @@ -79,22 +79,7 @@ function LoopNode({ id, data }: NodeProps) { { - const existingLabels = nodes.map((n) => n.data.label); - const labelWithoutWhitespace = value.replace(/\s+/g, "_"); - const newLabel = getUniqueLabelForExistingNode( - labelWithoutWhitespace, - existingLabels, - ); - setLabel(newLabel); - setNodes( - getUpdatedNodesAfterLabelUpdateForParameterKeys( - id, - newLabel, - nodes as Array, - ), - ); - }} + onChange={setLabel} titleClassName="text-base" inputClassName="text-base" /> 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 99255769..21f03690 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/SendEmailNode/SendEmailNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/SendEmailNode/SendEmailNode.tsx @@ -2,29 +2,21 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; +import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler"; import { EnvelopeClosedIcon } from "@radix-ui/react-icons"; -import { - Handle, - NodeProps, - Position, - useNodes, - useReactFlow, -} from "@xyflow/react"; +import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; +import { useState } from "react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { SendEmailNode } from "./types"; -import { useState } from "react"; -import { - getUniqueLabelForExistingNode, - getUpdatedNodesAfterLabelUpdateForParameterKeys, -} from "../../workflowEditorUtils"; -import { AppNode } from ".."; function SendEmailNode({ id, data }: NodeProps) { - const { updateNodeData, setNodes } = useReactFlow(); - const nodes = useNodes(); + const { updateNodeData } = useReactFlow(); + const [label, setLabel] = useNodeLabelChangeHandler({ + id, + initialValue: data.label, + }); const deleteNodeCallback = useDeleteNodeCallback(); - const [label, setLabel] = useState(data.label); const [inputs, setInputs] = useState({ recipients: data.recipients, subject: data.subject, @@ -64,22 +56,7 @@ function SendEmailNode({ id, data }: NodeProps) { { - const existingLabels = nodes.map((n) => n.data.label); - const labelWithoutWhitespace = value.replace(/\s+/g, "_"); - const newLabel = getUniqueLabelForExistingNode( - labelWithoutWhitespace, - existingLabels, - ); - setLabel(newLabel); - setNodes( - getUpdatedNodesAfterLabelUpdateForParameterKeys( - id, - newLabel, - nodes as Array, - ), - ); - }} + onChange={setLabel} titleClassName="text-base" inputClassName="text-base" /> 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 6fe75d08..fbda4c9d 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/TaskNode/TaskNode.tsx @@ -11,6 +11,7 @@ import { Label } from "@/components/ui/label"; 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 { ListBulletIcon } from "@radix-ui/react-icons"; import { Edge, @@ -23,11 +24,7 @@ import { } from "@xyflow/react"; import { useState } from "react"; import { AppNode } from ".."; -import { - getUniqueLabelForExistingNode, - getOutputParameterKey, - getUpdatedNodesAfterLabelUpdateForParameterKeys, -} from "../../workflowEditorUtils"; +import { getOutputParameterKey } from "../../workflowEditorUtils"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import { TaskNodeDisplayModeSwitch } from "./TaskNodeDisplayModeSwitch"; @@ -58,7 +55,7 @@ function getPreviousNodeIds( } function TaskNode({ id, data }: NodeProps) { - const { updateNodeData, setNodes } = useReactFlow(); + const { updateNodeData } = useReactFlow(); const [displayMode, setDisplayMode] = useState("basic"); const { editable } = data; const deleteNodeCallback = useDeleteNodeCallback(); @@ -75,7 +72,11 @@ function TaskNode({ id, data }: NodeProps) { getOutputParameterKey(label), ); - const [label, setLabel] = useState(data.label); + const [label, setLabel] = useNodeLabelChangeHandler({ + id, + initialValue: data.label, + }); + const [inputs, setInputs] = useState({ url: data.url, navigationGoal: data.navigationGoal, @@ -421,22 +422,7 @@ function TaskNode({ id, data }: NodeProps) { { - const existingLabels = nodes.map((n) => n.data.label); - const labelWithoutWhitespace = value.replace(/\s+/g, "_"); - const newLabel = getUniqueLabelForExistingNode( - labelWithoutWhitespace, - existingLabels, - ); - setLabel(newLabel); - setNodes( - getUpdatedNodesAfterLabelUpdateForParameterKeys( - id, - newLabel, - nodes as Array, - ), - ); - }} + onChange={setLabel} titleClassName="text-base" inputClassName="text-base" /> 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 9a07d606..94563113 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/TextPromptNode/TextPromptNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/TextPromptNode/TextPromptNode.tsx @@ -4,35 +4,28 @@ import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { CodeEditor } from "@/routes/workflows/components/CodeEditor"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; +import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler"; import { CursorTextIcon } from "@radix-ui/react-icons"; -import { - Handle, - NodeProps, - Position, - useNodes, - useReactFlow, -} from "@xyflow/react"; +import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react"; +import { useState } from "react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { TextPromptNode } from "./types"; -import { useState } from "react"; -import { - getUniqueLabelForExistingNode, - getUpdatedNodesAfterLabelUpdateForParameterKeys, -} from "../../workflowEditorUtils"; -import { AppNode } from ".."; function TextPromptNode({ id, data }: NodeProps) { - const { updateNodeData, setNodes } = useReactFlow(); - const nodes = useNodes(); + 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, }); + const [label, setLabel] = useNodeLabelChangeHandler({ + id, + initialValue: data.label, + }); + return (
) { { - const existingLabels = nodes.map((n) => n.data.label); - const labelWithoutWhitespace = value.replace(/\s+/g, "_"); - const newLabel = getUniqueLabelForExistingNode( - labelWithoutWhitespace, - existingLabels, - ); - setLabel(newLabel); - setNodes( - getUpdatedNodesAfterLabelUpdateForParameterKeys( - id, - newLabel, - nodes as Array, - ), - ); - }} + onChange={setLabel} titleClassName="text-base" inputClassName="text-base" /> 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 e0fd0b43..74d4fe68 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/UploadNode/UploadNode.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/UploadNode/UploadNode.tsx @@ -1,29 +1,19 @@ import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback"; +import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler"; import { UploadIcon } from "@radix-ui/react-icons"; -import { - Handle, - NodeProps, - Position, - useNodes, - useReactFlow, -} from "@xyflow/react"; +import { Handle, NodeProps, Position } from "@xyflow/react"; import { EditableNodeTitle } from "../components/EditableNodeTitle"; import { NodeActionMenu } from "../NodeActionMenu"; import type { UploadNode } from "./types"; -import { useState } from "react"; -import { - getUniqueLabelForExistingNode, - getUpdatedNodesAfterLabelUpdateForParameterKeys, -} from "../../workflowEditorUtils"; -import { AppNode } from ".."; function UploadNode({ id, data }: NodeProps) { - const { setNodes } = useReactFlow(); - const nodes = useNodes(); const deleteNodeCallback = useDeleteNodeCallback(); - const [label, setLabel] = useState(data.label); + const [label, setLabel] = useNodeLabelChangeHandler({ + id, + initialValue: data.label, + }); return (
@@ -49,22 +39,7 @@ function UploadNode({ id, data }: NodeProps) { { - const existingLabels = nodes.map((n) => n.data.label); - const labelWithoutWhitespace = value.replace(/\s+/g, "_"); - const newLabel = getUniqueLabelForExistingNode( - labelWithoutWhitespace, - existingLabels, - ); - setLabel(newLabel); - setNodes( - getUpdatedNodesAfterLabelUpdateForParameterKeys( - id, - newLabel, - nodes as Array, - ), - ); - }} + onChange={setLabel} titleClassName="text-base" inputClassName="text-base" /> diff --git a/skyvern-frontend/src/routes/workflows/editor/nodes/components/HorizontallyResizingInput.tsx b/skyvern-frontend/src/routes/workflows/editor/nodes/components/HorizontallyResizingInput.tsx index 4acb7957..33ed80be 100644 --- a/skyvern-frontend/src/routes/workflows/editor/nodes/components/HorizontallyResizingInput.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/nodes/components/HorizontallyResizingInput.tsx @@ -1,7 +1,7 @@ import { Input } from "@/components/ui/input"; import { useLayoutEffect, useRef } from "react"; -type Props = React.ComponentProps; +type Props = React.ComponentPropsWithoutRef; function HorizontallyResizingInput(props: Props) { const ref = useRef(null); diff --git a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx index 34798ecc..c7c2474f 100644 --- a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterAddPanel.tsx @@ -17,9 +17,10 @@ import { WorkflowParameterInput } from "../../WorkflowParameterInput"; import { Checkbox } from "@/components/ui/checkbox"; import { getDefaultValueForParameterType } from "../workflowEditorUtils"; import { toast } from "@/components/ui/use-toast"; +import { SourceParameterKeySelector } from "../../components/SourceParameterKeySelector"; type Props = { - type: "workflow" | "credential"; + type: "workflow" | "credential" | "context"; onClose: () => void; onSave: (value: ParametersState[number]) => void; }; @@ -32,6 +33,16 @@ const workflowParameterTypeOptions = [ { label: "JSON", value: WorkflowParameterValueType.JSON }, ]; +function header(type: "workflow" | "credential" | "context") { + if (type === "workflow") { + return "Add Workflow Parameter"; + } + if (type === "credential") { + return "Add Credential Parameter"; + } + return "Add Context Parameter"; +} + function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) { const [key, setKey] = useState(""); const [urlParameterKey, setUrlParameterKey] = useState(""); @@ -46,13 +57,14 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) { hasDefaultValue: false, defaultValue: null, }); + const [sourceParameterKey, setSourceParameterKey] = useState< + string | undefined + >(undefined); return (
- - Add {type === "workflow" ? "Workflow" : "Credential"} Parameter - + {header(type)}
@@ -170,6 +182,15 @@ function WorkflowParameterAddPanel({ type, onClose, onSave }: Props) {
)} + {type === "context" && ( +
+ + +
+ )}