diff --git a/web/app/components/workflow/nodes/_base/components/form-input-item.tsx b/web/app/components/workflow/nodes/_base/components/form-input-item.tsx index fa60e8a28b..53366542de 100644 --- a/web/app/components/workflow/nodes/_base/components/form-input-item.tsx +++ b/web/app/components/workflow/nodes/_base/components/form-input-item.tsx @@ -7,6 +7,7 @@ import { useLanguage } from '@/app/components/header/account-setting/model-provi import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { VarType } from '@/app/components/workflow/types' import { useFetchDynamicOptions } from '@/service/use-plugins' +import { useTriggerPluginDynamicOptions } from '@/service/use-triggers' import type { ValueSelector, Var } from '@/app/components/workflow/types' import FormInputTypeSwitch from './form-input-type-switch' @@ -50,8 +51,8 @@ const FormInputItem: FC = ({ providerType, }) => { const language = useLanguage() - const [dynamicOptions, setDynamicOptions] = useState(null) - const [isLoadingOptions, setIsLoadingOptions] = useState(false) + const [toolsOptions, setToolsOptions] = useState(null) + const [isLoadingToolsOptions, setIsLoadingToolsOptions] = useState(false) const { placeholder, @@ -136,7 +137,7 @@ const FormInputItem: FC = ({ return VarKindType.mixed } - // Fetch dynamic options hook + // Fetch dynamic options hook for tools const { mutateAsync: fetchDynamicOptions } = useFetchDynamicOptions( currentProvider?.plugin_id || '', currentProvider?.name || '', @@ -146,27 +147,48 @@ const FormInputItem: FC = ({ extraParams, ) - // Fetch dynamic options when component mounts or dependencies change + // Fetch dynamic options hook for triggers + const { data: triggerDynamicOptions, isLoading: isTriggerOptionsLoading } = useTriggerPluginDynamicOptions({ + plugin_id: currentProvider?.plugin_id || '', + provider: currentProvider?.name || '', + action: currentResource?.name || '', + parameter: variable || '', + extra: extraParams, + }, isDynamicSelect && providerType === 'trigger' && !!currentResource && !!currentProvider) + + // Computed values for dynamic options (unified for triggers and tools) + const dynamicOptions = providerType === 'trigger' ? triggerDynamicOptions?.options || [] : toolsOptions + const isLoadingOptions = providerType === 'trigger' ? isTriggerOptionsLoading : isLoadingToolsOptions + + // Fetch dynamic options for tools only (triggers use hook directly) useEffect(() => { - const fetchOptions = async () => { - if (isDynamicSelect && currentResource && currentProvider) { - setIsLoadingOptions(true) + const fetchToolOptions = async () => { + if (isDynamicSelect && currentResource && currentProvider && providerType === 'tool') { + setIsLoadingToolsOptions(true) try { const data = await fetchDynamicOptions() - setDynamicOptions(data?.options || []) + setToolsOptions(data?.options || []) } catch (error) { console.error('Failed to fetch dynamic options:', error) - setDynamicOptions([]) + setToolsOptions([]) } finally { - setIsLoadingOptions(false) + setIsLoadingToolsOptions(false) } } } - fetchOptions() - }, [isDynamicSelect, currentResource?.name, currentProvider?.name, variable, extraParams]) + fetchToolOptions() + }, [ + isDynamicSelect, + currentResource?.name, + currentProvider?.name, + variable, + extraParams, + providerType, + fetchDynamicOptions, + ]) const handleTypeChange = (newType: string) => { if (newType === VarKindType.variable) { diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx index d38a0250d3..8056797260 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx @@ -49,7 +49,7 @@ import { BlockEnum, type Node, NodeRunningStatus } from '@/app/components/workfl import { useStore as useAppStore } from '@/app/components/app/store' import { useStore } from '@/app/components/workflow/store' import Tab, { TabType } from './tab' -import { useAllTriggerPlugins, useTriggerSubscriptions } from '@/service/use-triggers' +import { useAllTriggerPlugins } from '@/service/use-triggers' import AuthMethodSelector from '@/app/components/workflow/nodes/trigger-plugin/components/auth-method-selector' import LastRun from './last-run' import useLastRun from './last-run/use-last-run' @@ -238,23 +238,8 @@ const BasePanel: FC = ({ return buildInTools.find(item => canFindTool(item.id, data.provider_id)) }, [buildInTools, data.provider_id]) - // For trigger plugins, check if they have existing subscriptions (authenticated) - const triggerProvider = useMemo(() => { - if (data.type === BlockEnum.TriggerPlugin) { - if (data.provider_name) - return data.provider_name - return data.provider_id || '' - } - return '' - }, [data.type, data.provider_id, data.provider_name]) - - const { data: triggerSubscriptions = [] } = useTriggerSubscriptions( - triggerProvider, - data.type === BlockEnum.TriggerPlugin && !!triggerProvider, - ) - + // For trigger plugins, get basic provider info const { data: triggerProviders = [] } = useAllTriggerPlugins() - const currentTriggerProvider = useMemo(() => { if (data.type !== BlockEnum.TriggerPlugin || !data.provider_id || !data.provider_name) return undefined @@ -271,26 +256,20 @@ const BasePanel: FC = ({ return methods }, [currentTriggerProvider]) - const isTriggerAuthenticated = useMemo(() => { - if (data.type !== BlockEnum.TriggerPlugin) return true - if (!triggerSubscriptions.length) return false + // Simplified: Always show auth selector for trigger plugins + const shouldShowTriggerAuthSelector = useMemo(() => { + return data.type === BlockEnum.TriggerPlugin && currentTriggerProvider && supportedAuthMethods.length > 0 + }, [data.type, currentTriggerProvider, supportedAuthMethods.length]) - const subscription = triggerSubscriptions[0] - return subscription.credential_type !== 'unauthorized' - }, [data.type, triggerSubscriptions]) + // Simplified: Always show tab for trigger plugins + const shouldShowTriggerTab = useMemo(() => { + return data.type === BlockEnum.TriggerPlugin && currentTriggerProvider + }, [data.type, currentTriggerProvider]) - const shouldShowAuthSelector = useMemo(() => { - return data.type === BlockEnum.TriggerPlugin - && !isTriggerAuthenticated - && supportedAuthMethods.length > 0 - && !!currentTriggerProvider - }, [data.type, isTriggerAuthenticated, supportedAuthMethods.length, currentTriggerProvider]) - - // Unified check for any node that needs authentication UI - const needsAuth = useMemo(() => { + // Unified check for tool authentication UI + const needsToolAuth = useMemo(() => { return (data.type === BlockEnum.Tool && currCollection?.allow_delete) - || (data.type === BlockEnum.TriggerPlugin && isTriggerAuthenticated) - }, [data.type, currCollection?.allow_delete, isTriggerAuthenticated]) + }, [data.type, currCollection?.allow_delete]) const handleAuthorizationItemClick = useCallback((credential_id: string) => { handleNodeDataUpdateWithSyncDraft({ @@ -433,7 +412,7 @@ const BasePanel: FC = ({ /> { - needsAuth && data.type === BlockEnum.Tool && currCollection?.allow_delete && ( + needsToolAuth && ( = ({ ) } { - needsAuth && data.type !== BlockEnum.Tool && ( + shouldShowTriggerAuthSelector && ( + + ) + } + { + shouldShowTriggerTab && (
= ({ ) } { - shouldShowAuthSelector && ( - - ) - } - { - !needsAuth && data.type !== BlockEnum.TriggerPlugin && ( + !needsToolAuth && data.type !== BlockEnum.TriggerPlugin && (
= ({ const { t } = useTranslation() const { notify } = useToastContext() const language = useLanguage() - const [credentialSchema, setCredentialSchema] = useState([]) - const [tempCredential, setTempCredential] = useState>({}) - const [isLoading, setIsLoading] = useState(false) - const [subscriptionBuilderId, setSubscriptionBuilderId] = useState('') - - const createBuilder = useCreateTriggerSubscriptionBuilder() - const updateBuilder = useUpdateTriggerSubscriptionBuilder() - const verifyBuilder = useVerifyTriggerSubscriptionBuilder() - const buildSubscription = useBuildTriggerSubscription() const invalidateSubscriptions = useInvalidateTriggerSubscriptions() + const [credentialSchema, setCredentialSchema] = useState([]) + const [credentials, setCredentials] = useState>({}) + const [parameters, setParameters] = useState>({}) + const [properties, setProperties] = useState>({}) + const [subscriptionName, setSubscriptionName] = useState('') + + const { + step, + builderId, + isLoading, + startAuth, + verifyAuth, + completeConfig, + reset, + } = useTriggerAuthFlow(provider) + useEffect(() => { if (provider.credentials_schema) { const schemas = toolCredentialToFormSchemas(provider.credentials_schema as any) setCredentialSchema(schemas) const defaultCredentials = addDefaultValue({}, schemas) - // Use utility function for consistent data sanitization - setTempCredential(sanitizeFormValues(defaultCredentials)) + setCredentials(sanitizeFormValues(defaultCredentials)) } }, [provider.credentials_schema]) - const handleSave = async () => { - // Validate required fields using utility function + useEffect(() => { + startAuth().catch((err) => { + notify({ + type: 'error', + message: t('workflow.nodes.triggerPlugin.failedToStart', { error: err.message }), + }) + }) + + return () => { + reset() + } + }, []) // Remove dependencies to run only once on mount + + const handleCredentialsSubmit = async () => { const requiredFields = credentialSchema .filter(field => field.required) .map(field => ({ @@ -65,7 +79,7 @@ const ApiKeyConfigModal: FC = ({ label: field.label[language] || field.label.en_US, })) - const missingField = findMissingRequiredField(tempCredential, requiredFields) + const missingField = findMissingRequiredField(credentials, requiredFields) if (missingField) { Toast.notify({ type: 'error', @@ -76,56 +90,156 @@ const ApiKeyConfigModal: FC = ({ return } - setIsLoading(true) + try { + await verifyAuth(credentials) + notify({ + type: 'success', + message: t('workflow.nodes.triggerPlugin.credentialsVerified'), + }) + } + catch (err: any) { + notify({ + type: 'error', + message: t('workflow.nodes.triggerPlugin.credentialVerificationFailed', { + error: err.message, + }), + }) + } + } + + const handleFinalSubmit = async () => { + if (!subscriptionName.trim()) { + notify({ + type: 'error', + message: t('workflow.nodes.triggerPlugin.subscriptionNameRequired'), + }) + return + } try { - // Step 1: Create subscription builder - let builderId = subscriptionBuilderId - if (!builderId) { - const createResponse = await createBuilder.mutateAsync({ - provider: provider.name, - credentials: tempCredential, - }) - builderId = createResponse.subscription_builder.id - setSubscriptionBuilderId(builderId) - } - else { - // Update existing builder - await updateBuilder.mutateAsync({ - provider: provider.name, - subscriptionBuilderId: builderId, - credentials: tempCredential, - }) - } + await completeConfig(parameters, properties, subscriptionName) - // Step 2: Verify credentials - await verifyBuilder.mutateAsync({ - provider: provider.name, - subscriptionBuilderId: builderId, - }) - - // Step 3: Build final subscription - await buildSubscription.mutateAsync({ - provider: provider.name, - subscriptionBuilderId: builderId, - }) - - // Step 4: Invalidate and notify success invalidateSubscriptions(provider.name) notify({ type: 'success', - message: t('workflow.nodes.triggerPlugin.apiKeyConfigured'), + message: t('workflow.nodes.triggerPlugin.configurationComplete'), }) onSuccess() } - catch (error: any) { + catch (err: any) { notify({ type: 'error', - message: t('workflow.nodes.triggerPlugin.configurationFailed', { error: error.message }), + message: t('workflow.nodes.triggerPlugin.configurationFailed', { error: err.message }), }) } - finally { - setIsLoading(false) + } + + const getTitle = () => { + switch (step) { + case 'auth': + return t('workflow.nodes.triggerPlugin.configureApiKey') + case 'params': + return t('workflow.nodes.triggerPlugin.configureParameters') + case 'complete': + return t('workflow.nodes.triggerPlugin.configurationComplete') + default: + return t('workflow.nodes.triggerPlugin.configureApiKey') + } + } + + const getDescription = () => { + switch (step) { + case 'auth': + return t('workflow.nodes.triggerPlugin.apiKeyDescription') + case 'params': + return t('workflow.nodes.triggerPlugin.parametersDescription') + case 'complete': + return t('workflow.nodes.triggerPlugin.configurationCompleteDescription') + default: + return '' + } + } + + const renderContent = () => { + if (credentialSchema.length === 0 && step === 'auth') + return + + switch (step) { + case 'auth': + return ( + <> +
item.url ? ( + + {t('tools.howToGet')} + + + ) : null} + /> +
+ + +
+ + ) + + case 'params': + return ( + + ) + + case 'complete': + return ( +
+
+ + + +
+

+ {t('workflow.nodes.triggerPlugin.configurationCompleteMessage')} +

+ +
+ ) + + default: + return null } } @@ -133,8 +247,8 @@ const ApiKeyConfigModal: FC = ({ = ({ headerClassName='!border-b-divider-subtle' body={
- {credentialSchema.length === 0 ? ( - - ) : ( - <> - item.url ? ( - - {t('tools.howToGet')} - - - ) : null} - /> -
- - -
- - )} + {renderContent()}
} isShowMask={true} diff --git a/web/app/components/workflow/nodes/trigger-plugin/components/parameters-form.tsx b/web/app/components/workflow/nodes/trigger-plugin/components/parameters-form.tsx new file mode 100644 index 0000000000..53b8eb4b80 --- /dev/null +++ b/web/app/components/workflow/nodes/trigger-plugin/components/parameters-form.tsx @@ -0,0 +1,171 @@ +'use client' +import type { FC } from 'react' +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' +import type { Trigger } from '@/app/components/tools/types' +import { toolCredentialToFormSchemas, toolParametersToFormSchemas } from '@/app/components/tools/utils/to-form-schema' +import TriggerForm from './trigger-form' +import Button from '@/app/components/base/button' +import Input from '@/app/components/base/input' + +type ParametersFormProps = { + provider: TriggerWithProvider + trigger?: Trigger + builderId: string + parametersValue: Record + propertiesValue: Record + subscriptionName: string + onParametersChange: (value: Record) => void + onPropertiesChange: (value: Record) => void + onSubscriptionNameChange: (value: string) => void + onSubmit: () => void + onCancel: () => void + isLoading?: boolean + readOnly?: boolean +} + +const ParametersForm: FC = ({ + provider, + trigger, + builderId, + parametersValue, + propertiesValue, + subscriptionName, + onParametersChange, + onPropertiesChange, + onSubscriptionNameChange, + onSubmit, + onCancel, + isLoading = false, + readOnly = false, +}) => { + const { t } = useTranslation() + + // Use the first trigger if no specific trigger is provided + // This is needed for dynamic options API which requires a trigger action + const currentTrigger = trigger || provider.triggers?.[0] + + const parametersSchema = useMemo(() => { + if (!provider.subscription_schema?.parameters_schema) return [] + return toolParametersToFormSchemas(provider.subscription_schema.parameters_schema as any) + }, [provider.subscription_schema?.parameters_schema]) + + const propertiesSchema = useMemo(() => { + if (!provider.subscription_schema?.properties_schema) return [] + return toolCredentialToFormSchemas(provider.subscription_schema.properties_schema as any) + }, [provider.subscription_schema?.properties_schema]) + + const hasParameters = parametersSchema.length > 0 + const hasProperties = propertiesSchema.length > 0 + + if (!hasParameters && !hasProperties) { + return ( +
+

+ {t('workflow.nodes.triggerPlugin.noConfigurationRequired')} +

+
+ + +
+
+ ) + } + + return ( +
+ {/* Subscription Name Section */} +
+
+

+ {t('workflow.nodes.triggerPlugin.subscriptionName')} +

+

+ {t('workflow.nodes.triggerPlugin.subscriptionNameDescription')} +

+
+ onSubscriptionNameChange(e.target.value)} + placeholder={t('workflow.nodes.triggerPlugin.subscriptionNamePlaceholder')} + readOnly={readOnly} + /> +
+ + {/* Parameters Section */} + {hasParameters && ( +
+
+

+ {t('workflow.nodes.triggerPlugin.parameters')} +

+

+ {t('workflow.nodes.triggerPlugin.parametersDescription')} +

+
+ +
+ )} + + {/* Properties Section */} + {hasProperties && ( +
+
+

+ {t('workflow.nodes.triggerPlugin.properties')} +

+

+ {t('workflow.nodes.triggerPlugin.propertiesDescription')} +

+
+ +
+ )} + + {/* Action Buttons */} +
+ + +
+
+ ) +} + +export default ParametersForm diff --git a/web/app/components/workflow/nodes/trigger-plugin/trigger-form/index.tsx b/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/index.tsx similarity index 90% rename from web/app/components/workflow/nodes/trigger-plugin/trigger-form/index.tsx rename to web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/index.tsx index f8f9d1c6c2..ce27788bab 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/trigger-form/index.tsx +++ b/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/index.tsx @@ -2,9 +2,9 @@ import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations' import type { Trigger } from '@/app/components/tools/types' import type { FC } from 'react' -import type { PluginTriggerVarInputs } from '../types' +import type { PluginTriggerVarInputs } from '../../types' import TriggerFormItem from './item' -import type { TriggerWithProvider } from '../../../block-selector/types' +import type { TriggerWithProvider } from '../../../../block-selector/types' type Props = { readOnly: boolean diff --git a/web/app/components/workflow/nodes/trigger-plugin/trigger-form/item.tsx b/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/item.tsx similarity index 96% rename from web/app/components/workflow/nodes/trigger-plugin/trigger-form/item.tsx rename to web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/item.tsx index d096343e30..3d1cdd1da6 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/trigger-form/item.tsx +++ b/web/app/components/workflow/nodes/trigger-plugin/components/trigger-form/item.tsx @@ -3,7 +3,7 @@ import type { FC } from 'react' import { RiBracesLine, } from '@remixicon/react' -import type { PluginTriggerVarInputs } from '../types' +import type { PluginTriggerVarInputs } from '../../types' import type { CredentialFormSchema } from '@/app/components/header/account-setting/model-provider-page/declarations' import { FormTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations' import { useLanguage } from '@/app/components/header/account-setting/model-provider-page/hooks' @@ -13,7 +13,7 @@ import FormInputItem from '@/app/components/workflow/nodes/_base/components/form import { useBoolean } from 'ahooks' import SchemaModal from '@/app/components/plugins/plugin-detail-panel/tool-selector/schema-modal' import type { Trigger } from '@/app/components/tools/types' -import type { TriggerWithProvider } from '../../../block-selector/types' +import type { TriggerWithProvider } from '../../../../block-selector/types' type Props = { readOnly: boolean diff --git a/web/app/components/workflow/nodes/trigger-plugin/hooks/use-trigger-auth-flow.ts b/web/app/components/workflow/nodes/trigger-plugin/hooks/use-trigger-auth-flow.ts new file mode 100644 index 0000000000..983b8512de --- /dev/null +++ b/web/app/components/workflow/nodes/trigger-plugin/hooks/use-trigger-auth-flow.ts @@ -0,0 +1,162 @@ +import { useCallback, useState } from 'react' +import { + useBuildTriggerSubscription, + useCreateTriggerSubscriptionBuilder, + useUpdateTriggerSubscriptionBuilder, + useVerifyTriggerSubscriptionBuilder, +} from '@/service/use-triggers' +import type { TriggerWithProvider } from '@/app/components/workflow/block-selector/types' + +// Helper function to serialize complex values to strings for backend encryption +const serializeFormValues = (values: Record): Record => { + const result: Record = {} + + for (const [key, value] of Object.entries(values)) { + if (value === null || value === undefined) + result[key] = '' + else if (typeof value === 'object') + result[key] = JSON.stringify(value) + else + result[key] = String(value) + } + + return result +} + +export type AuthFlowStep = 'auth' | 'params' | 'complete' + +export type AuthFlowState = { + step: AuthFlowStep + builderId: string + isLoading: boolean + error: string | null +} + +export type AuthFlowActions = { + startAuth: () => Promise + verifyAuth: (credentials: Record) => Promise + completeConfig: (parameters: Record, properties?: Record, name?: string) => Promise + reset: () => void +} + +export const useTriggerAuthFlow = (provider: TriggerWithProvider): AuthFlowState & AuthFlowActions => { + const [step, setStep] = useState('auth') + const [builderId, setBuilderId] = useState('') + const [isLoading, setIsLoading] = useState(false) + const [error, setError] = useState(null) + + const createBuilder = useCreateTriggerSubscriptionBuilder() + const updateBuilder = useUpdateTriggerSubscriptionBuilder() + const verifyBuilder = useVerifyTriggerSubscriptionBuilder() + const buildSubscription = useBuildTriggerSubscription() + + const startAuth = useCallback(async () => { + if (builderId) return // Prevent multiple calls if already started + + setIsLoading(true) + setError(null) + + try { + const response = await createBuilder.mutateAsync({ + provider: provider.name, + }) + setBuilderId(response.subscription_builder.id) + setStep('auth') + } + catch (err: any) { + setError(err.message || 'Failed to start authentication flow') + throw err + } + finally { + setIsLoading(false) + } + }, [provider.name, createBuilder, builderId]) + + const verifyAuth = useCallback(async (credentials: Record) => { + if (!builderId) { + setError('No builder ID available') + return + } + + setIsLoading(true) + setError(null) + + try { + await updateBuilder.mutateAsync({ + provider: provider.name, + subscriptionBuilderId: builderId, + credentials: serializeFormValues(credentials), + }) + + await verifyBuilder.mutateAsync({ + provider: provider.name, + subscriptionBuilderId: builderId, + }) + + setStep('params') + } + catch (err: any) { + setError(err.message || 'Authentication verification failed') + throw err + } + finally { + setIsLoading(false) + } + }, [provider.name, builderId, updateBuilder, verifyBuilder]) + + const completeConfig = useCallback(async ( + parameters: Record, + properties: Record = {}, + name?: string, + ) => { + if (!builderId) { + setError('No builder ID available') + return + } + + setIsLoading(true) + setError(null) + + try { + await updateBuilder.mutateAsync({ + provider: provider.name, + subscriptionBuilderId: builderId, + parameters: serializeFormValues(parameters), + properties: serializeFormValues(properties), + name, + }) + + await buildSubscription.mutateAsync({ + provider: provider.name, + subscriptionBuilderId: builderId, + }) + + setStep('complete') + } + catch (err: any) { + setError(err.message || 'Configuration failed') + throw err + } + finally { + setIsLoading(false) + } + }, [provider.name, builderId, updateBuilder, buildSubscription]) + + const reset = useCallback(() => { + setStep('auth') + setBuilderId('') + setIsLoading(false) + setError(null) + }, []) + + return { + step, + builderId, + isLoading, + error, + startAuth, + verifyAuth, + completeConfig, + reset, + } +} diff --git a/web/app/components/workflow/nodes/trigger-plugin/panel.tsx b/web/app/components/workflow/nodes/trigger-plugin/panel.tsx index 917bb8eaf7..a412df39c1 100644 --- a/web/app/components/workflow/nodes/trigger-plugin/panel.tsx +++ b/web/app/components/workflow/nodes/trigger-plugin/panel.tsx @@ -5,7 +5,7 @@ import Split from '@/app/components/workflow/nodes/_base/components/split' import OutputVars, { VarItem } from '@/app/components/workflow/nodes/_base/components/output-vars' import type { NodePanelProps } from '@/app/components/workflow/types' import useConfig from './use-config' -import TriggerForm from './trigger-form' +import TriggerForm from './components/trigger-form' import StructureOutputItem from '@/app/components/workflow/nodes/_base/components/variable/object-child-tree-panel/show' import { Type } from '../llm/types' diff --git a/web/i18n/en-US/workflow.ts b/web/i18n/en-US/workflow.ts index 4129407367..92a7101200 100644 --- a/web/i18n/en-US/workflow.ts +++ b/web/i18n/en-US/workflow.ts @@ -743,6 +743,23 @@ const translation = { apiKeyDescription: 'Configure API key credentials for authentication', apiKeyConfigured: 'API key configured successfully', configurationFailed: 'Configuration failed', + failedToStart: 'Failed to start authentication flow', + credentialsVerified: 'Credentials verified successfully', + credentialVerificationFailed: 'Credential verification failed', + verifyAndContinue: 'Verify & Continue', + configureParameters: 'Configure Parameters', + parametersDescription: 'Configure trigger parameters and properties', + configurationComplete: 'Configuration Complete', + configurationCompleteDescription: 'Your trigger has been configured successfully', + configurationCompleteMessage: 'Your trigger configuration is now complete and ready to use.', + parameters: 'Parameters', + properties: 'Properties', + propertiesDescription: 'Additional configuration properties for this trigger', + noConfigurationRequired: 'No additional configuration required for this trigger.', + subscriptionName: 'Subscription Name', + subscriptionNameDescription: 'Enter a unique name for this trigger subscription', + subscriptionNamePlaceholder: 'Enter subscription name...', + subscriptionNameRequired: 'Subscription name is required', }, questionClassifiers: { model: 'model', diff --git a/web/i18n/ja-JP/workflow.ts b/web/i18n/ja-JP/workflow.ts index c5c2762206..1deae3dae3 100644 --- a/web/i18n/ja-JP/workflow.ts +++ b/web/i18n/ja-JP/workflow.ts @@ -1053,6 +1053,23 @@ const translation = { apiKeyDescription: '認証のための API キー認証情報を設定してください', apiKeyConfigured: 'API キーが正常に設定されました', configurationFailed: '設定に失敗しました', + failedToStart: '認証フローの開始に失敗しました', + credentialsVerified: '認証情報が正常に検証されました', + credentialVerificationFailed: '認証情報の検証に失敗しました', + verifyAndContinue: '検証して続行', + configureParameters: 'パラメーターを設定', + parametersDescription: 'トリガーのパラメーターとプロパティを設定してください', + configurationComplete: '設定完了', + configurationCompleteDescription: 'トリガーが正常に設定されました', + configurationCompleteMessage: 'トリガーの設定が完了し、使用する準備ができました。', + parameters: 'パラメーター', + properties: 'プロパティ', + propertiesDescription: 'このトリガーの追加設定プロパティ', + noConfigurationRequired: 'このトリガーには追加の設定は必要ありません。', + subscriptionName: 'サブスクリプション名', + subscriptionNameDescription: 'このトリガーサブスクリプションの一意な名前を入力してください', + subscriptionNamePlaceholder: 'サブスクリプション名を入力...', + subscriptionNameRequired: 'サブスクリプション名は必須です', }, }, tracing: { diff --git a/web/i18n/zh-Hans/workflow.ts b/web/i18n/zh-Hans/workflow.ts index 988d99c1a7..8e14579fc3 100644 --- a/web/i18n/zh-Hans/workflow.ts +++ b/web/i18n/zh-Hans/workflow.ts @@ -1053,6 +1053,23 @@ const translation = { apiKeyDescription: '配置 API key 凭据进行身份验证', apiKeyConfigured: 'API key 配置成功', configurationFailed: '配置失败', + failedToStart: '启动身份验证流程失败', + credentialsVerified: '凭据验证成功', + credentialVerificationFailed: '凭据验证失败', + verifyAndContinue: '验证并继续', + configureParameters: '配置参数', + parametersDescription: '配置触发器参数和属性', + configurationComplete: '配置完成', + configurationCompleteDescription: '您的触发器已成功配置', + configurationCompleteMessage: '您的触发器配置已完成,现在可以使用了。', + parameters: '参数', + properties: '属性', + propertiesDescription: '此触发器的额外配置属性', + noConfigurationRequired: '此触发器不需要额外配置。', + subscriptionName: '订阅名称', + subscriptionNameDescription: '为此触发器订阅输入一个唯一名称', + subscriptionNamePlaceholder: '输入订阅名称...', + subscriptionNameRequired: '订阅名称是必需的', }, }, tracing: { diff --git a/web/service/use-triggers.ts b/web/service/use-triggers.ts index 2d341a5d41..4f8a38ec3b 100644 --- a/web/service/use-triggers.ts +++ b/web/service/use-triggers.ts @@ -268,7 +268,12 @@ export const useTriggerPluginDynamicOptions = (payload: { queryKey: [NAME_SPACE, 'dynamic-options', payload.plugin_id, payload.provider, payload.action, payload.parameter, payload.extra], queryFn: () => get<{ options: Array<{ value: string; label: any }> }>( '/workspaces/current/plugin/parameters/dynamic-options', - { params: payload }, + { + params: { + ...payload, + provider_type: 'trigger', // Add required provider_type parameter + }, + }, ), enabled: enabled && !!payload.plugin_id && !!payload.provider && !!payload.action && !!payload.parameter, })