Unify parameter creation dialog with credential type option (SKY-7768) (#4621)

This commit is contained in:
Celal Zamanoglu
2026-02-03 23:45:52 +03:00
committed by GitHub
parent 10fa135aa1
commit 7e35514924
2 changed files with 154 additions and 122 deletions

View File

@@ -42,6 +42,7 @@ type Props = {
const workflowParameterTypeOptions = [
{ label: "string", value: WorkflowParameterValueType.String },
{ label: "credential", value: "credential" },
{ label: "float", value: WorkflowParameterValueType.Float },
{ label: "integer", value: WorkflowParameterValueType.Integer },
{ label: "boolean", value: WorkflowParameterValueType.Boolean },
@@ -52,6 +53,11 @@ const workflowParameterTypeOptions = [
type CredentialDataType = "password" | "secret" | "creditCard";
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
function getAvailableSourcesForDataType(
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";
if (type === "workflow" && !isEdit) {
// Unified add mode
return `${prefix} Parameter`;
}
if (type === "workflow") {
return `${prefix} Input Parameter`;
}
if (type === "credential") {
if (type === "credential" || (!isEdit && isCredentialSelected)) {
return `${prefix} Credential Parameter`;
}
return `${prefix} Context Parameter`;
@@ -103,8 +117,9 @@ function detectInitialCredentialDataType(
// Helper to detect initial credential source from existing parameter
function detectInitialCredentialSource(
initialValues: ParametersState[number] | undefined,
isCloud: boolean,
): CredentialSource {
if (!initialValues) return "bitwarden";
if (!initialValues) return isCloud ? "skyvern" : "bitwarden";
if (initialValues.parameterType === "secret") return "bitwarden";
if (initialValues.parameterType === "creditCardData") return "bitwarden";
@@ -116,7 +131,7 @@ function detectInitialCredentialSource(
if (parameterIsAzureVaultCredential(initialValues)) return "azurevault";
}
return "bitwarden";
return isCloud ? "skyvern" : "bitwarden";
}
function WorkflowParameterEditPanel({
@@ -156,7 +171,7 @@ function WorkflowParameterEditPanel({
detectInitialCredentialDataType(initialValues),
);
const [credentialSource, setCredentialSource] = useState<CredentialSource>(
detectInitialCredentialSource(initialValues),
detectInitialCredentialSource(initialValues, isCloud),
);
const [urlParameterKey, setUrlParameterKey] = useState(
@@ -172,12 +187,13 @@ function WorkflowParameterEditPanel({
? initialValues?.collectionId ?? ""
: "",
);
const [parameterType, setParameterType] =
useState<WorkflowParameterValueType>(
initialValues?.parameterType === "workflow"
const [parameterType, setParameterType] = useState<ParameterTypeSelection>(
type === "credential"
? "credential"
: initialValues?.parameterType === "workflow"
? initialValues.dataType
: "string",
);
);
const [defaultValueState, setDefaultValueState] = useState<{
hasDefaultValue: boolean;
@@ -261,32 +277,38 @@ function WorkflowParameterEditPanel({
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
const showBitwardenPasswordFields =
type === "credential" &&
showCredentialFields &&
credentialDataType === "password" &&
credentialSource === "bitwarden";
const showBitwardenSecretFields =
type === "credential" &&
showCredentialFields &&
credentialDataType === "secret" &&
credentialSource === "bitwarden";
const showBitwardenCreditCardFields =
type === "credential" &&
showCredentialFields &&
credentialDataType === "creditCard" &&
credentialSource === "bitwarden";
const showOnePasswordFields =
type === "credential" && credentialSource === "onepassword";
showCredentialFields && credentialSource === "onepassword";
const showAzureVaultFields =
type === "credential" && credentialSource === "azurevault";
showCredentialFields && credentialSource === "azurevault";
const showSkyvernCredentialSelector =
type === "credential" && credentialSource === "skyvern" && isCloud;
showCredentialFields && credentialSource === "skyvern" && isCloud;
return (
<ScrollArea>
<ScrollAreaViewport className="max-h-[500px]">
<div className="space-y-4 p-1 px-4">
<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} />
</header>
<div className="space-y-1">
@@ -312,15 +334,42 @@ function WorkflowParameterEditPanel({
<Select
value={parameterType}
onValueChange={(value) => {
setParameterType(value as WorkflowParameterValueType);
setDefaultValueState((state) => {
return {
...state,
defaultValue: getDefaultValueForParameterType(
value as WorkflowParameterValueType,
),
};
});
const newValue = value as ParameterTypeSelection;
const wasCredential = parameterType === "credential";
const isNowCredential = newValue === "credential";
setParameterType(newValue);
// 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">
@@ -328,72 +377,84 @@ function WorkflowParameterEditPanel({
</SelectTrigger>
<SelectContent>
<SelectGroup>
{workflowParameterTypeOptions.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
{workflowParameterTypeOptions
.filter((option) => {
// In edit mode, don't show credential option
if (isEditMode && option.value === "credential") {
return false;
}
return true;
})
.map((option) => (
<SelectItem key={option.value} value={option.value}>
{option.label}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</div>
<div className="space-y-4">
<div className="flex items-center gap-2">
<Checkbox
checked={defaultValueState.hasDefaultValue}
onCheckedChange={(checked) => {
if (!checked) {
{/* Default value section - only for non-credential types */}
{!isCredentialSelected && (
<div className="space-y-4">
<div className="flex items-center gap-2">
<Checkbox
checked={defaultValueState.hasDefaultValue}
onCheckedChange={(checked) => {
if (!checked) {
setDefaultValueState({
hasDefaultValue: false,
defaultValue: null,
});
return;
}
setDefaultValueState({
hasDefaultValue: false,
defaultValue: null,
hasDefaultValue: true,
defaultValue: getDefaultValueForParameterType(
parameterType as WorkflowParameterValueType,
),
});
return;
}
setDefaultValueState({
hasDefaultValue: true,
defaultValue:
getDefaultValueForParameterType(parameterType),
});
}}
/>
<Label className="text-xs text-slate-300">
Use Default Value
</Label>
</div>
{defaultValueState.hasDefaultValue && (
<WorkflowParameterInput
onChange={(value) => {
if (
parameterType === "file_url" &&
typeof value === "object" &&
value &&
"s3uri" in value
) {
}}
/>
<Label className="text-xs text-slate-300">
Use Default Value
</Label>
</div>
{defaultValueState.hasDefaultValue && (
<WorkflowParameterInput
onChange={(value) => {
if (
parameterType === "file_url" &&
typeof value === "object" &&
value &&
"s3uri" in value
) {
setDefaultValueState((state) => {
return {
...state,
defaultValue: value.s3uri,
};
});
return;
}
setDefaultValueState((state) => {
return {
...state,
defaultValue: value.s3uri,
defaultValue: value,
};
});
return;
}
setDefaultValueState((state) => {
return {
...state,
defaultValue: value,
};
});
}}
type={parameterType}
value={defaultValueState.defaultValue}
/>
)}
</div>
}}
type={parameterType as WorkflowParameterValueType}
value={defaultValueState.defaultValue}
/>
)}
</div>
)}
</>
)}
{/* Credential Parameter - Unified Flow */}
{type === "credential" && (
{showCredentialFields && (
<>
{/* Step 1: Credential Type */}
<div className="space-y-1">
@@ -717,8 +778,8 @@ function WorkflowParameterEditPanel({
return;
}
// Handle workflow parameters
if (type === "workflow") {
// Handle workflow parameters (non-credential)
if (type === "workflow" && !isCredentialSelected) {
if (
parameterType === "json" &&
typeof defaultValueState.defaultValue === "string"
@@ -763,7 +824,7 @@ function WorkflowParameterEditPanel({
onSave({
key,
parameterType: "workflow",
dataType: parameterType,
dataType: parameterType as WorkflowParameterValueType,
description,
defaultValue: defaultValueState.hasDefaultValue
? defaultValue
@@ -792,7 +853,7 @@ function WorkflowParameterEditPanel({
}
// Handle credential parameters based on type + source combination
if (type === "credential") {
if (type === "credential" || isCredentialSelected) {
// Skyvern managed credentials
if (credentialSource === "skyvern") {
if (!credentialId) {

View File

@@ -4,14 +4,6 @@ import { WorkflowParameterEditPanel } from "./WorkflowParameterEditPanel";
import { MixerVerticalIcon, PlusIcon } from "@radix-ui/react-icons";
import { Button } from "@/components/ui/button";
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 { useWorkflowHasChangesStore } from "@/store/WorkflowHasChangesStore";
import { useWorkflowParametersStore } from "@/store/WorkflowParametersStore";
@@ -106,40 +98,19 @@ function WorkflowParametersPanel({ onMouseDownCapture }: Props) {
prompted to fill them in before running your workflow.
</span>
</header>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button className="w-full">
<PlusIcon className="mr-2 h-6 w-6" />
Add Parameter
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-60">
<DropdownMenuLabel>Add Parameter</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => {
setOperationPanelState({
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>
<Button
className="w-full"
onClick={() => {
setOperationPanelState({
active: true,
operation: "add",
type: WorkflowEditorParameterTypes.Workflow,
});
}}
>
<PlusIcon className="mr-2 h-6 w-6" />
Add Parameter
</Button>
<ScrollArea>
<ScrollAreaViewport className="max-h-96">