Use popover in add parameter experience (#1377)
This commit is contained in:
@@ -1,19 +1,42 @@
|
|||||||
import { PlusIcon } from "@radix-ui/react-icons";
|
import { PlusIcon } from "@radix-ui/react-icons";
|
||||||
import { cn } from "@/util/utils";
|
import { cn } from "@/util/utils";
|
||||||
import { Input } from "./ui/input";
|
import { Input } from "./ui/input";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
||||||
|
import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect";
|
||||||
|
|
||||||
type Props = React.ComponentProps<typeof Input> & {
|
type Props = Omit<React.ComponentProps<typeof Input>, "onChange"> & {
|
||||||
onIconClick: () => void;
|
onChange: (value: string) => void;
|
||||||
|
nodeId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function WorkflowBlockInput(props: Props) {
|
function WorkflowBlockInput(props: Props) {
|
||||||
|
const { nodeId, onChange, ...inputProps } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Input {...props} className={cn("pr-9", props.className)} />
|
<Input
|
||||||
|
{...inputProps}
|
||||||
|
className={cn("pr-9", props.className)}
|
||||||
|
onChange={(event) => {
|
||||||
|
onChange(event.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
<div className="absolute right-0 top-0 flex size-9 cursor-pointer items-center justify-center">
|
<div className="absolute right-0 top-0 flex size-9 cursor-pointer items-center justify-center">
|
||||||
<div className="rounded p-1 hover:bg-muted" onClick={props.onIconClick}>
|
<Popover>
|
||||||
<PlusIcon className="size-4" />
|
<PopoverTrigger asChild>
|
||||||
</div>
|
<div className="rounded p-1 hover:bg-muted">
|
||||||
|
<PlusIcon className="size-4" />
|
||||||
|
</div>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<WorkflowBlockParameterSelect
|
||||||
|
nodeId={nodeId}
|
||||||
|
onAdd={(parameterKey) => {
|
||||||
|
onChange(`${props.value ?? ""}{{${parameterKey}}}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,22 +1,45 @@
|
|||||||
import { PlusIcon } from "@radix-ui/react-icons";
|
import { PlusIcon } from "@radix-ui/react-icons";
|
||||||
import { cn } from "@/util/utils";
|
import { cn } from "@/util/utils";
|
||||||
import { AutoResizingTextarea } from "./AutoResizingTextarea/AutoResizingTextarea";
|
import { AutoResizingTextarea } from "./AutoResizingTextarea/AutoResizingTextarea";
|
||||||
|
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
||||||
|
import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect";
|
||||||
|
|
||||||
type Props = React.ComponentProps<typeof AutoResizingTextarea> & {
|
type Props = Omit<
|
||||||
onIconClick: () => void;
|
React.ComponentProps<typeof AutoResizingTextarea>,
|
||||||
|
"onChange"
|
||||||
|
> & {
|
||||||
|
onChange: (value: string) => void;
|
||||||
|
nodeId: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
function WorkflowBlockInputTextarea(props: Props) {
|
function WorkflowBlockInputTextarea(props: Props) {
|
||||||
|
const { nodeId, onChange, ...textAreaProps } = props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<AutoResizingTextarea
|
<AutoResizingTextarea
|
||||||
{...props}
|
{...textAreaProps}
|
||||||
|
onChange={(event) => {
|
||||||
|
onChange(event.target.value);
|
||||||
|
}}
|
||||||
className={cn("pr-9", props.className)}
|
className={cn("pr-9", props.className)}
|
||||||
/>
|
/>
|
||||||
<div className="absolute right-0 top-0 flex size-9 cursor-pointer items-center justify-center">
|
<div className="absolute right-0 top-0 flex size-9 cursor-pointer items-center justify-center">
|
||||||
<div className="rounded p-1 hover:bg-muted" onClick={props.onIconClick}>
|
<Popover>
|
||||||
<PlusIcon className="size-4" />
|
<PopoverTrigger asChild>
|
||||||
</div>
|
<div className="rounded p-1 hover:bg-muted">
|
||||||
|
<PlusIcon className="size-4" />
|
||||||
|
</div>
|
||||||
|
</PopoverTrigger>
|
||||||
|
<PopoverContent>
|
||||||
|
<WorkflowBlockParameterSelect
|
||||||
|
nodeId={nodeId}
|
||||||
|
onAdd={(parameterKey) => {
|
||||||
|
onChange(`${props.value ?? ""}{{${parameterKey}}}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</PopoverContent>
|
||||||
|
</Popover>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ import { Switch } from "@/components/ui/switch";
|
|||||||
import { ClickIcon } from "@/components/icons/ClickIcon";
|
import { ClickIcon } from "@/components/icons/ClickIcon";
|
||||||
import { placeholders, helpTooltips } from "../../helpContent";
|
import { placeholders, helpTooltips } from "../../helpContent";
|
||||||
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
||||||
import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect";
|
import { WorkflowBlockInput } from "@/components/WorkflowBlockInput";
|
||||||
|
|
||||||
const urlTooltip =
|
const urlTooltip =
|
||||||
"The URL Skyvern is navigating to. Leave this field blank to pick up from where the last block left off.";
|
"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.';
|
const navigationGoalPlaceholder = 'Input {{ name }} into "Name" field.';
|
||||||
|
|
||||||
function ActionNode({ id, data }: NodeProps<ActionNode>) {
|
function ActionNode({ id, data }: NodeProps<ActionNode>) {
|
||||||
const [parametersPanelField, setParametersPanelField] = useState<
|
|
||||||
string | null
|
|
||||||
>(null);
|
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
const { editable } = data;
|
const { editable } = data;
|
||||||
const [label, setLabel] = useNodeLabelChangeHandler({
|
const [label, setLabel] = useNodeLabelChangeHandler({
|
||||||
@@ -107,14 +104,9 @@ function ActionNode({ id, data }: NodeProps<ActionNode>) {
|
|||||||
<HelpTooltip content={urlTooltip} />
|
<HelpTooltip content={urlTooltip} />
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("url");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("url", value);
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("url", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.url}
|
value={inputs.url}
|
||||||
placeholder={placeholders["action"]["url"]}
|
placeholder={placeholders["action"]["url"]}
|
||||||
@@ -129,14 +121,9 @@ function ActionNode({ id, data }: NodeProps<ActionNode>) {
|
|||||||
<HelpTooltip content={navigationGoalTooltip} />
|
<HelpTooltip content={navigationGoalTooltip} />
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("navigationGoal");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("navigationGoal", value);
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("navigationGoal", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.navigationGoal}
|
value={inputs.navigationGoal}
|
||||||
placeholder={navigationGoalPlaceholder}
|
placeholder={navigationGoalPlaceholder}
|
||||||
@@ -302,16 +289,14 @@ function ActionNode({ id, data }: NodeProps<ActionNode>) {
|
|||||||
content={helpTooltips["action"]["fileSuffix"]}
|
content={helpTooltips["action"]["fileSuffix"]}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Input
|
<WorkflowBlockInput
|
||||||
|
nodeId={id}
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={placeholders["action"]["downloadSuffix"]}
|
placeholder={placeholders["action"]["downloadSuffix"]}
|
||||||
className="nopan w-52 text-xs"
|
className="nopan w-52 text-xs"
|
||||||
value={inputs.downloadSuffix ?? ""}
|
value={inputs.downloadSuffix ?? ""}
|
||||||
onChange={(event) => {
|
onChange={(value) => {
|
||||||
if (!editable) {
|
handleChange("downloadSuffix", value);
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("downloadSuffix", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -326,11 +311,9 @@ function ActionNode({ id, data }: NodeProps<ActionNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("totpVerificationUrl");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("totpVerificationUrl", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("totpVerificationUrl", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.totpVerificationUrl ?? ""}
|
value={inputs.totpVerificationUrl ?? ""}
|
||||||
placeholder={placeholders["action"]["totpVerificationUrl"]}
|
placeholder={placeholders["action"]["totpVerificationUrl"]}
|
||||||
@@ -347,14 +330,9 @@ function ActionNode({ id, data }: NodeProps<ActionNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("totpIdentifier");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("totpIdentifier", value);
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("totpIdentifier", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.totpIdentifier ?? ""}
|
value={inputs.totpIdentifier ?? ""}
|
||||||
placeholder={placeholders["action"]["totpIdentifier"]}
|
placeholder={placeholders["action"]["totpIdentifier"]}
|
||||||
@@ -366,25 +344,6 @@ function ActionNode({ id, data }: NodeProps<ActionNode>) {
|
|||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
{typeof parametersPanelField === "string" && (
|
|
||||||
<WorkflowBlockParameterSelect
|
|
||||||
nodeId={id}
|
|
||||||
onClose={() => 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} }}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,30 @@
|
|||||||
|
import { HelpTooltip } from "@/components/HelpTooltip";
|
||||||
|
import { ExtractIcon } from "@/components/icons/ExtractIcon";
|
||||||
import {
|
import {
|
||||||
Accordion,
|
Accordion,
|
||||||
AccordionContent,
|
AccordionContent,
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
} from "@/components/ui/accordion";
|
} from "@/components/ui/accordion";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Separator } from "@/components/ui/separator";
|
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 { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback";
|
||||||
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
|
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
|
||||||
import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react";
|
import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
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 { dataSchemaExampleValue } from "../types";
|
||||||
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
|
|
||||||
import { Switch } from "@/components/ui/switch";
|
|
||||||
import type { ExtractionNode } from "./types";
|
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 { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
||||||
|
import { helpTooltips, placeholders } from "../../helpContent";
|
||||||
|
|
||||||
function ExtractionNode({ id, data }: NodeProps<ExtractionNode>) {
|
function ExtractionNode({ id, data }: NodeProps<ExtractionNode>) {
|
||||||
const [parametersPanelField, setParametersPanelField] = useState<
|
|
||||||
string | null
|
|
||||||
>(null);
|
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
const { editable } = data;
|
const { editable } = data;
|
||||||
const [label, setLabel] = useNodeLabelChangeHandler({
|
const [label, setLabel] = useNodeLabelChangeHandler({
|
||||||
@@ -101,14 +97,12 @@ function ExtractionNode({ id, data }: NodeProps<ExtractionNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("dataExtractionGoal");
|
onChange={(value) => {
|
||||||
}}
|
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
if (!editable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handleChange("dataExtractionGoal", event.target.value);
|
handleChange("dataExtractionGoal", value);
|
||||||
}}
|
}}
|
||||||
value={inputs.dataExtractionGoal}
|
value={inputs.dataExtractionGoal}
|
||||||
placeholder={placeholders["extraction"]["dataExtractionGoal"]}
|
placeholder={placeholders["extraction"]["dataExtractionGoal"]}
|
||||||
@@ -263,25 +257,6 @@ function ExtractionNode({ id, data }: NodeProps<ExtractionNode>) {
|
|||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
{typeof parametersPanelField === "string" && (
|
|
||||||
<WorkflowBlockParameterSelect
|
|
||||||
nodeId={id}
|
|
||||||
onClose={() => 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} }}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,19 +10,18 @@ import { Input } from "@/components/ui/input";
|
|||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { Switch } from "@/components/ui/switch";
|
import { Switch } from "@/components/ui/switch";
|
||||||
|
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
||||||
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
|
import { CodeEditor } from "@/routes/workflows/components/CodeEditor";
|
||||||
import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback";
|
import { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback";
|
||||||
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
|
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
|
||||||
import { DownloadIcon } from "@radix-ui/react-icons";
|
import { DownloadIcon } from "@radix-ui/react-icons";
|
||||||
import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react";
|
import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { helpTooltips, placeholders } from "../../helpContent";
|
||||||
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import { errorMappingExampleValue } from "../types";
|
import { errorMappingExampleValue } from "../types";
|
||||||
import type { FileDownloadNode } from "./types";
|
import type { FileDownloadNode } from "./types";
|
||||||
import { helpTooltips, placeholders } from "../../helpContent";
|
|
||||||
import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect";
|
|
||||||
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
|
||||||
|
|
||||||
const urlTooltip =
|
const urlTooltip =
|
||||||
"The URL Skyvern is navigating to. Leave this field blank to pick up from where the last block left off.";
|
"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.";
|
const navigationGoalPlaceholder = "Tell Skyvern which file to download.";
|
||||||
|
|
||||||
function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
||||||
const [parametersPanelField, setParametersPanelField] = useState<
|
|
||||||
string | null
|
|
||||||
>(null);
|
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
const { editable } = data;
|
const { editable } = data;
|
||||||
const [label, setLabel] = useNodeLabelChangeHandler({
|
const [label, setLabel] = useNodeLabelChangeHandler({
|
||||||
@@ -109,11 +105,9 @@ function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
|||||||
<HelpTooltip content={urlTooltip} />
|
<HelpTooltip content={urlTooltip} />
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("url");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("url", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("url", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.url}
|
value={inputs.url}
|
||||||
placeholder={urlPlaceholder}
|
placeholder={urlPlaceholder}
|
||||||
@@ -126,11 +120,9 @@ function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
|||||||
<HelpTooltip content={navigationGoalTooltip} />
|
<HelpTooltip content={navigationGoalTooltip} />
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("navigationGoal");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("navigationGoal", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("navigationGoal", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.navigationGoal}
|
value={inputs.navigationGoal}
|
||||||
placeholder={navigationGoalPlaceholder}
|
placeholder={navigationGoalPlaceholder}
|
||||||
@@ -302,11 +294,9 @@ function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("totpVerificationUrl");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("totpVerificationUrl", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("totpVerificationUrl", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.totpVerificationUrl ?? ""}
|
value={inputs.totpVerificationUrl ?? ""}
|
||||||
placeholder={
|
placeholder={
|
||||||
@@ -325,11 +315,9 @@ function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("totpIdentifier");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("totpIdentifier", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("totpIdentifier", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.totpIdentifier ?? ""}
|
value={inputs.totpIdentifier ?? ""}
|
||||||
placeholder={placeholders["download"]["totpIdentifier"]}
|
placeholder={placeholders["download"]["totpIdentifier"]}
|
||||||
@@ -341,25 +329,6 @@ function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
|||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
{typeof parametersPanelField === "string" && (
|
|
||||||
<WorkflowBlockParameterSelect
|
|
||||||
nodeId={id}
|
|
||||||
onClose={() => 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} }}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,30 @@
|
|||||||
|
import { HelpTooltip } from "@/components/HelpTooltip";
|
||||||
import {
|
import {
|
||||||
Accordion,
|
Accordion,
|
||||||
AccordionContent,
|
AccordionContent,
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
} from "@/components/ui/accordion";
|
} from "@/components/ui/accordion";
|
||||||
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Separator } from "@/components/ui/separator";
|
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 { useDeleteNodeCallback } from "@/routes/workflows/hooks/useDeleteNodeCallback";
|
||||||
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
|
import { useNodeLabelChangeHandler } from "@/routes/workflows/hooks/useLabelChangeHandler";
|
||||||
|
import { LockOpen1Icon } from "@radix-ui/react-icons";
|
||||||
import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react";
|
import { Handle, NodeProps, Position, useReactFlow } from "@xyflow/react";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { helpTooltips, placeholders } from "../../helpContent";
|
||||||
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
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 { 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 { CredentialParameterSelector } from "./CredentialParameterSelector";
|
||||||
import { helpTooltips, placeholders } from "../../helpContent";
|
import type { LoginNode } from "./types";
|
||||||
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
|
||||||
import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect";
|
|
||||||
|
|
||||||
function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
||||||
const [parametersPanelField, setParametersPanelField] = useState<
|
|
||||||
string | null
|
|
||||||
>(null);
|
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
const { editable } = data;
|
const { editable } = data;
|
||||||
const [label, setLabel] = useNodeLabelChangeHandler({
|
const [label, setLabel] = useNodeLabelChangeHandler({
|
||||||
@@ -100,11 +96,9 @@ function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
|||||||
<HelpTooltip content={helpTooltips["login"]["url"]} />
|
<HelpTooltip content={helpTooltips["login"]["url"]} />
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("url");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("url", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("url", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.url}
|
value={inputs.url}
|
||||||
placeholder={placeholders["login"]["url"]}
|
placeholder={placeholders["login"]["url"]}
|
||||||
@@ -117,11 +111,9 @@ function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
|||||||
<HelpTooltip content={helpTooltips["login"]["navigationGoal"]} />
|
<HelpTooltip content={helpTooltips["login"]["navigationGoal"]} />
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("navigationGoal");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("navigationGoal", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("navigationGoal", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.navigationGoal}
|
value={inputs.navigationGoal}
|
||||||
placeholder={placeholders["login"]["navigationGoal"]}
|
placeholder={placeholders["login"]["navigationGoal"]}
|
||||||
@@ -296,11 +288,9 @@ function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("totpVerificationUrl");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("totpVerificationUrl", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("totpVerificationUrl", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.totpVerificationUrl ?? ""}
|
value={inputs.totpVerificationUrl ?? ""}
|
||||||
placeholder={placeholders["login"]["totpVerificationUrl"]}
|
placeholder={placeholders["login"]["totpVerificationUrl"]}
|
||||||
@@ -317,11 +307,9 @@ function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("totpIdentifier");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("totpIdentifier", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("totpIdentifier", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.totpIdentifier ?? ""}
|
value={inputs.totpIdentifier ?? ""}
|
||||||
placeholder={placeholders["login"]["totpIdentifier"]}
|
placeholder={placeholders["login"]["totpIdentifier"]}
|
||||||
@@ -333,25 +321,6 @@ function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
|||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
{typeof parametersPanelField === "string" && (
|
|
||||||
<WorkflowBlockParameterSelect
|
|
||||||
nodeId={id}
|
|
||||||
onClose={() => 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} }}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,16 +21,11 @@ import { Switch } from "@/components/ui/switch";
|
|||||||
import type { NavigationNode } from "./types";
|
import type { NavigationNode } from "./types";
|
||||||
import { RobotIcon } from "@/components/icons/RobotIcon";
|
import { RobotIcon } from "@/components/icons/RobotIcon";
|
||||||
import { helpTooltips, placeholders } from "../../helpContent";
|
import { helpTooltips, placeholders } from "../../helpContent";
|
||||||
import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect";
|
|
||||||
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
||||||
import { WorkflowBlockInput } from "@/components/WorkflowBlockInput";
|
import { WorkflowBlockInput } from "@/components/WorkflowBlockInput";
|
||||||
|
|
||||||
function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
const [parametersPanelField, setParametersPanelField] = useState<
|
|
||||||
string | null
|
|
||||||
>(null);
|
|
||||||
|
|
||||||
const { editable } = data;
|
const { editable } = data;
|
||||||
const [label, setLabel] = useNodeLabelChangeHandler({
|
const [label, setLabel] = useNodeLabelChangeHandler({
|
||||||
id,
|
id,
|
||||||
@@ -103,9 +98,9 @@ function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
|||||||
<HelpTooltip content={helpTooltips["navigation"]["url"]} />
|
<HelpTooltip content={helpTooltips["navigation"]["url"]} />
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => setParametersPanelField("url")}
|
nodeId={id}
|
||||||
onChange={(event) => {
|
onChange={(value) => {
|
||||||
handleChange("url", event.target.value);
|
handleChange("url", value);
|
||||||
}}
|
}}
|
||||||
value={inputs.url}
|
value={inputs.url}
|
||||||
placeholder={placeholders["navigation"]["url"]}
|
placeholder={placeholders["navigation"]["url"]}
|
||||||
@@ -120,9 +115,9 @@ function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => setParametersPanelField("navigationGoal")}
|
nodeId={id}
|
||||||
onChange={(event) => {
|
onChange={(value) => {
|
||||||
handleChange("navigationGoal", event.target.value);
|
handleChange("navigationGoal", value);
|
||||||
}}
|
}}
|
||||||
value={inputs.navigationGoal}
|
value={inputs.navigationGoal}
|
||||||
placeholder={placeholders["navigation"]["navigationGoal"]}
|
placeholder={placeholders["navigation"]["navigationGoal"]}
|
||||||
@@ -295,15 +290,13 @@ function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInput
|
<WorkflowBlockInput
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("downloadSuffix");
|
|
||||||
}}
|
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={placeholders["navigation"]["downloadSuffix"]}
|
placeholder={placeholders["navigation"]["downloadSuffix"]}
|
||||||
className="nopan w-52 text-xs"
|
className="nopan w-52 text-xs"
|
||||||
value={inputs.downloadSuffix ?? ""}
|
value={inputs.downloadSuffix ?? ""}
|
||||||
onChange={(event) => {
|
onChange={(value) => {
|
||||||
handleChange("downloadSuffix", event.target.value);
|
handleChange("downloadSuffix", value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -320,11 +313,9 @@ function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("totpVerificationUrl");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("totpVerificationUrl", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("totpVerificationUrl", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.totpVerificationUrl ?? ""}
|
value={inputs.totpVerificationUrl ?? ""}
|
||||||
placeholder={
|
placeholder={
|
||||||
@@ -343,11 +334,9 @@ function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("totpIdentifier");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("totpIdentifier", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("totpIdentifier", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.totpIdentifier ?? ""}
|
value={inputs.totpIdentifier ?? ""}
|
||||||
placeholder={placeholders["navigation"]["totpIdentifier"]}
|
placeholder={placeholders["navigation"]["totpIdentifier"]}
|
||||||
@@ -359,25 +348,6 @@ function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
|||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
{typeof parametersPanelField === "string" && (
|
|
||||||
<WorkflowBlockParameterSelect
|
|
||||||
nodeId={id}
|
|
||||||
onClose={() => 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} }}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,16 +29,12 @@ import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
|||||||
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import { dataSchemaExampleValue, errorMappingExampleValue } from "../types";
|
import { dataSchemaExampleValue, errorMappingExampleValue } from "../types";
|
||||||
import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect";
|
|
||||||
import { ParametersMultiSelect } from "./ParametersMultiSelect";
|
import { ParametersMultiSelect } from "./ParametersMultiSelect";
|
||||||
import type { TaskNode } from "./types";
|
import type { TaskNode } from "./types";
|
||||||
import { WorkflowBlockInput } from "@/components/WorkflowBlockInput";
|
import { WorkflowBlockInput } from "@/components/WorkflowBlockInput";
|
||||||
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
||||||
|
|
||||||
function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
||||||
const [parametersPanelField, setParametersPanelField] = useState<
|
|
||||||
string | null
|
|
||||||
>(null);
|
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
const { editable } = data;
|
const { editable } = data;
|
||||||
const deleteNodeCallback = useDeleteNodeCallback();
|
const deleteNodeCallback = useDeleteNodeCallback();
|
||||||
@@ -123,11 +119,9 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
<HelpTooltip content={helpTooltips["task"]["url"]} />
|
<HelpTooltip content={helpTooltips["task"]["url"]} />
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("url");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("url", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("url", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.url}
|
value={inputs.url}
|
||||||
placeholder={placeholders["task"]["url"]}
|
placeholder={placeholders["task"]["url"]}
|
||||||
@@ -142,11 +136,9 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("navigationGoal");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("navigationGoal", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("navigationGoal", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.navigationGoal}
|
value={inputs.navigationGoal}
|
||||||
placeholder={placeholders["task"]["navigationGoal"]}
|
placeholder={placeholders["task"]["navigationGoal"]}
|
||||||
@@ -179,11 +171,9 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("dataExtractionGoal");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("dataExtractionGoal", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("dataExtractionGoal", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.dataExtractionGoal}
|
value={inputs.dataExtractionGoal}
|
||||||
placeholder={placeholders["task"]["dataExtractionGoal"]}
|
placeholder={placeholders["task"]["dataExtractionGoal"]}
|
||||||
@@ -380,15 +370,13 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
<HelpTooltip content={helpTooltips["task"]["fileSuffix"]} />
|
<HelpTooltip content={helpTooltips["task"]["fileSuffix"]} />
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInput
|
<WorkflowBlockInput
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("downloadSuffix");
|
|
||||||
}}
|
|
||||||
type="text"
|
type="text"
|
||||||
placeholder={placeholders["task"]["downloadSuffix"]}
|
placeholder={placeholders["task"]["downloadSuffix"]}
|
||||||
className="nopan w-52 text-xs"
|
className="nopan w-52 text-xs"
|
||||||
value={inputs.downloadSuffix ?? ""}
|
value={inputs.downloadSuffix ?? ""}
|
||||||
onChange={(event) => {
|
onChange={(value) => {
|
||||||
handleChange("downloadSuffix", event.target.value);
|
handleChange("downloadSuffix", value);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -403,11 +391,9 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("totpVerificationUrl");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("totpVerificationUrl", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("totpVerificationUrl", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.totpVerificationUrl ?? ""}
|
value={inputs.totpVerificationUrl ?? ""}
|
||||||
placeholder={placeholders["task"]["totpVerificationUrl"]}
|
placeholder={placeholders["task"]["totpVerificationUrl"]}
|
||||||
@@ -424,11 +410,9 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("totpIdentifier");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("totpIdentifier", value);
|
||||||
onChange={(event) => {
|
|
||||||
handleChange("totpIdentifier", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.totpIdentifier ?? ""}
|
value={inputs.totpIdentifier ?? ""}
|
||||||
placeholder={placeholders["task"]["totpIdentifier"]}
|
placeholder={placeholders["task"]["totpIdentifier"]}
|
||||||
@@ -440,25 +424,6 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
{typeof parametersPanelField === "string" && (
|
|
||||||
<WorkflowBlockParameterSelect
|
|
||||||
nodeId={id}
|
|
||||||
onClose={() => 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} }}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,13 +20,9 @@ import {
|
|||||||
} from "@/components/ui/accordion";
|
} from "@/components/ui/accordion";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
import { helpTooltips } from "../../helpContent";
|
import { helpTooltips } from "../../helpContent";
|
||||||
import { WorkflowBlockParameterSelect } from "../WorkflowBlockParameterSelect";
|
|
||||||
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
||||||
|
|
||||||
function ValidationNode({ id, data }: NodeProps<ValidationNode>) {
|
function ValidationNode({ id, data }: NodeProps<ValidationNode>) {
|
||||||
const [parametersPanelField, setParametersPanelField] = useState<
|
|
||||||
string | null
|
|
||||||
>(null);
|
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
const { editable } = data;
|
const { editable } = data;
|
||||||
const [label, setLabel] = useNodeLabelChangeHandler({
|
const [label, setLabel] = useNodeLabelChangeHandler({
|
||||||
@@ -88,14 +84,9 @@ function ValidationNode({ id, data }: NodeProps<ValidationNode>) {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="text-xs text-slate-300">Complete if...</Label>
|
<Label className="text-xs text-slate-300">Complete if...</Label>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("completeCriterion");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("completeCriterion", value);
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("completeCriterion", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.completeCriterion}
|
value={inputs.completeCriterion}
|
||||||
className="nopan text-xs"
|
className="nopan text-xs"
|
||||||
@@ -104,14 +95,9 @@ function ValidationNode({ id, data }: NodeProps<ValidationNode>) {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="text-xs text-slate-300">Terminate if...</Label>
|
<Label className="text-xs text-slate-300">Terminate if...</Label>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
onIconClick={() => {
|
nodeId={id}
|
||||||
setParametersPanelField("terminateCriterion");
|
onChange={(value) => {
|
||||||
}}
|
handleChange("terminateCriterion", value);
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("terminateCriterion", event.target.value);
|
|
||||||
}}
|
}}
|
||||||
value={inputs.terminateCriterion}
|
value={inputs.terminateCriterion}
|
||||||
className="nopan text-xs"
|
className="nopan text-xs"
|
||||||
@@ -195,25 +181,6 @@ function ValidationNode({ id, data }: NodeProps<ValidationNode>) {
|
|||||||
</AccordionItem>
|
</AccordionItem>
|
||||||
</Accordion>
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
{typeof parametersPanelField === "string" && (
|
|
||||||
<WorkflowBlockParameterSelect
|
|
||||||
nodeId={id}
|
|
||||||
onClose={() => 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} }}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { useEdges, useNodes } from "@xyflow/react";
|
|||||||
import { useWorkflowParametersState } from "../useWorkflowParametersState";
|
import { useWorkflowParametersState } from "../useWorkflowParametersState";
|
||||||
import { AppNode } from ".";
|
import { AppNode } from ".";
|
||||||
import { getAvailableOutputParameterKeys } from "../workflowEditorUtils";
|
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 { SwitchBar } from "@/components/SwitchBar";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
@@ -10,11 +10,10 @@ import { ScrollAreaViewport } from "@radix-ui/react-scroll-area";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
onClose: () => void;
|
|
||||||
onAdd: (parameterKey: string) => void;
|
onAdd: (parameterKey: string) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
function WorkflowBlockParameterSelect({ nodeId, onClose, onAdd }: Props) {
|
function WorkflowBlockParameterSelect({ nodeId, onAdd }: Props) {
|
||||||
const [content, setContent] = useState("parameters");
|
const [content, setContent] = useState("parameters");
|
||||||
const [workflowParameters] = useWorkflowParametersState();
|
const [workflowParameters] = useWorkflowParametersState();
|
||||||
const nodes = useNodes<AppNode>();
|
const nodes = useNodes<AppNode>();
|
||||||
@@ -29,10 +28,9 @@ function WorkflowBlockParameterSelect({ nodeId, onClose, onAdd }: Props) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="nopan nowheel absolute right-[-296px] top-0 mt-0 w-[280px] cursor-auto space-y-3 rounded-md border border-slate-700 bg-slate-950 p-4">
|
<div className="cursor-auto space-y-3">
|
||||||
<header className="flex justify-between">
|
<header className="flex justify-between">
|
||||||
<h1>Add Parameter</h1>
|
<h1>Add Parameter</h1>
|
||||||
<Cross2Icon className="size-6 cursor-pointer" onClick={onClose} />
|
|
||||||
</header>
|
</header>
|
||||||
<SwitchBar
|
<SwitchBar
|
||||||
onChange={(value) => setContent(value)}
|
onChange={(value) => setContent(value)}
|
||||||
@@ -79,7 +77,7 @@ function WorkflowBlockParameterSelect({ nodeId, onClose, onAdd }: Props) {
|
|||||||
key={parameterKey}
|
key={parameterKey}
|
||||||
className="flex cursor-pointer justify-between rounded-md bg-slate-elevation1 px-3 py-2 text-xs hover:bg-slate-elevation2"
|
className="flex cursor-pointer justify-between rounded-md bg-slate-elevation1 px-3 py-2 text-xs hover:bg-slate-elevation2"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onAdd(parameterKey);
|
onAdd?.(parameterKey);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{parameterKey}
|
{parameterKey}
|
||||||
|
|||||||
Reference in New Issue
Block a user