Update parameter hint (#1628)
This commit is contained in:
@@ -3,43 +3,14 @@ import { cn } from "@/util/utils";
|
|||||||
import { Input } from "./ui/input";
|
import { Input } from "./ui/input";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
||||||
import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect";
|
import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect";
|
||||||
import { useEdges, useNodes } from "@xyflow/react";
|
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "./ui/tooltip";
|
|
||||||
|
|
||||||
type Props = Omit<React.ComponentProps<typeof Input>, "onChange"> & {
|
type Props = Omit<React.ComponentProps<typeof Input>, "onChange"> & {
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
isFirstInputInNode?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function WorkflowBlockInput(props: Props) {
|
function WorkflowBlockInput(props: Props) {
|
||||||
const { nodeId, onChange, ...inputProps } = props;
|
const { nodeId, onChange, ...inputProps } = props;
|
||||||
const edges = useEdges();
|
|
||||||
const nodes = useNodes();
|
|
||||||
|
|
||||||
function isInsideFirstNode() {
|
|
||||||
const node = nodes.find((node) => node.id === nodeId);
|
|
||||||
if (!node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const incomingEdge = edges.find((edge) => edge.target === node.id);
|
|
||||||
if (!incomingEdge) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const source = incomingEdge.source;
|
|
||||||
const sourceNode = nodes.find((node) => node.id === source);
|
|
||||||
if (!sourceNode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return !node.parentId && sourceNode.type === "start";
|
|
||||||
}
|
|
||||||
|
|
||||||
const showInputTooltip = isInsideFirstNode() && props.isFirstInputInNode;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@@ -52,19 +23,11 @@ function WorkflowBlockInput(props: Props) {
|
|||||||
/>
|
/>
|
||||||
<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">
|
||||||
<Popover>
|
<Popover>
|
||||||
<TooltipProvider>
|
<PopoverTrigger asChild>
|
||||||
<Tooltip open={showInputTooltip}>
|
<div className="rounded p-1 hover:bg-muted">
|
||||||
<TooltipTrigger asChild>
|
<PlusIcon className="size-4" />
|
||||||
<PopoverTrigger asChild>
|
</div>
|
||||||
<div className="rounded p-1 hover:bg-muted">
|
</PopoverTrigger>
|
||||||
<PlusIcon className="size-4" />
|
|
||||||
</div>
|
|
||||||
</PopoverTrigger>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Add parameters using the + button</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
|
|
||||||
<PopoverContent>
|
<PopoverContent>
|
||||||
<WorkflowBlockParameterSelect
|
<WorkflowBlockParameterSelect
|
||||||
nodeId={nodeId}
|
nodeId={nodeId}
|
||||||
|
|||||||
@@ -3,13 +3,6 @@ import { cn } from "@/util/utils";
|
|||||||
import { AutoResizingTextarea } from "./AutoResizingTextarea/AutoResizingTextarea";
|
import { AutoResizingTextarea } from "./AutoResizingTextarea/AutoResizingTextarea";
|
||||||
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
|
||||||
import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect";
|
import { WorkflowBlockParameterSelect } from "@/routes/workflows/editor/nodes/WorkflowBlockParameterSelect";
|
||||||
import {
|
|
||||||
Tooltip,
|
|
||||||
TooltipContent,
|
|
||||||
TooltipProvider,
|
|
||||||
TooltipTrigger,
|
|
||||||
} from "./ui/tooltip";
|
|
||||||
import { useEdges, useNodes } from "@xyflow/react";
|
|
||||||
|
|
||||||
type Props = Omit<
|
type Props = Omit<
|
||||||
React.ComponentProps<typeof AutoResizingTextarea>,
|
React.ComponentProps<typeof AutoResizingTextarea>,
|
||||||
@@ -17,32 +10,10 @@ type Props = Omit<
|
|||||||
> & {
|
> & {
|
||||||
onChange: (value: string) => void;
|
onChange: (value: string) => void;
|
||||||
nodeId: string;
|
nodeId: string;
|
||||||
isFirstInputInNode?: boolean;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function WorkflowBlockInputTextarea(props: Props) {
|
function WorkflowBlockInputTextarea(props: Props) {
|
||||||
const { nodeId, onChange, ...textAreaProps } = props;
|
const { nodeId, onChange, ...textAreaProps } = props;
|
||||||
const edges = useEdges();
|
|
||||||
const nodes = useNodes();
|
|
||||||
|
|
||||||
function isInsideFirstNode() {
|
|
||||||
const node = nodes.find((node) => node.id === nodeId);
|
|
||||||
if (!node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const incomingEdge = edges.find((edge) => edge.target === node.id);
|
|
||||||
if (!incomingEdge) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const source = incomingEdge.source;
|
|
||||||
const sourceNode = nodes.find((node) => node.id === source);
|
|
||||||
if (!sourceNode) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return !node.parentId && sourceNode.type === "start";
|
|
||||||
}
|
|
||||||
|
|
||||||
const showInputTooltip = isInsideFirstNode() && props.isFirstInputInNode;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
@@ -55,18 +26,11 @@ function WorkflowBlockInputTextarea(props: Props) {
|
|||||||
/>
|
/>
|
||||||
<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">
|
||||||
<Popover>
|
<Popover>
|
||||||
<TooltipProvider>
|
<PopoverTrigger asChild>
|
||||||
<Tooltip open={showInputTooltip}>
|
<div className="rounded p-1 hover:bg-muted">
|
||||||
<TooltipTrigger asChild>
|
<PlusIcon className="size-4" />
|
||||||
<PopoverTrigger asChild>
|
</div>
|
||||||
<div className="rounded p-1 hover:bg-muted">
|
</PopoverTrigger>
|
||||||
<PlusIcon className="size-4" />
|
|
||||||
</div>
|
|
||||||
</PopoverTrigger>
|
|
||||||
</TooltipTrigger>
|
|
||||||
<TooltipContent>Add parameters using the + button</TooltipContent>
|
|
||||||
</Tooltip>
|
|
||||||
</TooltipProvider>
|
|
||||||
<PopoverContent>
|
<PopoverContent>
|
||||||
<WorkflowBlockParameterSelect
|
<WorkflowBlockParameterSelect
|
||||||
nodeId={nodeId}
|
nodeId={nodeId}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { Edge, useEdges, useNodes } from "@xyflow/react";
|
||||||
|
import { AppNode } from "../nodes";
|
||||||
|
|
||||||
|
function isFirstNode(nodes: Array<AppNode>, edges: Array<Edge>, id: string) {
|
||||||
|
const node = nodes.find((node) => node.id === id);
|
||||||
|
if (!node) {
|
||||||
|
return false; // doesn't make sense but for TS
|
||||||
|
}
|
||||||
|
const incomingEdge = edges.find((edge) => edge.target === node.id);
|
||||||
|
if (!incomingEdge) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const source = incomingEdge.source;
|
||||||
|
const sourceNode = nodes.find((node) => node.id === source);
|
||||||
|
if (!sourceNode) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !node.parentId && sourceNode.type === "start";
|
||||||
|
}
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function useIsFirstBlockInWorkflow({ id }: Props): boolean {
|
||||||
|
const nodes = useNodes<AppNode>();
|
||||||
|
const edges = useEdges();
|
||||||
|
|
||||||
|
return isFirstNode(nodes, edges, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { useIsFirstBlockInWorkflow };
|
||||||
@@ -34,6 +34,7 @@ import { WorkflowBlockTypes } from "@/routes/workflows/types/workflowTypes";
|
|||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||||
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
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.";
|
||||||
@@ -75,6 +76,8 @@ function ActionNode({ id, data }: NodeProps<ActionNode>) {
|
|||||||
updateNodeData(id, { [key]: value });
|
updateNodeData(id, { [key]: value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Handle
|
<Handle
|
||||||
@@ -117,12 +120,19 @@ function ActionNode({ id, data }: NodeProps<ActionNode>) {
|
|||||||
</header>
|
</header>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex gap-2">
|
<div className="flex justify-between">
|
||||||
<Label className="text-xs text-slate-300">URL</Label>
|
<div className="flex gap-2">
|
||||||
<HelpTooltip content={urlTooltip} />
|
<Label className="text-xs text-slate-300">URL</Label>
|
||||||
|
<HelpTooltip content={urlTooltip} />
|
||||||
|
</div>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
isFirstInputInNode
|
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
handleChange("url", value);
|
handleChange("url", value);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import { AppNode } from "..";
|
|||||||
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||||
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
||||||
import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/WorkflowDataSchemaInputGroup";
|
import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/WorkflowDataSchemaInputGroup";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
function ExtractionNode({ id, data }: NodeProps<ExtractionNode>) {
|
function ExtractionNode({ id, data }: NodeProps<ExtractionNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
@@ -54,6 +55,8 @@ function ExtractionNode({ id, data }: NodeProps<ExtractionNode>) {
|
|||||||
const edges = useEdges();
|
const edges = useEdges();
|
||||||
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
||||||
|
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
function handleChange(key: string, value: unknown) {
|
function handleChange(key: string, value: unknown) {
|
||||||
if (!editable) {
|
if (!editable) {
|
||||||
return;
|
return;
|
||||||
@@ -100,16 +103,23 @@ function ExtractionNode({ id, data }: NodeProps<ExtractionNode>) {
|
|||||||
/>
|
/>
|
||||||
</header>
|
</header>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex gap-2">
|
<div className="flex justify-between">
|
||||||
<Label className="text-xs text-slate-300">
|
<div className="flex gap-2">
|
||||||
Data Extraction Goal
|
<Label className="text-xs text-slate-300">
|
||||||
</Label>
|
Data Extraction Goal
|
||||||
<HelpTooltip
|
</Label>
|
||||||
content={helpTooltips["extraction"]["dataExtractionGoal"]}
|
<HelpTooltip
|
||||||
/>
|
content={helpTooltips["extraction"]["dataExtractionGoal"]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
isFirstInputInNode
|
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
if (!editable) {
|
if (!editable) {
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import type { FileDownloadNode } from "./types";
|
|||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||||
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
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.";
|
||||||
@@ -65,6 +66,8 @@ function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
|||||||
const edges = useEdges();
|
const edges = useEdges();
|
||||||
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
||||||
|
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
function handleChange(key: string, value: unknown) {
|
function handleChange(key: string, value: unknown) {
|
||||||
if (!editable) {
|
if (!editable) {
|
||||||
return;
|
return;
|
||||||
@@ -114,12 +117,18 @@ function FileDownloadNode({ id, data }: NodeProps<FileDownloadNode>) {
|
|||||||
</header>
|
</header>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex gap-2">
|
<div className="flex justify-between">
|
||||||
<Label className="text-xs text-slate-300">URL</Label>
|
<div className="flex gap-2">
|
||||||
<HelpTooltip content={urlTooltip} />
|
<Label className="text-xs text-slate-300">URL</Label>
|
||||||
|
<HelpTooltip content={urlTooltip} />
|
||||||
|
</div>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
isFirstInputInNode
|
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
handleChange("url", value);
|
handleChange("url", value);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { HelpTooltip } from "@/components/HelpTooltip";
|
import { HelpTooltip } from "@/components/HelpTooltip";
|
||||||
import { Input } from "@/components/ui/input";
|
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
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";
|
||||||
@@ -11,6 +10,8 @@ import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
|||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
|
import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
|
||||||
import { type FileParserNode } from "./types";
|
import { type FileParserNode } from "./types";
|
||||||
|
import { WorkflowBlockInput } from "@/components/WorkflowBlockInput";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
function FileParserNode({ id, data }: NodeProps<FileParserNode>) {
|
function FileParserNode({ id, data }: NodeProps<FileParserNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
@@ -23,6 +24,8 @@ function FileParserNode({ id, data }: NodeProps<FileParserNode>) {
|
|||||||
initialValue: data.label,
|
initialValue: data.label,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Handle
|
<Handle
|
||||||
@@ -65,18 +68,27 @@ function FileParserNode({ id, data }: NodeProps<FileParserNode>) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex gap-2">
|
<div className="flex justify-between">
|
||||||
<Label className="text-xs text-slate-300">File URL</Label>
|
<div className="flex gap-2">
|
||||||
<HelpTooltip content={helpTooltips["fileParser"]["fileUrl"]} />
|
<Label className="text-xs text-slate-300">File URL</Label>
|
||||||
|
<HelpTooltip content={helpTooltips["fileParser"]["fileUrl"]} />
|
||||||
|
</div>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<Input
|
|
||||||
|
<WorkflowBlockInput
|
||||||
|
nodeId={id}
|
||||||
value={inputs.fileUrl}
|
value={inputs.fileUrl}
|
||||||
onChange={(event) => {
|
onChange={(value) => {
|
||||||
if (!data.editable) {
|
if (!data.editable) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setInputs({ ...inputs, fileUrl: event.target.value });
|
setInputs({ ...inputs, fileUrl: value });
|
||||||
updateNodeData(id, { fileUrl: event.target.value });
|
updateNodeData(id, { fileUrl: value });
|
||||||
}}
|
}}
|
||||||
className="nopan text-xs"
|
className="nopan text-xs"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import type { LoginNode } from "./types";
|
|||||||
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
@@ -60,6 +61,7 @@ function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
|||||||
const nodes = useNodes<AppNode>();
|
const nodes = useNodes<AppNode>();
|
||||||
const edges = useEdges();
|
const edges = useEdges();
|
||||||
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
function handleChange(key: string, value: unknown) {
|
function handleChange(key: string, value: unknown) {
|
||||||
if (!editable) {
|
if (!editable) {
|
||||||
@@ -111,12 +113,19 @@ function LoginNode({ id, data }: NodeProps<LoginNode>) {
|
|||||||
</header>
|
</header>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex gap-2">
|
<div className="flex justify-between">
|
||||||
<Label className="text-xs text-slate-300">URL</Label>
|
<div className="flex gap-2">
|
||||||
<HelpTooltip content={helpTooltips["login"]["url"]} />
|
<Label className="text-xs text-slate-300">URL</Label>
|
||||||
|
<HelpTooltip content={helpTooltips["login"]["url"]} />
|
||||||
|
</div>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
isFirstInputInNode
|
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
handleChange("url", value);
|
handleChange("url", value);
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import { NodeActionMenu } from "../NodeActionMenu";
|
|||||||
import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
|
import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
|
||||||
import type { LoopNode } from "./types";
|
import type { LoopNode } from "./types";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
@@ -32,6 +33,8 @@ function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
|||||||
});
|
});
|
||||||
const deleteNodeCallback = useDeleteNodeCallback();
|
const deleteNodeCallback = useDeleteNodeCallback();
|
||||||
|
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
const children = nodes.filter((node) => node.parentId === id);
|
const children = nodes.filter((node) => node.parentId === id);
|
||||||
const furthestDownChild: Node | null = children.reduce(
|
const furthestDownChild: Node | null = children.reduce(
|
||||||
(acc, child) => {
|
(acc, child) => {
|
||||||
@@ -99,12 +102,18 @@ function LoopNode({ id, data }: NodeProps<LoopNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex gap-2">
|
<div className="flex justify-between">
|
||||||
<Label className="text-xs text-slate-300">Loop Value</Label>
|
<div className="flex gap-2">
|
||||||
<HelpTooltip content={helpTooltips["loop"]["loopValue"]} />
|
<Label className="text-xs text-slate-300">Loop Value</Label>
|
||||||
|
<HelpTooltip content={helpTooltips["loop"]["loopValue"]} />
|
||||||
|
</div>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInput
|
<WorkflowBlockInput
|
||||||
isFirstInputInNode
|
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
value={inputs.loopVariableReference}
|
value={inputs.loopVariableReference}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import type { NavigationNode } from "./types";
|
|||||||
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
@@ -63,6 +64,8 @@ function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
|||||||
const edges = useEdges();
|
const edges = useEdges();
|
||||||
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
||||||
|
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
function handleChange(key: string, value: unknown) {
|
function handleChange(key: string, value: unknown) {
|
||||||
if (!editable) {
|
if (!editable) {
|
||||||
return;
|
return;
|
||||||
@@ -113,12 +116,19 @@ function NavigationNode({ id, data }: NodeProps<NavigationNode>) {
|
|||||||
</header>
|
</header>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex gap-2">
|
<div className="flex justify-between">
|
||||||
<Label className="text-xs text-slate-300">URL</Label>
|
<div className="flex gap-2">
|
||||||
<HelpTooltip content={helpTooltips["navigation"]["url"]} />
|
<Label className="text-xs text-slate-300">URL</Label>
|
||||||
|
<HelpTooltip content={helpTooltips["navigation"]["url"]} />
|
||||||
|
</div>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
isFirstInputInNode
|
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
handleChange("url", value);
|
handleChange("url", value);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { dataSchemaExampleForFileExtraction } from "../types";
|
|||||||
import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
|
import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
|
||||||
import { type PDFParserNode } from "./types";
|
import { type PDFParserNode } from "./types";
|
||||||
import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/WorkflowDataSchemaInputGroup";
|
import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/WorkflowDataSchemaInputGroup";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
function PDFParserNode({ id, data }: NodeProps<PDFParserNode>) {
|
function PDFParserNode({ id, data }: NodeProps<PDFParserNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
@@ -34,6 +35,8 @@ function PDFParserNode({ id, data }: NodeProps<PDFParserNode>) {
|
|||||||
updateNodeData(id, { [key]: value });
|
updateNodeData(id, { [key]: value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Handle
|
<Handle
|
||||||
@@ -76,12 +79,18 @@ function PDFParserNode({ id, data }: NodeProps<PDFParserNode>) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex gap-2">
|
<div className="flex justify-between">
|
||||||
<Label className="text-xs text-slate-300">File URL</Label>
|
<div className="flex gap-2">
|
||||||
<HelpTooltip content={helpTooltips["pdfParser"]["fileUrl"]} />
|
<Label className="text-xs text-slate-300">File URL</Label>
|
||||||
|
<HelpTooltip content={helpTooltips["pdfParser"]["fileUrl"]} />
|
||||||
|
</div>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInput
|
<WorkflowBlockInput
|
||||||
isFirstInputInNode
|
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
value={inputs.fileUrl}
|
value={inputs.fileUrl}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
|
|||||||
import { type SendEmailNode } from "./types";
|
import { type SendEmailNode } from "./types";
|
||||||
import { WorkflowBlockInput } from "@/components/WorkflowBlockInput";
|
import { WorkflowBlockInput } from "@/components/WorkflowBlockInput";
|
||||||
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
function SendEmailNode({ id, data }: NodeProps<SendEmailNode>) {
|
function SendEmailNode({ id, data }: NodeProps<SendEmailNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
@@ -36,6 +37,8 @@ function SendEmailNode({ id, data }: NodeProps<SendEmailNode>) {
|
|||||||
updateNodeData(id, { [key]: value });
|
updateNodeData(id, { [key]: value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Handle
|
<Handle
|
||||||
@@ -77,9 +80,15 @@ function SendEmailNode({ id, data }: NodeProps<SendEmailNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="text-xs text-slate-300">Recipients</Label>
|
<div className="flex justify-between">
|
||||||
|
<Label className="text-xs text-slate-300">Recipients</Label>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
<WorkflowBlockInput
|
<WorkflowBlockInput
|
||||||
isFirstInputInNode
|
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
handleChange("recipients", value);
|
handleChange("recipients", value);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
|
|||||||
import { ParametersMultiSelect } from "./ParametersMultiSelect";
|
import { ParametersMultiSelect } from "./ParametersMultiSelect";
|
||||||
import type { TaskNode } from "./types";
|
import type { TaskNode } from "./types";
|
||||||
import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/WorkflowDataSchemaInputGroup";
|
import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/WorkflowDataSchemaInputGroup";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
@@ -43,6 +44,7 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
const nodes = useNodes<AppNode>();
|
const nodes = useNodes<AppNode>();
|
||||||
const edges = useEdges();
|
const edges = useEdges();
|
||||||
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
const outputParameterKeys = getAvailableOutputParameterKeys(nodes, edges, id);
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
const [label, setLabel] = useNodeLabelChangeHandler({
|
const [label, setLabel] = useNodeLabelChangeHandler({
|
||||||
id,
|
id,
|
||||||
@@ -121,12 +123,18 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
<AccordionContent className="pl-[1.5rem] pr-1">
|
<AccordionContent className="pl-[1.5rem] pr-1">
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex gap-2">
|
<div className="flex justify-between">
|
||||||
<Label className="text-xs text-slate-300">URL</Label>
|
<div className="flex gap-2">
|
||||||
<HelpTooltip content={helpTooltips["task"]["url"]} />
|
<Label className="text-xs text-slate-300">URL</Label>
|
||||||
|
<HelpTooltip content={helpTooltips["task"]["url"]} />
|
||||||
|
</div>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
isFirstInputInNode
|
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
handleChange("url", value);
|
handleChange("url", value);
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import { type TextPromptNode } from "./types";
|
|||||||
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
import { WorkflowBlockInputTextarea } from "@/components/WorkflowBlockInputTextarea";
|
||||||
import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/WorkflowDataSchemaInputGroup";
|
import { WorkflowDataSchemaInputGroup } from "@/components/DataSchemaInputGroup/WorkflowDataSchemaInputGroup";
|
||||||
import { dataSchemaExampleValue } from "../types";
|
import { dataSchemaExampleValue } from "../types";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
@@ -51,6 +52,8 @@ function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
|||||||
updateNodeData(id, { [key]: value });
|
updateNodeData(id, { [key]: value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Handle
|
<Handle
|
||||||
@@ -92,12 +95,19 @@ function TextPromptNode({ id, data }: NodeProps<TextPromptNode>) {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex gap-2">
|
<div className="flex justify-between">
|
||||||
<Label className="text-xs text-slate-300">Prompt</Label>
|
<div className="flex gap-2">
|
||||||
<HelpTooltip content={helpTooltips["textPrompt"]["prompt"]} />
|
<Label className="text-xs text-slate-300">Prompt</Label>
|
||||||
|
<HelpTooltip content={helpTooltips["textPrompt"]["prompt"]} />
|
||||||
|
</div>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
isFirstInputInNode
|
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
handleChange("prompt", value);
|
handleChange("prompt", value);
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import type { ValidationNode } from "./types";
|
|||||||
import { AppNode } from "..";
|
import { AppNode } from "..";
|
||||||
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||||
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
import { ParametersMultiSelect } from "../TaskNode/ParametersMultiSelect";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
function ValidationNode({ id, data }: NodeProps<ValidationNode>) {
|
function ValidationNode({ id, data }: NodeProps<ValidationNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
@@ -58,6 +59,8 @@ function ValidationNode({ id, data }: NodeProps<ValidationNode>) {
|
|||||||
updateNodeData(id, { [key]: value });
|
updateNodeData(id, { [key]: value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Handle
|
<Handle
|
||||||
@@ -99,9 +102,15 @@ function ValidationNode({ id, data }: NodeProps<ValidationNode>) {
|
|||||||
/>
|
/>
|
||||||
</header>
|
</header>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label className="text-xs text-slate-300">Complete if...</Label>
|
<div className="flex justify-between">
|
||||||
|
<Label className="text-xs text-slate-300">Complete if...</Label>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
<WorkflowBlockInputTextarea
|
<WorkflowBlockInputTextarea
|
||||||
isFirstInputInNode
|
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
handleChange("completeCriterion", value);
|
handleChange("completeCriterion", value);
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { NodeActionMenu } from "../NodeActionMenu";
|
|||||||
import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
|
import { WorkflowBlockIcon } from "../WorkflowBlockIcon";
|
||||||
import type { WaitNode } from "./types";
|
import type { WaitNode } from "./types";
|
||||||
import { WorkflowBlockInput } from "@/components/WorkflowBlockInput";
|
import { WorkflowBlockInput } from "@/components/WorkflowBlockInput";
|
||||||
|
import { useIsFirstBlockInWorkflow } from "../../hooks/useIsFirstNodeInWorkflow";
|
||||||
|
|
||||||
function WaitNode({ id, data }: NodeProps<WaitNode>) {
|
function WaitNode({ id, data }: NodeProps<WaitNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
@@ -32,6 +33,8 @@ function WaitNode({ id, data }: NodeProps<WaitNode>) {
|
|||||||
updateNodeData(id, { [key]: value });
|
updateNodeData(id, { [key]: value });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isFirstWorkflowBlock = useIsFirstBlockInWorkflow({ id });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Handle
|
<Handle
|
||||||
@@ -73,15 +76,22 @@ function WaitNode({ id, data }: NodeProps<WaitNode>) {
|
|||||||
/>
|
/>
|
||||||
</header>
|
</header>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<div className="flex gap-2">
|
<div className="flex justify-between">
|
||||||
<Label className="text-xs text-slate-300">
|
<div className="flex gap-2">
|
||||||
Wait Time (in seconds)
|
<Label className="text-xs text-slate-300">
|
||||||
</Label>
|
Wait Time (in seconds)
|
||||||
<HelpTooltip content={helpTooltips["wait"]["waitInSeconds"]} />
|
</Label>
|
||||||
|
<HelpTooltip content={helpTooltips["wait"]["waitInSeconds"]} />
|
||||||
|
</div>
|
||||||
|
{isFirstWorkflowBlock ? (
|
||||||
|
<div className="flex justify-end text-xs text-slate-400">
|
||||||
|
Tip: Use the {"+"} button to add parameters!
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<WorkflowBlockInput
|
<WorkflowBlockInput
|
||||||
nodeId={id}
|
nodeId={id}
|
||||||
isFirstInputInNode
|
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
max="300"
|
max="300"
|
||||||
|
|||||||
Reference in New Issue
Block a user