Task block changes (#989)
This commit is contained in:
@@ -27,7 +27,6 @@ import { AppNode } from "..";
|
|||||||
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
import { getAvailableOutputParameterKeys } from "../../workflowEditorUtils";
|
||||||
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
import { EditableNodeTitle } from "../components/EditableNodeTitle";
|
||||||
import { NodeActionMenu } from "../NodeActionMenu";
|
import { NodeActionMenu } from "../NodeActionMenu";
|
||||||
import { TaskNodeDisplayModeSwitch } from "./TaskNodeDisplayModeSwitch";
|
|
||||||
import { TaskNodeParametersPanel } from "./TaskNodeParametersPanel";
|
import { TaskNodeParametersPanel } from "./TaskNodeParametersPanel";
|
||||||
import {
|
import {
|
||||||
dataSchemaExampleValue,
|
dataSchemaExampleValue,
|
||||||
@@ -35,17 +34,11 @@ import {
|
|||||||
fieldPlaceholders,
|
fieldPlaceholders,
|
||||||
helpTooltipContent,
|
helpTooltipContent,
|
||||||
type TaskNode,
|
type TaskNode,
|
||||||
type TaskNodeDisplayMode,
|
|
||||||
} from "./types";
|
} from "./types";
|
||||||
import { useParams } from "react-router-dom";
|
import { Separator } from "@/components/ui/separator";
|
||||||
|
|
||||||
function getLocalStorageKey(workflowPermanentId: string, label: string) {
|
|
||||||
return `skyvern-task-block-${workflowPermanentId}-${label}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
||||||
const { updateNodeData } = useReactFlow();
|
const { updateNodeData } = useReactFlow();
|
||||||
const { workflowPermanentId } = useParams();
|
|
||||||
const { editable } = data;
|
const { editable } = data;
|
||||||
const deleteNodeCallback = useDeleteNodeCallback();
|
const deleteNodeCallback = useDeleteNodeCallback();
|
||||||
const nodes = useNodes<AppNode>();
|
const nodes = useNodes<AppNode>();
|
||||||
@@ -56,15 +49,6 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
initialValue: data.label,
|
initialValue: data.label,
|
||||||
});
|
});
|
||||||
|
|
||||||
const [displayMode, setDisplayMode] = useState<TaskNodeDisplayMode>(
|
|
||||||
workflowPermanentId &&
|
|
||||||
localStorage.getItem(getLocalStorageKey(workflowPermanentId, label))
|
|
||||||
? (localStorage.getItem(
|
|
||||||
getLocalStorageKey(workflowPermanentId, label),
|
|
||||||
) as TaskNodeDisplayMode)
|
|
||||||
: "basic",
|
|
||||||
);
|
|
||||||
|
|
||||||
const [inputs, setInputs] = useState({
|
const [inputs, setInputs] = useState({
|
||||||
url: data.url,
|
url: data.url,
|
||||||
navigationGoal: data.navigationGoal,
|
navigationGoal: data.navigationGoal,
|
||||||
@@ -74,6 +58,7 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
maxStepsOverride: data.maxStepsOverride,
|
maxStepsOverride: data.maxStepsOverride,
|
||||||
allowDownloads: data.allowDownloads,
|
allowDownloads: data.allowDownloads,
|
||||||
continueOnFailure: data.continueOnFailure,
|
continueOnFailure: data.continueOnFailure,
|
||||||
|
cacheActions: data.cacheActions,
|
||||||
downloadSuffix: data.downloadSuffix,
|
downloadSuffix: data.downloadSuffix,
|
||||||
errorCodeMapping: data.errorCodeMapping,
|
errorCodeMapping: data.errorCodeMapping,
|
||||||
totpVerificationUrl: data.totpVerificationUrl,
|
totpVerificationUrl: data.totpVerificationUrl,
|
||||||
@@ -88,386 +73,6 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
updateNodeData(id, { [key]: value });
|
updateNodeData(id, { [key]: value });
|
||||||
}
|
}
|
||||||
|
|
||||||
const basicContent = (
|
|
||||||
<>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs text-slate-300">URL</Label>
|
|
||||||
<HelpTooltip content={helpTooltipContent["url"]} />
|
|
||||||
</div>
|
|
||||||
<AutoResizingTextarea
|
|
||||||
value={inputs.url}
|
|
||||||
className="nopan text-xs"
|
|
||||||
name="url"
|
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("url", event.target.value);
|
|
||||||
}}
|
|
||||||
placeholder={fieldPlaceholders["url"]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs text-slate-300">Goal</Label>
|
|
||||||
<HelpTooltip content={helpTooltipContent["navigationGoal"]} />
|
|
||||||
</div>
|
|
||||||
<AutoResizingTextarea
|
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("navigationGoal", event.target.value);
|
|
||||||
}}
|
|
||||||
value={inputs.navigationGoal}
|
|
||||||
placeholder={fieldPlaceholders["navigationGoal"]}
|
|
||||||
className="nopan text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<TaskNodeParametersPanel
|
|
||||||
availableOutputParameters={outputParameterKeys}
|
|
||||||
parameters={data.parameterKeys}
|
|
||||||
onParametersChange={(parameterKeys) => {
|
|
||||||
updateNodeData(id, { parameterKeys });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
const advancedContent = (
|
|
||||||
<>
|
|
||||||
<Accordion type="multiple" defaultValue={["content"]}>
|
|
||||||
<AccordionItem value="content">
|
|
||||||
<AccordionTrigger>Content</AccordionTrigger>
|
|
||||||
<AccordionContent className="pl-[1.5rem] pr-1">
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs text-slate-300">URL</Label>
|
|
||||||
<HelpTooltip content={helpTooltipContent["url"]} />
|
|
||||||
</div>
|
|
||||||
<AutoResizingTextarea
|
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("url", event.target.value);
|
|
||||||
}}
|
|
||||||
value={inputs.url}
|
|
||||||
placeholder={fieldPlaceholders["url"]}
|
|
||||||
className="nopan text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs text-slate-300">Goal</Label>
|
|
||||||
<HelpTooltip content={helpTooltipContent["navigationGoal"]} />
|
|
||||||
</div>
|
|
||||||
<AutoResizingTextarea
|
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("navigationGoal", event.target.value);
|
|
||||||
}}
|
|
||||||
value={inputs.navigationGoal}
|
|
||||||
placeholder={fieldPlaceholders["navigationGoal"]}
|
|
||||||
className="nopan text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<TaskNodeParametersPanel
|
|
||||||
availableOutputParameters={outputParameterKeys}
|
|
||||||
parameters={data.parameterKeys}
|
|
||||||
onParametersChange={(parameterKeys) => {
|
|
||||||
updateNodeData(id, { parameterKeys });
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem value="extraction">
|
|
||||||
<AccordionTrigger>Extraction</AccordionTrigger>
|
|
||||||
<AccordionContent className="pl-[1.5rem] pr-1">
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs text-slate-300">
|
|
||||||
Data Extraction Goal
|
|
||||||
</Label>
|
|
||||||
<HelpTooltip
|
|
||||||
content={helpTooltipContent["dataExtractionGoal"]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<AutoResizingTextarea
|
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("dataExtractionGoal", event.target.value);
|
|
||||||
}}
|
|
||||||
value={inputs.dataExtractionGoal}
|
|
||||||
placeholder={fieldPlaceholders["dataExtractionGoal"]}
|
|
||||||
className="nopan text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs text-slate-300">
|
|
||||||
Data Schema
|
|
||||||
</Label>
|
|
||||||
<HelpTooltip content={helpTooltipContent["dataSchema"]} />
|
|
||||||
</div>
|
|
||||||
<Checkbox
|
|
||||||
checked={inputs.dataSchema !== "null"}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange(
|
|
||||||
"dataSchema",
|
|
||||||
checked
|
|
||||||
? JSON.stringify(dataSchemaExampleValue, null, 2)
|
|
||||||
: "null",
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{inputs.dataSchema !== "null" && (
|
|
||||||
<div>
|
|
||||||
<CodeEditor
|
|
||||||
language="json"
|
|
||||||
value={inputs.dataSchema}
|
|
||||||
onChange={(value) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("dataSchema", value);
|
|
||||||
}}
|
|
||||||
className="nowheel nopan"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem value="limits">
|
|
||||||
<AccordionTrigger>Limits</AccordionTrigger>
|
|
||||||
<AccordionContent className="pl-[1.5rem] pr-1 pt-1">
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs font-normal text-slate-300">
|
|
||||||
Max Retries
|
|
||||||
</Label>
|
|
||||||
<HelpTooltip content={helpTooltipContent["maxRetries"]} />
|
|
||||||
</div>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder={fieldPlaceholders["maxRetries"]}
|
|
||||||
className="nopan w-52 text-xs"
|
|
||||||
min="0"
|
|
||||||
value={inputs.maxRetries ?? ""}
|
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const value =
|
|
||||||
event.target.value === ""
|
|
||||||
? null
|
|
||||||
: Number(event.target.value);
|
|
||||||
handleChange("maxRetries", value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs font-normal text-slate-300">
|
|
||||||
Max Steps Override
|
|
||||||
</Label>
|
|
||||||
<HelpTooltip
|
|
||||||
content={helpTooltipContent["maxStepsOverride"]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Input
|
|
||||||
type="number"
|
|
||||||
placeholder={fieldPlaceholders["maxStepsOverride"]}
|
|
||||||
className="nopan w-52 text-xs"
|
|
||||||
min="0"
|
|
||||||
value={inputs.maxStepsOverride ?? ""}
|
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const value =
|
|
||||||
event.target.value === ""
|
|
||||||
? null
|
|
||||||
: Number(event.target.value);
|
|
||||||
handleChange("maxStepsOverride", value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs font-normal text-slate-300">
|
|
||||||
Complete on Download
|
|
||||||
</Label>
|
|
||||||
<HelpTooltip
|
|
||||||
content={helpTooltipContent["completeOnDownload"]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="w-52">
|
|
||||||
<Switch
|
|
||||||
checked={inputs.allowDownloads}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("allowDownloads", checked);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs font-normal text-slate-300">
|
|
||||||
Continue on Failure
|
|
||||||
</Label>
|
|
||||||
</div>
|
|
||||||
<div className="w-52">
|
|
||||||
<Switch
|
|
||||||
checked={inputs.continueOnFailure}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("continueOnFailure", checked);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center justify-between">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs font-normal text-slate-300">
|
|
||||||
File Suffix
|
|
||||||
</Label>
|
|
||||||
<HelpTooltip content={helpTooltipContent["fileSuffix"]} />
|
|
||||||
</div>
|
|
||||||
<Input
|
|
||||||
type="text"
|
|
||||||
placeholder={fieldPlaceholders["downloadSuffix"]}
|
|
||||||
className="nopan w-52 text-xs"
|
|
||||||
value={inputs.downloadSuffix ?? ""}
|
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("downloadSuffix", event.target.value);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs font-normal text-slate-300">
|
|
||||||
Error Messages
|
|
||||||
</Label>
|
|
||||||
<HelpTooltip
|
|
||||||
content={helpTooltipContent["errorCodeMapping"]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<Checkbox
|
|
||||||
checked={inputs.errorCodeMapping !== "null"}
|
|
||||||
disabled={!editable}
|
|
||||||
onCheckedChange={(checked) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange(
|
|
||||||
"errorCodeMapping",
|
|
||||||
checked
|
|
||||||
? JSON.stringify(errorMappingExampleValue, null, 2)
|
|
||||||
: "null",
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{inputs.errorCodeMapping !== "null" && (
|
|
||||||
<div>
|
|
||||||
<CodeEditor
|
|
||||||
language="json"
|
|
||||||
value={inputs.errorCodeMapping}
|
|
||||||
onChange={(value) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("errorCodeMapping", value);
|
|
||||||
}}
|
|
||||||
className="nowheel nopan"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
<AccordionItem value="totp">
|
|
||||||
<AccordionTrigger>Two-Factor Authentication</AccordionTrigger>
|
|
||||||
<AccordionContent className="pl-[1.5rem] pr-1">
|
|
||||||
<div className="space-y-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs text-slate-300">
|
|
||||||
2FA Verification URL
|
|
||||||
</Label>
|
|
||||||
<HelpTooltip
|
|
||||||
content={helpTooltipContent["totpVerificationUrl"]}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<AutoResizingTextarea
|
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("totpVerificationUrl", event.target.value);
|
|
||||||
}}
|
|
||||||
value={inputs.totpVerificationUrl ?? ""}
|
|
||||||
placeholder={fieldPlaceholders["totpVerificationUrl"]}
|
|
||||||
className="nopan text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="space-y-2">
|
|
||||||
<div className="flex gap-2">
|
|
||||||
<Label className="text-xs text-slate-300">
|
|
||||||
2FA Identifier
|
|
||||||
</Label>
|
|
||||||
<HelpTooltip content={helpTooltipContent["totpIdentifier"]} />
|
|
||||||
</div>
|
|
||||||
<AutoResizingTextarea
|
|
||||||
onChange={(event) => {
|
|
||||||
if (!editable) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
handleChange("totpIdentifier", event.target.value);
|
|
||||||
}}
|
|
||||||
value={inputs.totpIdentifier ?? ""}
|
|
||||||
placeholder={fieldPlaceholders["totpIdentifier"]}
|
|
||||||
className="nopan text-xs"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</AccordionContent>
|
|
||||||
</AccordionItem>
|
|
||||||
</Accordion>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Handle
|
<Handle
|
||||||
@@ -482,7 +87,7 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
id="b"
|
id="b"
|
||||||
className="opacity-0"
|
className="opacity-0"
|
||||||
/>
|
/>
|
||||||
<div className="w-[30rem] space-y-4 rounded-lg bg-slate-elevation3 px-6 py-4">
|
<div className="w-[30rem] space-y-2 rounded-lg bg-slate-elevation3 px-6 py-4">
|
||||||
<div className="flex h-[2.75rem] justify-between">
|
<div className="flex h-[2.75rem] justify-between">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
<div className="flex h-[2.75rem] w-[2.75rem] items-center justify-center rounded border border-slate-600">
|
<div className="flex h-[2.75rem] w-[2.75rem] items-center justify-center rounded border border-slate-600">
|
||||||
@@ -505,20 +110,354 @@ function TaskNode({ id, data }: NodeProps<TaskNode>) {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<TaskNodeDisplayModeSwitch
|
<Accordion type="multiple" defaultValue={["content", "extraction"]}>
|
||||||
value={displayMode}
|
<AccordionItem value="content">
|
||||||
onChange={(mode) => {
|
<AccordionTrigger>Content</AccordionTrigger>
|
||||||
setDisplayMode(mode);
|
<AccordionContent className="pl-[1.5rem] pr-1">
|
||||||
if (workflowPermanentId) {
|
<div className="space-y-4">
|
||||||
localStorage.setItem(
|
<div className="space-y-2">
|
||||||
getLocalStorageKey(workflowPermanentId, label),
|
<div className="flex gap-2">
|
||||||
mode,
|
<Label className="text-xs text-slate-300">URL</Label>
|
||||||
);
|
<HelpTooltip content={helpTooltipContent["url"]} />
|
||||||
}
|
</div>
|
||||||
}}
|
<AutoResizingTextarea
|
||||||
/>
|
onChange={(event) => {
|
||||||
{displayMode === "basic" && basicContent}
|
if (!editable) {
|
||||||
{displayMode === "advanced" && advancedContent}
|
return;
|
||||||
|
}
|
||||||
|
handleChange("url", event.target.value);
|
||||||
|
}}
|
||||||
|
value={inputs.url}
|
||||||
|
placeholder={fieldPlaceholders["url"]}
|
||||||
|
className="nopan text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs text-slate-300">Goal</Label>
|
||||||
|
<HelpTooltip
|
||||||
|
content={helpTooltipContent["navigationGoal"]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<AutoResizingTextarea
|
||||||
|
onChange={(event) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleChange("navigationGoal", event.target.value);
|
||||||
|
}}
|
||||||
|
value={inputs.navigationGoal}
|
||||||
|
placeholder={fieldPlaceholders["navigationGoal"]}
|
||||||
|
className="nopan text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<TaskNodeParametersPanel
|
||||||
|
availableOutputParameters={outputParameterKeys}
|
||||||
|
parameters={data.parameterKeys}
|
||||||
|
onParametersChange={(parameterKeys) => {
|
||||||
|
updateNodeData(id, { parameterKeys });
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
<AccordionItem value="extraction">
|
||||||
|
<AccordionTrigger>Extraction</AccordionTrigger>
|
||||||
|
<AccordionContent className="pl-[1.5rem] pr-1">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs text-slate-300">
|
||||||
|
Data Extraction Goal
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip
|
||||||
|
content={helpTooltipContent["dataExtractionGoal"]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<AutoResizingTextarea
|
||||||
|
onChange={(event) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleChange("dataExtractionGoal", event.target.value);
|
||||||
|
}}
|
||||||
|
value={inputs.dataExtractionGoal}
|
||||||
|
placeholder={fieldPlaceholders["dataExtractionGoal"]}
|
||||||
|
className="nopan text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs text-slate-300">
|
||||||
|
Data Schema
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content={helpTooltipContent["dataSchema"]} />
|
||||||
|
</div>
|
||||||
|
<Checkbox
|
||||||
|
checked={inputs.dataSchema !== "null"}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleChange(
|
||||||
|
"dataSchema",
|
||||||
|
checked
|
||||||
|
? JSON.stringify(dataSchemaExampleValue, null, 2)
|
||||||
|
: "null",
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{inputs.dataSchema !== "null" && (
|
||||||
|
<div>
|
||||||
|
<CodeEditor
|
||||||
|
language="json"
|
||||||
|
value={inputs.dataSchema}
|
||||||
|
onChange={(value) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleChange("dataSchema", value);
|
||||||
|
}}
|
||||||
|
className="nowheel nopan"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
<AccordionItem value="advanced" className="border-b-0">
|
||||||
|
<AccordionTrigger>Advanced Settings</AccordionTrigger>
|
||||||
|
<AccordionContent className="pl-[1.5rem] pr-1 pt-1">
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs font-normal text-slate-300">
|
||||||
|
Max Retries
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content={helpTooltipContent["maxRetries"]} />
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder={fieldPlaceholders["maxRetries"]}
|
||||||
|
className="nopan w-52 text-xs"
|
||||||
|
min="0"
|
||||||
|
value={inputs.maxRetries ?? ""}
|
||||||
|
onChange={(event) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const value =
|
||||||
|
event.target.value === ""
|
||||||
|
? null
|
||||||
|
: Number(event.target.value);
|
||||||
|
handleChange("maxRetries", value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs font-normal text-slate-300">
|
||||||
|
Max Steps Override
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip
|
||||||
|
content={helpTooltipContent["maxStepsOverride"]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
placeholder={fieldPlaceholders["maxStepsOverride"]}
|
||||||
|
className="nopan w-52 text-xs"
|
||||||
|
min="0"
|
||||||
|
value={inputs.maxStepsOverride ?? ""}
|
||||||
|
onChange={(event) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const value =
|
||||||
|
event.target.value === ""
|
||||||
|
? null
|
||||||
|
: Number(event.target.value);
|
||||||
|
handleChange("maxStepsOverride", value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex gap-4">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs font-normal text-slate-300">
|
||||||
|
Error Messages
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip
|
||||||
|
content={helpTooltipContent["errorCodeMapping"]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Checkbox
|
||||||
|
checked={inputs.errorCodeMapping !== "null"}
|
||||||
|
disabled={!editable}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleChange(
|
||||||
|
"errorCodeMapping",
|
||||||
|
checked
|
||||||
|
? JSON.stringify(errorMappingExampleValue, null, 2)
|
||||||
|
: "null",
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{inputs.errorCodeMapping !== "null" && (
|
||||||
|
<div>
|
||||||
|
<CodeEditor
|
||||||
|
language="json"
|
||||||
|
value={inputs.errorCodeMapping}
|
||||||
|
onChange={(value) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleChange("errorCodeMapping", value);
|
||||||
|
}}
|
||||||
|
className="nowheel nopan"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs font-normal text-slate-300">
|
||||||
|
Continue on Failure
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip
|
||||||
|
content={helpTooltipContent["continueOnFailure"]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="w-52">
|
||||||
|
<Switch
|
||||||
|
checked={inputs.continueOnFailure}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleChange("continueOnFailure", checked);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs font-normal text-slate-300">
|
||||||
|
Cache Actions
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content={helpTooltipContent["cacheActions"]} />
|
||||||
|
</div>
|
||||||
|
<div className="w-52">
|
||||||
|
<Switch
|
||||||
|
checked={inputs.cacheActions}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleChange("cacheActions", checked);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs font-normal text-slate-300">
|
||||||
|
Complete on Download
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip
|
||||||
|
content={helpTooltipContent["completeOnDownload"]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="w-52">
|
||||||
|
<Switch
|
||||||
|
checked={inputs.allowDownloads}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleChange("allowDownloads", checked);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs font-normal text-slate-300">
|
||||||
|
File Suffix
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip content={helpTooltipContent["fileSuffix"]} />
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder={fieldPlaceholders["downloadSuffix"]}
|
||||||
|
className="nopan w-52 text-xs"
|
||||||
|
value={inputs.downloadSuffix ?? ""}
|
||||||
|
onChange={(event) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleChange("downloadSuffix", event.target.value);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Separator />
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs text-slate-300">
|
||||||
|
2FA Verification URL
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip
|
||||||
|
content={helpTooltipContent["totpVerificationUrl"]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<AutoResizingTextarea
|
||||||
|
onChange={(event) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleChange("totpVerificationUrl", event.target.value);
|
||||||
|
}}
|
||||||
|
value={inputs.totpVerificationUrl ?? ""}
|
||||||
|
placeholder={fieldPlaceholders["totpVerificationUrl"]}
|
||||||
|
className="nopan text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Label className="text-xs text-slate-300">
|
||||||
|
2FA Identifier
|
||||||
|
</Label>
|
||||||
|
<HelpTooltip
|
||||||
|
content={helpTooltipContent["totpIdentifier"]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<AutoResizingTextarea
|
||||||
|
onChange={(event) => {
|
||||||
|
if (!editable) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handleChange("totpIdentifier", event.target.value);
|
||||||
|
}}
|
||||||
|
value={inputs.totpIdentifier ?? ""}
|
||||||
|
placeholder={fieldPlaceholders["totpIdentifier"]}
|
||||||
|
className="nopan text-xs"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AccordionContent>
|
||||||
|
</AccordionItem>
|
||||||
|
</Accordion>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
import { cn } from "@/util/utils";
|
|
||||||
import { TaskNodeDisplayMode } from "./types";
|
|
||||||
|
|
||||||
type Props = {
|
|
||||||
value: TaskNodeDisplayMode;
|
|
||||||
onChange: (mode: TaskNodeDisplayMode) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
function TaskNodeDisplayModeSwitch({ value, onChange }: Props) {
|
|
||||||
return (
|
|
||||||
<div className="flex w-fit gap-1 rounded-sm border border-slate-700 p-2">
|
|
||||||
<div
|
|
||||||
className={cn("cursor-pointer rounded-sm p-2 hover:bg-slate-700", {
|
|
||||||
"bg-slate-700": value === "basic",
|
|
||||||
})}
|
|
||||||
onClick={() => {
|
|
||||||
onChange("basic");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Basic
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
className={cn("cursor-pointer rounded-sm p-2 hover:bg-slate-700", {
|
|
||||||
"bg-slate-700": value === "advanced",
|
|
||||||
})}
|
|
||||||
onClick={() => {
|
|
||||||
onChange("advanced");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Advanced
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export { TaskNodeDisplayModeSwitch };
|
|
||||||
@@ -74,6 +74,9 @@ export const helpTooltipContent = {
|
|||||||
"If you have an internal system for storing TOTP codes, link the endpoint here.",
|
"If you have an internal system for storing TOTP codes, link the endpoint here.",
|
||||||
totpIdentifier:
|
totpIdentifier:
|
||||||
"If you are running multiple tasks or workflows at once, you will need to give the task an identifier to know that this TOTP goes with this task.",
|
"If you are running multiple tasks or workflows at once, you will need to give the task an identifier to know that this TOTP goes with this task.",
|
||||||
|
continueOnFailure:
|
||||||
|
"Allow the workflow to continue if it encounters a failure.",
|
||||||
|
cacheActions: "Cache the actions of this task.",
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export const fieldPlaceholders = {
|
export const fieldPlaceholders = {
|
||||||
|
|||||||
Reference in New Issue
Block a user