feat: implement subscription editing modals and enhance subscription management
- Added `ApiKeyEditModal`, `OAuthEditModal`, and `ManualEditModal` for editing different types of subscriptions. - Updated `EditModal` to conditionally render the appropriate modal based on the subscription's credential type. - Introduced `useVerifyTriggerSubscription` hook for verifying subscription credentials. - Enhanced form handling and validation for subscription properties and parameters across modals. - Refactored existing components to integrate new editing functionality, improving user experience in subscription management.
This commit is contained in:
@@ -0,0 +1,323 @@
|
|||||||
|
'use client'
|
||||||
|
import { BaseForm } from '@/app/components/base/form/components/base'
|
||||||
|
import type { FormRefObject, FormSchema } from '@/app/components/base/form/types'
|
||||||
|
import { FormTypeEnum } from '@/app/components/base/form/types'
|
||||||
|
import Modal from '@/app/components/base/modal/modal'
|
||||||
|
import Toast from '@/app/components/base/toast'
|
||||||
|
import type { ParametersSchema, PluginDetail } from '@/app/components/plugins/types'
|
||||||
|
import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance'
|
||||||
|
import type { TriggerSubscription } from '@/app/components/workflow/block-selector/types'
|
||||||
|
import { useUpdateTriggerSubscription, useVerifyTriggerSubscription } from '@/service/use-triggers'
|
||||||
|
import { parsePluginErrorMessage } from '@/utils/error-parser'
|
||||||
|
import { useMemo, useRef, useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { usePluginStore } from '../../store'
|
||||||
|
import { useSubscriptionList } from '../use-subscription-list'
|
||||||
|
import { ReadmeShowType } from '../../../readme-panel/store'
|
||||||
|
import { EncryptedBottom } from '@/app/components/base/encrypted-bottom'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onClose: () => void
|
||||||
|
subscription: TriggerSubscription
|
||||||
|
pluginDetail?: PluginDetail
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EditStep {
|
||||||
|
EditCredentials = 'edit_credentials',
|
||||||
|
EditConfiguration = 'edit_configuration',
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeFormType = (type: string): FormTypeEnum => {
|
||||||
|
switch (type) {
|
||||||
|
case 'string':
|
||||||
|
case 'text':
|
||||||
|
return FormTypeEnum.textInput
|
||||||
|
case 'password':
|
||||||
|
case 'secret':
|
||||||
|
return FormTypeEnum.secretInput
|
||||||
|
case 'number':
|
||||||
|
case 'integer':
|
||||||
|
return FormTypeEnum.textNumber
|
||||||
|
case 'boolean':
|
||||||
|
return FormTypeEnum.boolean
|
||||||
|
case 'select':
|
||||||
|
return FormTypeEnum.select
|
||||||
|
default:
|
||||||
|
if (Object.values(FormTypeEnum).includes(type as FormTypeEnum))
|
||||||
|
return type as FormTypeEnum
|
||||||
|
return FormTypeEnum.textInput
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const StatusStep = ({ isActive, text }: { isActive: boolean, text: string }) => {
|
||||||
|
return <div className={`system-2xs-semibold-uppercase flex items-center gap-1 ${isActive
|
||||||
|
? 'text-state-accent-solid'
|
||||||
|
: 'text-text-tertiary'}`}>
|
||||||
|
{isActive && (
|
||||||
|
<div className='h-1 w-1 rounded-full bg-state-accent-solid'></div>
|
||||||
|
)}
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
const MultiSteps = ({ currentStep }: { currentStep: EditStep }) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
return <div className='mb-6 flex w-1/3 items-center gap-2'>
|
||||||
|
<StatusStep isActive={currentStep === EditStep.EditCredentials} text={t('pluginTrigger.modal.steps.verify')} />
|
||||||
|
<div className='h-px w-3 shrink-0 bg-divider-deep'></div>
|
||||||
|
<StatusStep isActive={currentStep === EditStep.EditConfiguration} text={t('pluginTrigger.modal.steps.configuration')} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ApiKeyEditModal = ({ onClose, subscription, pluginDetail }: Props) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const detail = usePluginStore(state => state.detail)
|
||||||
|
const { refetch } = useSubscriptionList()
|
||||||
|
|
||||||
|
const [currentStep, setCurrentStep] = useState<EditStep>(EditStep.EditCredentials)
|
||||||
|
|
||||||
|
const { mutate: updateSubscription, isPending: isUpdating } = useUpdateTriggerSubscription()
|
||||||
|
const { mutate: verifyCredentials, isPending: isVerifying } = useVerifyTriggerSubscription()
|
||||||
|
|
||||||
|
const parametersSchema = useMemo<ParametersSchema[]>(
|
||||||
|
() => detail?.declaration?.trigger?.subscription_constructor?.parameters || [],
|
||||||
|
[detail?.declaration?.trigger?.subscription_constructor?.parameters],
|
||||||
|
)
|
||||||
|
|
||||||
|
const rawApiKeyCredentialsSchema = detail?.declaration.trigger?.subscription_constructor?.credentials_schema || []
|
||||||
|
const apiKeyCredentialsSchema = useMemo(() => {
|
||||||
|
return rawApiKeyCredentialsSchema.map(schema => ({
|
||||||
|
...schema,
|
||||||
|
tooltip: schema.help,
|
||||||
|
}))
|
||||||
|
}, [rawApiKeyCredentialsSchema])
|
||||||
|
|
||||||
|
const basicFormRef = useRef<FormRefObject>(null)
|
||||||
|
const parametersFormRef = useRef<FormRefObject>(null)
|
||||||
|
const credentialsFormRef = useRef<FormRefObject>(null)
|
||||||
|
|
||||||
|
const handleVerifyCredentials = () => {
|
||||||
|
const credentialsFormValues = credentialsFormRef.current?.getFormValues({
|
||||||
|
needTransformWhenSecretFieldIsPristine: true,
|
||||||
|
}) || { values: {}, isCheckValidated: false }
|
||||||
|
|
||||||
|
if (!credentialsFormValues.isCheckValidated)
|
||||||
|
return
|
||||||
|
|
||||||
|
const credentials = credentialsFormValues.values
|
||||||
|
|
||||||
|
// Clear previous errors
|
||||||
|
if (Object.keys(credentials).length > 0) {
|
||||||
|
credentialsFormRef.current?.setFields([{
|
||||||
|
name: Object.keys(credentials)[0],
|
||||||
|
errors: [],
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyCredentials(
|
||||||
|
{
|
||||||
|
provider: subscription.provider,
|
||||||
|
subscriptionId: subscription.id,
|
||||||
|
credentials,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
Toast.notify({
|
||||||
|
type: 'success',
|
||||||
|
message: t('pluginTrigger.modal.apiKey.verify.success'),
|
||||||
|
})
|
||||||
|
setCurrentStep(EditStep.EditConfiguration)
|
||||||
|
},
|
||||||
|
onError: async (error: any) => {
|
||||||
|
const errorMessage = await parsePluginErrorMessage(error) || t('pluginTrigger.modal.apiKey.verify.error')
|
||||||
|
if (Object.keys(credentials).length > 0) {
|
||||||
|
credentialsFormRef.current?.setFields([{
|
||||||
|
name: Object.keys(credentials)[0],
|
||||||
|
errors: [errorMessage],
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleUpdate = () => {
|
||||||
|
const basicFormValues = basicFormRef.current?.getFormValues({})
|
||||||
|
if (!basicFormValues?.isCheckValidated)
|
||||||
|
return
|
||||||
|
|
||||||
|
const name = basicFormValues.values.subscription_name as string
|
||||||
|
|
||||||
|
let parameters: Record<string, any> | undefined
|
||||||
|
|
||||||
|
if (parametersSchema.length > 0) {
|
||||||
|
const paramsFormValues = parametersFormRef.current?.getFormValues({
|
||||||
|
needTransformWhenSecretFieldIsPristine: true,
|
||||||
|
})
|
||||||
|
if (!paramsFormValues?.isCheckValidated)
|
||||||
|
return
|
||||||
|
parameters = Object.keys(paramsFormValues.values).length > 0 ? paramsFormValues.values : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSubscription(
|
||||||
|
{
|
||||||
|
subscriptionId: subscription.id,
|
||||||
|
name,
|
||||||
|
parameters,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
Toast.notify({
|
||||||
|
type: 'success',
|
||||||
|
message: t('pluginTrigger.subscription.list.item.actions.edit.success'),
|
||||||
|
})
|
||||||
|
refetch?.()
|
||||||
|
onClose()
|
||||||
|
},
|
||||||
|
onError: (error: any) => {
|
||||||
|
Toast.notify({
|
||||||
|
type: 'error',
|
||||||
|
message: error?.message || t('pluginTrigger.subscription.list.item.actions.edit.error'),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleConfirm = () => {
|
||||||
|
if (currentStep === EditStep.EditCredentials)
|
||||||
|
handleVerifyCredentials()
|
||||||
|
else
|
||||||
|
handleUpdate()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCredentialsChange = () => {
|
||||||
|
if (apiKeyCredentialsSchema.length > 0) {
|
||||||
|
credentialsFormRef.current?.setFields([{
|
||||||
|
name: apiKeyCredentialsSchema[0].name,
|
||||||
|
errors: [],
|
||||||
|
}])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const basicFormSchemas: FormSchema[] = useMemo(() => [
|
||||||
|
{
|
||||||
|
name: 'subscription_name',
|
||||||
|
label: t('pluginTrigger.modal.form.subscriptionName.label'),
|
||||||
|
placeholder: t('pluginTrigger.modal.form.subscriptionName.placeholder'),
|
||||||
|
type: FormTypeEnum.textInput,
|
||||||
|
required: true,
|
||||||
|
default: subscription.name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'callback_url',
|
||||||
|
label: t('pluginTrigger.modal.form.callbackUrl.label'),
|
||||||
|
placeholder: t('pluginTrigger.modal.form.callbackUrl.placeholder'),
|
||||||
|
type: FormTypeEnum.textInput,
|
||||||
|
required: false,
|
||||||
|
default: subscription.endpoint || '',
|
||||||
|
disabled: true,
|
||||||
|
tooltip: t('pluginTrigger.modal.form.callbackUrl.tooltip'),
|
||||||
|
showCopy: true,
|
||||||
|
},
|
||||||
|
], [t, subscription.name, subscription.endpoint])
|
||||||
|
|
||||||
|
const credentialsFormSchemas: FormSchema[] = useMemo(() => {
|
||||||
|
return apiKeyCredentialsSchema.map(schema => ({
|
||||||
|
...schema,
|
||||||
|
type: normalizeFormType(schema.type as string),
|
||||||
|
tooltip: schema.help,
|
||||||
|
default: subscription.credentials?.[schema.name] || schema.default,
|
||||||
|
}))
|
||||||
|
}, [apiKeyCredentialsSchema, subscription.credentials])
|
||||||
|
|
||||||
|
const parametersFormSchemas: FormSchema[] = useMemo(() => {
|
||||||
|
return parametersSchema.map((schema: ParametersSchema) => {
|
||||||
|
const normalizedType = normalizeFormType(schema.type as string)
|
||||||
|
return {
|
||||||
|
...schema,
|
||||||
|
type: normalizedType,
|
||||||
|
tooltip: schema.description,
|
||||||
|
default: subscription.parameters?.[schema.name] || schema.default,
|
||||||
|
dynamicSelectParams: normalizedType === FormTypeEnum.dynamicSelect
|
||||||
|
? {
|
||||||
|
plugin_id: detail?.plugin_id || '',
|
||||||
|
provider: detail?.provider || '',
|
||||||
|
action: 'provider',
|
||||||
|
parameter: schema.name,
|
||||||
|
credential_id: subscription.id,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
fieldClassName: schema.type === FormTypeEnum.boolean ? 'flex items-center justify-between' : undefined,
|
||||||
|
labelClassName: schema.type === FormTypeEnum.boolean ? 'mb-0' : undefined,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}, [parametersSchema, subscription.parameters, subscription.id, detail?.plugin_id, detail?.provider])
|
||||||
|
|
||||||
|
const getConfirmButtonText = () => {
|
||||||
|
if (currentStep === EditStep.EditCredentials)
|
||||||
|
return isVerifying ? t('pluginTrigger.modal.common.verifying') : t('pluginTrigger.modal.common.verify')
|
||||||
|
|
||||||
|
return isUpdating ? t('common.operation.saving') : t('common.operation.save')
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={t('pluginTrigger.subscription.list.item.actions.edit.title')}
|
||||||
|
confirmButtonText={getConfirmButtonText()}
|
||||||
|
onClose={onClose}
|
||||||
|
onCancel={onClose}
|
||||||
|
onConfirm={handleConfirm}
|
||||||
|
disabled={isUpdating || isVerifying}
|
||||||
|
clickOutsideNotClose
|
||||||
|
wrapperClassName='!z-[101]'
|
||||||
|
bottomSlot={currentStep === EditStep.EditCredentials ? <EncryptedBottom /> : null}
|
||||||
|
>
|
||||||
|
{pluginDetail && (
|
||||||
|
<ReadmeEntrance pluginDetail={pluginDetail} showType={ReadmeShowType.modal} />
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Multi-step indicator */}
|
||||||
|
<MultiSteps currentStep={currentStep} />
|
||||||
|
|
||||||
|
{/* Step 1: Edit Credentials */}
|
||||||
|
{currentStep === EditStep.EditCredentials && (
|
||||||
|
<div className='mb-4'>
|
||||||
|
{credentialsFormSchemas.length > 0 && (
|
||||||
|
<BaseForm
|
||||||
|
formSchemas={credentialsFormSchemas}
|
||||||
|
ref={credentialsFormRef}
|
||||||
|
labelClassName='system-sm-medium mb-2 flex items-center gap-1 text-text-primary'
|
||||||
|
formClassName='space-y-4'
|
||||||
|
preventDefaultSubmit={true}
|
||||||
|
onChange={handleCredentialsChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Step 2: Edit Configuration */}
|
||||||
|
{currentStep === EditStep.EditConfiguration && (
|
||||||
|
<div className='max-h-[70vh]'>
|
||||||
|
{/* Basic form: subscription name and callback URL */}
|
||||||
|
<BaseForm
|
||||||
|
formSchemas={basicFormSchemas}
|
||||||
|
ref={basicFormRef}
|
||||||
|
labelClassName='system-sm-medium mb-2 flex items-center gap-1 text-text-primary'
|
||||||
|
formClassName='space-y-4 mb-4'
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Parameters */}
|
||||||
|
{parametersFormSchemas.length > 0 && (
|
||||||
|
<BaseForm
|
||||||
|
formSchemas={parametersFormSchemas}
|
||||||
|
ref={parametersFormRef}
|
||||||
|
labelClassName='system-sm-medium mb-2 flex items-center gap-1 text-text-primary'
|
||||||
|
formClassName='space-y-4'
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
'use client'
|
||||||
|
import type { PluginDetail } from '@/app/components/plugins/types'
|
||||||
|
import type { TriggerSubscription } from '@/app/components/workflow/block-selector/types'
|
||||||
|
import { TriggerCredentialTypeEnum } from '@/app/components/workflow/block-selector/types'
|
||||||
|
import { ManualEditModal } from './manual-edit-modal'
|
||||||
|
import { OAuthEditModal } from './oauth-edit-modal'
|
||||||
|
import { ApiKeyEditModal } from './apikey-edit-modal'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onClose: () => void
|
||||||
|
subscription: TriggerSubscription
|
||||||
|
pluginDetail?: PluginDetail
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EditModal = ({ onClose, subscription, pluginDetail }: Props) => {
|
||||||
|
const credentialType = subscription.credential_type
|
||||||
|
|
||||||
|
switch (credentialType) {
|
||||||
|
case TriggerCredentialTypeEnum.Unauthorized:
|
||||||
|
return <ManualEditModal onClose={onClose} subscription={subscription} pluginDetail={pluginDetail} />
|
||||||
|
case TriggerCredentialTypeEnum.Oauth2:
|
||||||
|
return <OAuthEditModal onClose={onClose} subscription={subscription} pluginDetail={pluginDetail} />
|
||||||
|
case TriggerCredentialTypeEnum.ApiKey:
|
||||||
|
return <ApiKeyEditModal onClose={onClose} subscription={subscription} pluginDetail={pluginDetail} />
|
||||||
|
default:
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,9 +10,9 @@ import type { TriggerSubscription } from '@/app/components/workflow/block-select
|
|||||||
import { useUpdateTriggerSubscription } from '@/service/use-triggers'
|
import { useUpdateTriggerSubscription } from '@/service/use-triggers'
|
||||||
import { useMemo, useRef } from 'react'
|
import { useMemo, useRef } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { usePluginStore } from '../store'
|
import { usePluginStore } from '../../store'
|
||||||
import { useSubscriptionList } from './use-subscription-list'
|
import { useSubscriptionList } from '../use-subscription-list'
|
||||||
import { ReadmeShowType } from '../../readme-panel/store'
|
import { ReadmeShowType } from '../../../readme-panel/store'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
@@ -20,7 +20,6 @@ type Props = {
|
|||||||
pluginDetail?: PluginDetail
|
pluginDetail?: PluginDetail
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normalize backend type to FormTypeEnum
|
|
||||||
const normalizeFormType = (type: string): FormTypeEnum => {
|
const normalizeFormType = (type: string): FormTypeEnum => {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'string':
|
case 'string':
|
||||||
@@ -43,7 +42,7 @@ const normalizeFormType = (type: string): FormTypeEnum => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EditModal = ({ onClose, subscription, pluginDetail }: Props) => {
|
export const ManualEditModal = ({ onClose, subscription, pluginDetail }: Props) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const detail = usePluginStore(state => state.detail)
|
const detail = usePluginStore(state => state.detail)
|
||||||
const { refetch } = useSubscriptionList()
|
const { refetch } = useSubscriptionList()
|
||||||
@@ -54,12 +53,10 @@ export const EditModal = ({ onClose, subscription, pluginDetail }: Props) => {
|
|||||||
() => detail?.declaration?.trigger?.subscription_schema || [],
|
() => detail?.declaration?.trigger?.subscription_schema || [],
|
||||||
[detail?.declaration?.trigger?.subscription_schema],
|
[detail?.declaration?.trigger?.subscription_schema],
|
||||||
)
|
)
|
||||||
|
|
||||||
const formRef = useRef<FormRefObject>(null)
|
const formRef = useRef<FormRefObject>(null)
|
||||||
|
|
||||||
const handleConfirm = () => {
|
const handleConfirm = () => {
|
||||||
// Use needTransformWhenSecretFieldIsPristine to handle secret fields
|
|
||||||
// When secret field is not modified, it will be transformed to '[__HIDDEN__]'
|
|
||||||
// Backend will preserve original value when receiving '[__HIDDEN__]'
|
|
||||||
const formValues = formRef.current?.getFormValues({
|
const formValues = formRef.current?.getFormValues({
|
||||||
needTransformWhenSecretFieldIsPristine: true,
|
needTransformWhenSecretFieldIsPristine: true,
|
||||||
})
|
})
|
||||||
@@ -68,7 +65,7 @@ export const EditModal = ({ onClose, subscription, pluginDetail }: Props) => {
|
|||||||
|
|
||||||
const name = formValues.values.subscription_name as string
|
const name = formValues.values.subscription_name as string
|
||||||
|
|
||||||
// Extract properties values (exclude subscription_name and callback_url)
|
// Extract properties (exclude subscription_name and callback_url)
|
||||||
const properties = { ...formValues.values }
|
const properties = { ...formValues.values }
|
||||||
delete properties.subscription_name
|
delete properties.subscription_name
|
||||||
delete properties.callback_url
|
delete properties.callback_url
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
'use client'
|
||||||
|
import { BaseForm } from '@/app/components/base/form/components/base'
|
||||||
|
import type { FormRefObject, FormSchema } from '@/app/components/base/form/types'
|
||||||
|
import { FormTypeEnum } from '@/app/components/base/form/types'
|
||||||
|
import Modal from '@/app/components/base/modal/modal'
|
||||||
|
import Toast from '@/app/components/base/toast'
|
||||||
|
import type { ParametersSchema, PluginDetail } from '@/app/components/plugins/types'
|
||||||
|
import { ReadmeEntrance } from '@/app/components/plugins/readme-panel/entrance'
|
||||||
|
import type { TriggerSubscription } from '@/app/components/workflow/block-selector/types'
|
||||||
|
import { useUpdateTriggerSubscription } from '@/service/use-triggers'
|
||||||
|
import { useMemo, useRef } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { usePluginStore } from '../../store'
|
||||||
|
import { useSubscriptionList } from '../use-subscription-list'
|
||||||
|
import { ReadmeShowType } from '../../../readme-panel/store'
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
onClose: () => void
|
||||||
|
subscription: TriggerSubscription
|
||||||
|
pluginDetail?: PluginDetail
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeFormType = (type: string): FormTypeEnum => {
|
||||||
|
switch (type) {
|
||||||
|
case 'string':
|
||||||
|
case 'text':
|
||||||
|
return FormTypeEnum.textInput
|
||||||
|
case 'password':
|
||||||
|
case 'secret':
|
||||||
|
return FormTypeEnum.secretInput
|
||||||
|
case 'number':
|
||||||
|
case 'integer':
|
||||||
|
return FormTypeEnum.textNumber
|
||||||
|
case 'boolean':
|
||||||
|
return FormTypeEnum.boolean
|
||||||
|
case 'select':
|
||||||
|
return FormTypeEnum.select
|
||||||
|
default:
|
||||||
|
if (Object.values(FormTypeEnum).includes(type as FormTypeEnum))
|
||||||
|
return type as FormTypeEnum
|
||||||
|
return FormTypeEnum.textInput
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OAuthEditModal = ({ onClose, subscription, pluginDetail }: Props) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const detail = usePluginStore(state => state.detail)
|
||||||
|
const { refetch } = useSubscriptionList()
|
||||||
|
|
||||||
|
const { mutate: updateSubscription, isPending: isUpdating } = useUpdateTriggerSubscription()
|
||||||
|
|
||||||
|
const parametersSchema = useMemo<ParametersSchema[]>(
|
||||||
|
() => detail?.declaration?.trigger?.subscription_constructor?.parameters || [],
|
||||||
|
[detail?.declaration?.trigger?.subscription_constructor?.parameters],
|
||||||
|
)
|
||||||
|
|
||||||
|
const formRef = useRef<FormRefObject>(null)
|
||||||
|
|
||||||
|
const handleConfirm = () => {
|
||||||
|
const formValues = formRef.current?.getFormValues({
|
||||||
|
needTransformWhenSecretFieldIsPristine: true,
|
||||||
|
})
|
||||||
|
if (!formValues?.isCheckValidated)
|
||||||
|
return
|
||||||
|
|
||||||
|
const name = formValues.values.subscription_name as string
|
||||||
|
|
||||||
|
// Extract parameters (exclude subscription_name and callback_url)
|
||||||
|
const parameters = { ...formValues.values }
|
||||||
|
delete parameters.subscription_name
|
||||||
|
delete parameters.callback_url
|
||||||
|
|
||||||
|
updateSubscription(
|
||||||
|
{
|
||||||
|
subscriptionId: subscription.id,
|
||||||
|
name,
|
||||||
|
parameters: Object.keys(parameters).length > 0 ? parameters : undefined,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
Toast.notify({
|
||||||
|
type: 'success',
|
||||||
|
message: t('pluginTrigger.subscription.list.item.actions.edit.success'),
|
||||||
|
})
|
||||||
|
refetch?.()
|
||||||
|
onClose()
|
||||||
|
},
|
||||||
|
onError: (error: any) => {
|
||||||
|
Toast.notify({
|
||||||
|
type: 'error',
|
||||||
|
message: error?.message || t('pluginTrigger.subscription.list.item.actions.edit.error'),
|
||||||
|
})
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const formSchemas: FormSchema[] = useMemo(() => [
|
||||||
|
{
|
||||||
|
name: 'subscription_name',
|
||||||
|
label: t('pluginTrigger.modal.form.subscriptionName.label'),
|
||||||
|
placeholder: t('pluginTrigger.modal.form.subscriptionName.placeholder'),
|
||||||
|
type: FormTypeEnum.textInput,
|
||||||
|
required: true,
|
||||||
|
default: subscription.name,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'callback_url',
|
||||||
|
label: t('pluginTrigger.modal.form.callbackUrl.label'),
|
||||||
|
placeholder: t('pluginTrigger.modal.form.callbackUrl.placeholder'),
|
||||||
|
type: FormTypeEnum.textInput,
|
||||||
|
required: false,
|
||||||
|
default: subscription.endpoint || '',
|
||||||
|
disabled: true,
|
||||||
|
tooltip: t('pluginTrigger.modal.form.callbackUrl.tooltip'),
|
||||||
|
showCopy: true,
|
||||||
|
},
|
||||||
|
...parametersSchema.map((schema: ParametersSchema) => {
|
||||||
|
const normalizedType = normalizeFormType(schema.type as string)
|
||||||
|
return {
|
||||||
|
...schema,
|
||||||
|
type: normalizedType,
|
||||||
|
tooltip: schema.description,
|
||||||
|
default: subscription.parameters?.[schema.name] || schema.default,
|
||||||
|
dynamicSelectParams: normalizedType === FormTypeEnum.dynamicSelect
|
||||||
|
? {
|
||||||
|
plugin_id: detail?.plugin_id || '',
|
||||||
|
provider: detail?.provider || '',
|
||||||
|
action: 'provider',
|
||||||
|
parameter: schema.name,
|
||||||
|
credential_id: subscription.id,
|
||||||
|
}
|
||||||
|
: undefined,
|
||||||
|
fieldClassName: schema.type === FormTypeEnum.boolean ? 'flex items-center justify-between' : undefined,
|
||||||
|
labelClassName: schema.type === FormTypeEnum.boolean ? 'mb-0' : undefined,
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
], [t, subscription.name, subscription.endpoint, subscription.parameters, subscription.id, parametersSchema, detail?.plugin_id, detail?.provider])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title={t('pluginTrigger.subscription.list.item.actions.edit.title')}
|
||||||
|
confirmButtonText={isUpdating ? t('common.operation.saving') : t('common.operation.save')}
|
||||||
|
onClose={onClose}
|
||||||
|
onCancel={onClose}
|
||||||
|
onConfirm={handleConfirm}
|
||||||
|
disabled={isUpdating}
|
||||||
|
clickOutsideNotClose
|
||||||
|
wrapperClassName='!z-[101]'
|
||||||
|
>
|
||||||
|
{pluginDetail && (
|
||||||
|
<ReadmeEntrance pluginDetail={pluginDetail} showType={ReadmeShowType.modal} />
|
||||||
|
)}
|
||||||
|
<BaseForm
|
||||||
|
formSchemas={formSchemas}
|
||||||
|
ref={formRef}
|
||||||
|
labelClassName='system-sm-medium mb-2 flex items-center gap-1 text-text-primary'
|
||||||
|
formClassName='space-y-4'
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ import {
|
|||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { DeleteConfirm } from './delete-confirm'
|
import { DeleteConfirm } from './delete-confirm'
|
||||||
import { EditModal } from './edit-modal'
|
import { EditModal } from './edit'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
data: TriggerSubscription
|
data: TriggerSubscription
|
||||||
|
|||||||
@@ -180,6 +180,24 @@ export const useVerifyAndUpdateTriggerSubscriptionBuilder = () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const useVerifyTriggerSubscription = () => {
|
||||||
|
return useMutation({
|
||||||
|
mutationKey: [NAME_SPACE, 'verify-subscription'],
|
||||||
|
mutationFn: (payload: {
|
||||||
|
provider: string;
|
||||||
|
subscriptionId: string;
|
||||||
|
credentials?: Record<string, any>;
|
||||||
|
}) => {
|
||||||
|
const { provider, subscriptionId, ...body } = payload
|
||||||
|
return post<{ verified: boolean }>(
|
||||||
|
`/workspaces/current/trigger-provider/${provider}/subscriptions/verify/${subscriptionId}`,
|
||||||
|
{ body },
|
||||||
|
{ silent: true },
|
||||||
|
)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export type BuildTriggerSubscriptionPayload = {
|
export type BuildTriggerSubscriptionPayload = {
|
||||||
provider: string
|
provider: string
|
||||||
subscriptionBuilderId: string
|
subscriptionBuilderId: string
|
||||||
@@ -215,6 +233,7 @@ export type UpdateTriggerSubscriptionPayload = {
|
|||||||
subscriptionId: string
|
subscriptionId: string
|
||||||
name?: string
|
name?: string
|
||||||
properties?: Record<string, any>
|
properties?: Record<string, any>
|
||||||
|
parameters?: Record<string, any>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useUpdateTriggerSubscription = () => {
|
export const useUpdateTriggerSubscription = () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user