diff --git a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterEditPanel.tsx b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterEditPanel.tsx index 45ca385c..64834fac 100644 --- a/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterEditPanel.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/panels/WorkflowParameterEditPanel.tsx @@ -50,6 +50,36 @@ const workflowParameterTypeOptions = [ { label: "JSON", value: WorkflowParameterValueType.JSON }, ]; +type CredentialDataType = "password" | "secret" | "creditCard"; +type CredentialSource = "bitwarden" | "skyvern" | "onepassword" | "azurevault"; + +// Determine available sources based on credential data type +function getAvailableSourcesForDataType( + dataType: CredentialDataType, + isCloud: boolean, +): Array<{ value: CredentialSource; label: string }> { + switch (dataType) { + case "password": + return [ + ...(isCloud ? [{ value: "skyvern" as const, label: "Skyvern" }] : []), + { value: "bitwarden" as const, label: "Bitwarden" }, + { value: "onepassword" as const, label: "1Password" }, + { value: "azurevault" as const, label: "Azure Key Vault" }, + ]; + case "secret": + return [ + ...(isCloud ? [{ value: "skyvern" as const, label: "Skyvern" }] : []), + { value: "bitwarden" as const, label: "Bitwarden" }, + ]; + case "creditCard": + return [ + ...(isCloud ? [{ value: "skyvern" as const, label: "Skyvern" }] : []), + { value: "bitwarden" as const, label: "Bitwarden" }, + { value: "onepassword" as const, label: "1Password" }, + ]; + } +} + function header(type: WorkflowEditorParameterType, isEdit: boolean) { const prefix = isEdit ? "Edit" : "Add"; if (type === "workflow") { @@ -58,15 +88,38 @@ function header(type: WorkflowEditorParameterType, isEdit: boolean) { if (type === "credential") { return `${prefix} Credential Parameter`; } - if (type === "secret") { - return `${prefix} Secret Parameter`; - } - if (type === "creditCardData") { - return `${prefix} Credit Card Parameter`; - } return `${prefix} Context Parameter`; } +// Helper to detect initial credential data type from existing parameter +function detectInitialCredentialDataType( + initialValues: ParametersState[number] | undefined, +): CredentialDataType { + if (!initialValues) return "password"; + if (initialValues.parameterType === "secret") return "secret"; + if (initialValues.parameterType === "creditCardData") return "creditCard"; + return "password"; +} + +// Helper to detect initial credential source from existing parameter +function detectInitialCredentialSource( + initialValues: ParametersState[number] | undefined, +): CredentialSource { + if (!initialValues) return "bitwarden"; + + if (initialValues.parameterType === "secret") return "bitwarden"; + if (initialValues.parameterType === "creditCardData") return "bitwarden"; + if (initialValues.parameterType === "onepassword") return "onepassword"; + + if (initialValues.parameterType === "credential") { + if (parameterIsSkyvernCredential(initialValues)) return "skyvern"; + if (parameterIsBitwardenCredential(initialValues)) return "bitwarden"; + if (parameterIsAzureVaultCredential(initialValues)) return "azurevault"; + } + + return "bitwarden"; +} + function WorkflowParameterEditPanel({ type, onClose, @@ -83,6 +136,8 @@ function WorkflowParameterEditPanel({ const isEditMode = !!initialValues; const [key, setKey] = useState(initialValues?.key ?? ""); const hasWhitespace = /\s/.test(key); + + // Detect initial values for backward compatibility const isBitwardenCredential = initialValues?.parameterType === "credential" && parameterIsBitwardenCredential(initialValues); @@ -95,17 +150,16 @@ function WorkflowParameterEditPanel({ const isAzureVaultCredential = initialValues?.parameterType === "credential" && parameterIsAzureVaultCredential(initialValues); - const [credentialType, setCredentialType] = useState< - "bitwarden" | "skyvern" | "onepassword" | "azurevault" - >( - isBitwardenCredential - ? "bitwarden" - : isOnePasswordCredential - ? "onepassword" - : isAzureVaultCredential - ? "azurevault" - : "skyvern", + + // New unified credential state + const [credentialDataType, setCredentialDataType] = + useState( + detectInitialCredentialDataType(initialValues), + ); + const [credentialSource, setCredentialSource] = useState( + detectInitialCredentialSource(initialValues), ); + const [urlParameterKey, setUrlParameterKey] = useState( isBitwardenCredential ? initialValues?.urlParameterKey ?? "" : "", ); @@ -191,6 +245,43 @@ function WorkflowParameterEditPanel({ isAzureVaultCredential ? initialValues.totpSecretKey ?? "" : "", ); + // Handle credential data type change - reset source to first available + const handleCredentialDataTypeChange = (newDataType: CredentialDataType) => { + setCredentialDataType(newDataType); + const availableSources = getAvailableSourcesForDataType( + newDataType, + isCloud, + ); + if (!availableSources.find((s) => s.value === credentialSource)) { + setCredentialSource(availableSources[0]?.value ?? "bitwarden"); + } + }; + + const availableSources = getAvailableSourcesForDataType( + credentialDataType, + isCloud, + ); + + // Determine what fields to show based on credential data type and source + const showBitwardenPasswordFields = + type === "credential" && + credentialDataType === "password" && + credentialSource === "bitwarden"; + const showBitwardenSecretFields = + type === "credential" && + credentialDataType === "secret" && + credentialSource === "bitwarden"; + const showBitwardenCreditCardFields = + type === "credential" && + credentialDataType === "creditCard" && + credentialSource === "bitwarden"; + const showOnePasswordFields = + type === "credential" && credentialSource === "onepassword"; + const showAzureVaultFields = + type === "credential" && credentialSource === "azurevault"; + const showSkyvernCredentialSelector = + type === "credential" && credentialSource === "skyvern" && isCloud; + return ( @@ -301,40 +392,68 @@ function WorkflowParameterEditPanel({ )} + + {/* Credential Parameter - Unified Flow */} {type === "credential" && ( <> + {/* Step 1: Credential Type */}
- +
+ + +
+
+ + {/* Step 2: Source */} +
+
+ + +
+
)} - {type === "credential" && credentialType === "bitwarden" && ( + + {/* Bitwarden Password Fields */} + {showBitwardenPasswordFields && ( <>
@@ -351,9 +470,9 @@ function WorkflowParameterEditPanel({
- +
- - + +
)} - {type === "credential" && credentialType === "onepassword" && ( + + {/* Bitwarden Secret Fields */} + {showBitwardenSecretFields && ( <>
- - + + +
+ setBitwardenCollectionId(e.target.value)} + /> +
+
+
+ + +
+ setIdentityKey(e.target.value)} + /> +
+
+
+ + +
+ setIdentityFields(e.target.value)} + placeholder="field1, field2, field3" + /> +
+ + )} + + {/* Bitwarden Credit Card Fields */} + {showBitwardenCreditCardFields && ( + <> +
+
+ + +
+ setBitwardenCollectionId(e.target.value)} + /> +
+
+
+ + +
+ + setSensitiveInformationItemId(e.target.value) + } + /> +
+ + )} + + {/* 1Password Fields */} + {showOnePasswordFields && ( + <> +
+
+ +
- - + +
setOpItemId(e.target.value)} />
-
-
- Credit Cards: Due to a 1Password limitation, add the - expiration date as a separate text field named “Expire Date” - in the format MM/YYYY (e.g. 09/2027). + {credentialDataType === "creditCard" && ( +
+
+ Credit Cards: Due to a 1Password limitation, add the + expiration date as a separate text field named "Expire Date" + in the format MM/YYYY (e.g. 09/2027). +
-
+ )} )} - {type === "credential" && credentialType === "azurevault" && ( + + {/* Azure Key Vault Fields */} + {showAzureVaultFields && ( <>
- - + +
- - + +
- - + +
@@ -452,6 +662,23 @@ function WorkflowParameterEditPanel({
)} + + {/* Skyvern Managed Credential Selector */} + {showSkyvernCredentialSelector && ( +
+
+ + +
+ setCredentialId(value)} + /> +
+ )} + {type === "context" && (
@@ -461,67 +688,7 @@ function WorkflowParameterEditPanel({ />
)} - {type === "secret" && ( - <> -
- - setIdentityKey(e.target.value)} - /> -
-
- - setIdentityFields(e.target.value)} - /> -
-
- - setBitwardenCollectionId(e.target.value)} - /> -
- - )} - {type === "creditCardData" && ( - <> -
- - setBitwardenCollectionId(e.target.value)} - /> -
-
- - - setSensitiveInformationItemId(e.target.value) - } - /> -
- - )} - { - // temporarily cloud only - type === "credential" && - credentialType === "skyvern" && - isCloud && ( -
- - setCredentialId(value)} - /> -
- ) - } +