Unify parameter creation dialog with credential type option (SKY-7768) (#4621)
This commit is contained in:
@@ -42,6 +42,7 @@ type Props = {
|
|||||||
|
|
||||||
const workflowParameterTypeOptions = [
|
const workflowParameterTypeOptions = [
|
||||||
{ label: "string", value: WorkflowParameterValueType.String },
|
{ label: "string", value: WorkflowParameterValueType.String },
|
||||||
|
{ label: "credential", value: "credential" },
|
||||||
{ label: "float", value: WorkflowParameterValueType.Float },
|
{ label: "float", value: WorkflowParameterValueType.Float },
|
||||||
{ label: "integer", value: WorkflowParameterValueType.Integer },
|
{ label: "integer", value: WorkflowParameterValueType.Integer },
|
||||||
{ label: "boolean", value: WorkflowParameterValueType.Boolean },
|
{ label: "boolean", value: WorkflowParameterValueType.Boolean },
|
||||||
@@ -52,6 +53,11 @@ const workflowParameterTypeOptions = [
|
|||||||
type CredentialDataType = "password" | "secret" | "creditCard";
|
type CredentialDataType = "password" | "secret" | "creditCard";
|
||||||
type CredentialSource = "bitwarden" | "skyvern" | "onepassword" | "azurevault";
|
type CredentialSource = "bitwarden" | "skyvern" | "onepassword" | "azurevault";
|
||||||
|
|
||||||
|
// When selecting from the Value Type dropdown, "credential" is a special value that triggers
|
||||||
|
// credential-specific UI. This is separate from WorkflowParameterValueType which only includes
|
||||||
|
// data types like string, integer, etc.
|
||||||
|
type ParameterTypeSelection = WorkflowParameterValueType | "credential";
|
||||||
|
|
||||||
// Determine available sources based on credential data type
|
// Determine available sources based on credential data type
|
||||||
function getAvailableSourcesForDataType(
|
function getAvailableSourcesForDataType(
|
||||||
dataType: CredentialDataType,
|
dataType: CredentialDataType,
|
||||||
@@ -79,12 +85,20 @@ function getAvailableSourcesForDataType(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function header(type: WorkflowEditorParameterType, isEdit: boolean) {
|
function header(
|
||||||
|
type: WorkflowEditorParameterType,
|
||||||
|
isEdit: boolean,
|
||||||
|
isCredentialSelected: boolean,
|
||||||
|
) {
|
||||||
const prefix = isEdit ? "Edit" : "Add";
|
const prefix = isEdit ? "Edit" : "Add";
|
||||||
|
if (type === "workflow" && !isEdit) {
|
||||||
|
// Unified add mode
|
||||||
|
return `${prefix} Parameter`;
|
||||||
|
}
|
||||||
if (type === "workflow") {
|
if (type === "workflow") {
|
||||||
return `${prefix} Input Parameter`;
|
return `${prefix} Input Parameter`;
|
||||||
}
|
}
|
||||||
if (type === "credential") {
|
if (type === "credential" || (!isEdit && isCredentialSelected)) {
|
||||||
return `${prefix} Credential Parameter`;
|
return `${prefix} Credential Parameter`;
|
||||||
}
|
}
|
||||||
return `${prefix} Context Parameter`;
|
return `${prefix} Context Parameter`;
|
||||||
@@ -103,8 +117,9 @@ function detectInitialCredentialDataType(
|
|||||||
// Helper to detect initial credential source from existing parameter
|
// Helper to detect initial credential source from existing parameter
|
||||||
function detectInitialCredentialSource(
|
function detectInitialCredentialSource(
|
||||||
initialValues: ParametersState[number] | undefined,
|
initialValues: ParametersState[number] | undefined,
|
||||||
|
isCloud: boolean,
|
||||||
): CredentialSource {
|
): CredentialSource {
|
||||||
if (!initialValues) return "bitwarden";
|
if (!initialValues) return isCloud ? "skyvern" : "bitwarden";
|
||||||
|
|
||||||
if (initialValues.parameterType === "secret") return "bitwarden";
|
if (initialValues.parameterType === "secret") return "bitwarden";
|
||||||
if (initialValues.parameterType === "creditCardData") return "bitwarden";
|
if (initialValues.parameterType === "creditCardData") return "bitwarden";
|
||||||
@@ -116,7 +131,7 @@ function detectInitialCredentialSource(
|
|||||||
if (parameterIsAzureVaultCredential(initialValues)) return "azurevault";
|
if (parameterIsAzureVaultCredential(initialValues)) return "azurevault";
|
||||||
}
|
}
|
||||||
|
|
||||||
return "bitwarden";
|
return isCloud ? "skyvern" : "bitwarden";
|
||||||
}
|
}
|
||||||
|
|
||||||
function WorkflowParameterEditPanel({
|
function WorkflowParameterEditPanel({
|
||||||
@@ -156,7 +171,7 @@ function WorkflowParameterEditPanel({
|
|||||||
detectInitialCredentialDataType(initialValues),
|
detectInitialCredentialDataType(initialValues),
|
||||||
);
|
);
|
||||||
const [credentialSource, setCredentialSource] = useState<CredentialSource>(
|
const [credentialSource, setCredentialSource] = useState<CredentialSource>(
|
||||||
detectInitialCredentialSource(initialValues),
|
detectInitialCredentialSource(initialValues, isCloud),
|
||||||
);
|
);
|
||||||
|
|
||||||
const [urlParameterKey, setUrlParameterKey] = useState(
|
const [urlParameterKey, setUrlParameterKey] = useState(
|
||||||
@@ -172,12 +187,13 @@ function WorkflowParameterEditPanel({
|
|||||||
? initialValues?.collectionId ?? ""
|
? initialValues?.collectionId ?? ""
|
||||||
: "",
|
: "",
|
||||||
);
|
);
|
||||||
const [parameterType, setParameterType] =
|
const [parameterType, setParameterType] = useState<ParameterTypeSelection>(
|
||||||
useState<WorkflowParameterValueType>(
|
type === "credential"
|
||||||
initialValues?.parameterType === "workflow"
|
? "credential"
|
||||||
|
: initialValues?.parameterType === "workflow"
|
||||||
? initialValues.dataType
|
? initialValues.dataType
|
||||||
: "string",
|
: "string",
|
||||||
);
|
);
|
||||||
|
|
||||||
const [defaultValueState, setDefaultValueState] = useState<{
|
const [defaultValueState, setDefaultValueState] = useState<{
|
||||||
hasDefaultValue: boolean;
|
hasDefaultValue: boolean;
|
||||||
@@ -261,32 +277,38 @@ function WorkflowParameterEditPanel({
|
|||||||
isCloud,
|
isCloud,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Check if we're in unified add mode and credential is selected
|
||||||
|
const isCredentialSelected = parameterType === "credential";
|
||||||
|
const showCredentialFields =
|
||||||
|
type === "credential" ||
|
||||||
|
(type === "workflow" && !isEditMode && isCredentialSelected);
|
||||||
|
|
||||||
// Determine what fields to show based on credential data type and source
|
// Determine what fields to show based on credential data type and source
|
||||||
const showBitwardenPasswordFields =
|
const showBitwardenPasswordFields =
|
||||||
type === "credential" &&
|
showCredentialFields &&
|
||||||
credentialDataType === "password" &&
|
credentialDataType === "password" &&
|
||||||
credentialSource === "bitwarden";
|
credentialSource === "bitwarden";
|
||||||
const showBitwardenSecretFields =
|
const showBitwardenSecretFields =
|
||||||
type === "credential" &&
|
showCredentialFields &&
|
||||||
credentialDataType === "secret" &&
|
credentialDataType === "secret" &&
|
||||||
credentialSource === "bitwarden";
|
credentialSource === "bitwarden";
|
||||||
const showBitwardenCreditCardFields =
|
const showBitwardenCreditCardFields =
|
||||||
type === "credential" &&
|
showCredentialFields &&
|
||||||
credentialDataType === "creditCard" &&
|
credentialDataType === "creditCard" &&
|
||||||
credentialSource === "bitwarden";
|
credentialSource === "bitwarden";
|
||||||
const showOnePasswordFields =
|
const showOnePasswordFields =
|
||||||
type === "credential" && credentialSource === "onepassword";
|
showCredentialFields && credentialSource === "onepassword";
|
||||||
const showAzureVaultFields =
|
const showAzureVaultFields =
|
||||||
type === "credential" && credentialSource === "azurevault";
|
showCredentialFields && credentialSource === "azurevault";
|
||||||
const showSkyvernCredentialSelector =
|
const showSkyvernCredentialSelector =
|
||||||
type === "credential" && credentialSource === "skyvern" && isCloud;
|
showCredentialFields && credentialSource === "skyvern" && isCloud;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollArea>
|
<ScrollArea>
|
||||||
<ScrollAreaViewport className="max-h-[500px]">
|
<ScrollAreaViewport className="max-h-[500px]">
|
||||||
<div className="space-y-4 p-1 px-4">
|
<div className="space-y-4 p-1 px-4">
|
||||||
<header className="flex items-center justify-between">
|
<header className="flex items-center justify-between">
|
||||||
<span>{header(type, isEditMode)}</span>
|
<span>{header(type, isEditMode, isCredentialSelected)}</span>
|
||||||
<Cross2Icon className="h-6 w-6 cursor-pointer" onClick={onClose} />
|
<Cross2Icon className="h-6 w-6 cursor-pointer" onClick={onClose} />
|
||||||
</header>
|
</header>
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
@@ -312,15 +334,42 @@ function WorkflowParameterEditPanel({
|
|||||||
<Select
|
<Select
|
||||||
value={parameterType}
|
value={parameterType}
|
||||||
onValueChange={(value) => {
|
onValueChange={(value) => {
|
||||||
setParameterType(value as WorkflowParameterValueType);
|
const newValue = value as ParameterTypeSelection;
|
||||||
setDefaultValueState((state) => {
|
const wasCredential = parameterType === "credential";
|
||||||
return {
|
const isNowCredential = newValue === "credential";
|
||||||
...state,
|
|
||||||
defaultValue: getDefaultValueForParameterType(
|
setParameterType(newValue);
|
||||||
value as WorkflowParameterValueType,
|
|
||||||
),
|
// Clear credential-specific state when switching away from credential
|
||||||
};
|
// to prevent stale data if user switches back
|
||||||
});
|
if (wasCredential && !isNowCredential) {
|
||||||
|
setCredentialId("");
|
||||||
|
setCredentialDataType("password");
|
||||||
|
setCredentialSource(isCloud ? "skyvern" : "bitwarden");
|
||||||
|
setBitwardenLoginCredentialItemId("");
|
||||||
|
setBitwardenCollectionId("");
|
||||||
|
setUrlParameterKey("");
|
||||||
|
setIdentityKey("");
|
||||||
|
setIdentityFields("");
|
||||||
|
setSensitiveInformationItemId("");
|
||||||
|
setOpVaultId("");
|
||||||
|
setOpItemId("");
|
||||||
|
setAzureVaultName("");
|
||||||
|
setAzureUsernameKey("");
|
||||||
|
setAzurePasswordKey("");
|
||||||
|
setAzureTotpKey("");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isNowCredential) {
|
||||||
|
setDefaultValueState((state) => {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
defaultValue: getDefaultValueForParameterType(
|
||||||
|
newValue as WorkflowParameterValueType,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-full">
|
<SelectTrigger className="w-full">
|
||||||
@@ -328,72 +377,84 @@ function WorkflowParameterEditPanel({
|
|||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
<SelectGroup>
|
<SelectGroup>
|
||||||
{workflowParameterTypeOptions.map((option) => (
|
{workflowParameterTypeOptions
|
||||||
<SelectItem key={option.value} value={option.value}>
|
.filter((option) => {
|
||||||
{option.label}
|
// In edit mode, don't show credential option
|
||||||
</SelectItem>
|
if (isEditMode && option.value === "credential") {
|
||||||
))}
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((option) => (
|
||||||
|
<SelectItem key={option.value} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
</SelectGroup>
|
</SelectGroup>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-4">
|
{/* Default value section - only for non-credential types */}
|
||||||
<div className="flex items-center gap-2">
|
{!isCredentialSelected && (
|
||||||
<Checkbox
|
<div className="space-y-4">
|
||||||
checked={defaultValueState.hasDefaultValue}
|
<div className="flex items-center gap-2">
|
||||||
onCheckedChange={(checked) => {
|
<Checkbox
|
||||||
if (!checked) {
|
checked={defaultValueState.hasDefaultValue}
|
||||||
|
onCheckedChange={(checked) => {
|
||||||
|
if (!checked) {
|
||||||
|
setDefaultValueState({
|
||||||
|
hasDefaultValue: false,
|
||||||
|
defaultValue: null,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
setDefaultValueState({
|
setDefaultValueState({
|
||||||
hasDefaultValue: false,
|
hasDefaultValue: true,
|
||||||
defaultValue: null,
|
defaultValue: getDefaultValueForParameterType(
|
||||||
|
parameterType as WorkflowParameterValueType,
|
||||||
|
),
|
||||||
});
|
});
|
||||||
return;
|
}}
|
||||||
}
|
/>
|
||||||
setDefaultValueState({
|
<Label className="text-xs text-slate-300">
|
||||||
hasDefaultValue: true,
|
Use Default Value
|
||||||
defaultValue:
|
</Label>
|
||||||
getDefaultValueForParameterType(parameterType),
|
</div>
|
||||||
});
|
{defaultValueState.hasDefaultValue && (
|
||||||
}}
|
<WorkflowParameterInput
|
||||||
/>
|
onChange={(value) => {
|
||||||
<Label className="text-xs text-slate-300">
|
if (
|
||||||
Use Default Value
|
parameterType === "file_url" &&
|
||||||
</Label>
|
typeof value === "object" &&
|
||||||
</div>
|
value &&
|
||||||
{defaultValueState.hasDefaultValue && (
|
"s3uri" in value
|
||||||
<WorkflowParameterInput
|
) {
|
||||||
onChange={(value) => {
|
setDefaultValueState((state) => {
|
||||||
if (
|
return {
|
||||||
parameterType === "file_url" &&
|
...state,
|
||||||
typeof value === "object" &&
|
defaultValue: value.s3uri,
|
||||||
value &&
|
};
|
||||||
"s3uri" in value
|
});
|
||||||
) {
|
return;
|
||||||
|
}
|
||||||
setDefaultValueState((state) => {
|
setDefaultValueState((state) => {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
defaultValue: value.s3uri,
|
defaultValue: value,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return;
|
}}
|
||||||
}
|
type={parameterType as WorkflowParameterValueType}
|
||||||
setDefaultValueState((state) => {
|
value={defaultValueState.defaultValue}
|
||||||
return {
|
/>
|
||||||
...state,
|
)}
|
||||||
defaultValue: value,
|
</div>
|
||||||
};
|
)}
|
||||||
});
|
|
||||||
}}
|
|
||||||
type={parameterType}
|
|
||||||
value={defaultValueState.defaultValue}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Credential Parameter - Unified Flow */}
|
{/* Credential Parameter - Unified Flow */}
|
||||||
{type === "credential" && (
|
{showCredentialFields && (
|
||||||
<>
|
<>
|
||||||
{/* Step 1: Credential Type */}
|
{/* Step 1: Credential Type */}
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
@@ -717,8 +778,8 @@ function WorkflowParameterEditPanel({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle workflow parameters
|
// Handle workflow parameters (non-credential)
|
||||||
if (type === "workflow") {
|
if (type === "workflow" && !isCredentialSelected) {
|
||||||
if (
|
if (
|
||||||
parameterType === "json" &&
|
parameterType === "json" &&
|
||||||
typeof defaultValueState.defaultValue === "string"
|
typeof defaultValueState.defaultValue === "string"
|
||||||
@@ -763,7 +824,7 @@ function WorkflowParameterEditPanel({
|
|||||||
onSave({
|
onSave({
|
||||||
key,
|
key,
|
||||||
parameterType: "workflow",
|
parameterType: "workflow",
|
||||||
dataType: parameterType,
|
dataType: parameterType as WorkflowParameterValueType,
|
||||||
description,
|
description,
|
||||||
defaultValue: defaultValueState.hasDefaultValue
|
defaultValue: defaultValueState.hasDefaultValue
|
||||||
? defaultValue
|
? defaultValue
|
||||||
@@ -792,7 +853,7 @@ function WorkflowParameterEditPanel({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Handle credential parameters based on type + source combination
|
// Handle credential parameters based on type + source combination
|
||||||
if (type === "credential") {
|
if (type === "credential" || isCredentialSelected) {
|
||||||
// Skyvern managed credentials
|
// Skyvern managed credentials
|
||||||
if (credentialSource === "skyvern") {
|
if (credentialSource === "skyvern") {
|
||||||
if (!credentialId) {
|
if (!credentialId) {
|
||||||
|
|||||||
@@ -4,14 +4,6 @@ import { WorkflowParameterEditPanel } from "./WorkflowParameterEditPanel";
|
|||||||
import { MixerVerticalIcon, PlusIcon } from "@radix-ui/react-icons";
|
import { MixerVerticalIcon, PlusIcon } from "@radix-ui/react-icons";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { GarbageIcon } from "@/components/icons/GarbageIcon";
|
import { GarbageIcon } from "@/components/icons/GarbageIcon";
|
||||||
import {
|
|
||||||
DropdownMenu,
|
|
||||||
DropdownMenuContent,
|
|
||||||
DropdownMenuItem,
|
|
||||||
DropdownMenuLabel,
|
|
||||||
DropdownMenuSeparator,
|
|
||||||
DropdownMenuTrigger,
|
|
||||||
} from "@/components/ui/dropdown-menu";
|
|
||||||
import { useNodes, useReactFlow } from "@xyflow/react";
|
import { useNodes, useReactFlow } from "@xyflow/react";
|
||||||
import { useWorkflowHasChangesStore } from "@/store/WorkflowHasChangesStore";
|
import { useWorkflowHasChangesStore } from "@/store/WorkflowHasChangesStore";
|
||||||
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
|
||||||
@@ -106,40 +98,19 @@ function WorkflowParametersPanel({ onMouseDownCapture }: Props) {
|
|||||||
prompted to fill them in before running your workflow.
|
prompted to fill them in before running your workflow.
|
||||||
</span>
|
</span>
|
||||||
</header>
|
</header>
|
||||||
<DropdownMenu>
|
<Button
|
||||||
<DropdownMenuTrigger asChild>
|
className="w-full"
|
||||||
<Button className="w-full">
|
onClick={() => {
|
||||||
<PlusIcon className="mr-2 h-6 w-6" />
|
setOperationPanelState({
|
||||||
Add Parameter
|
active: true,
|
||||||
</Button>
|
operation: "add",
|
||||||
</DropdownMenuTrigger>
|
type: WorkflowEditorParameterTypes.Workflow,
|
||||||
<DropdownMenuContent className="w-60">
|
});
|
||||||
<DropdownMenuLabel>Add Parameter</DropdownMenuLabel>
|
}}
|
||||||
<DropdownMenuSeparator />
|
>
|
||||||
<DropdownMenuItem
|
<PlusIcon className="mr-2 h-6 w-6" />
|
||||||
onClick={() => {
|
Add Parameter
|
||||||
setOperationPanelState({
|
</Button>
|
||||||
active: true,
|
|
||||||
operation: "add",
|
|
||||||
type: WorkflowEditorParameterTypes.Workflow,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Input Parameter
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => {
|
|
||||||
setOperationPanelState({
|
|
||||||
active: true,
|
|
||||||
operation: "add",
|
|
||||||
type: WorkflowEditorParameterTypes.Credential,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Credential Parameter
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
|
|
||||||
<ScrollArea>
|
<ScrollArea>
|
||||||
<ScrollAreaViewport className="max-h-96">
|
<ScrollAreaViewport className="max-h-96">
|
||||||
|
|||||||
Reference in New Issue
Block a user