mirror of https://github.com/langgenius/dify.git
fix: oauth subscription
This commit is contained in:
parent
1a9798c559
commit
6b94d30a5f
|
|
@ -58,6 +58,7 @@ const CustomSelect = <T extends Option>({
|
|||
onOpenChange,
|
||||
placement,
|
||||
offset,
|
||||
triggerPopupSameWidth = true,
|
||||
} = containerProps || {}
|
||||
const {
|
||||
className: triggerClassName,
|
||||
|
|
@ -85,6 +86,7 @@ const CustomSelect = <T extends Option>({
|
|||
offset={offset || 4}
|
||||
open={mergedOpen}
|
||||
onOpenChange={handleOpenChange}
|
||||
triggerPopupSameWidth={triggerPopupSameWidth}
|
||||
>
|
||||
<PortalToFollowElemTrigger
|
||||
onClick={() => handleOpenChange(!mergedOpen)}
|
||||
|
|
|
|||
|
|
@ -1,311 +0,0 @@
|
|||
'use client'
|
||||
import React, { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiArrowLeftLine,
|
||||
RiArrowRightLine,
|
||||
RiCloseLine,
|
||||
} from '@remixicon/react'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import Form from '@/app/components/base/form/form-scenarios/auth'
|
||||
import type { FormRefObject } from '@/app/components/base/form/types'
|
||||
import {
|
||||
useBuildTriggerSubscription,
|
||||
useCreateTriggerSubscriptionBuilder,
|
||||
useVerifyTriggerSubscriptionBuilder,
|
||||
} from '@/service/use-triggers'
|
||||
import { TriggerCredentialTypeEnum } from '@/app/components/workflow/block-selector/types'
|
||||
import { usePluginStore } from '../../store'
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
onSuccess: () => void
|
||||
}
|
||||
|
||||
enum ApiKeyStep {
|
||||
Verify = 'verify',
|
||||
Configuration = 'configuration',
|
||||
}
|
||||
|
||||
export const ApiKeyCreateModal = ({ onClose, onSuccess }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const detail = usePluginStore(state => state.detail)
|
||||
// State
|
||||
const [currentStep, setCurrentStep] = useState<ApiKeyStep>(ApiKeyStep.Verify)
|
||||
const [subscriptionName, setSubscriptionName] = useState('')
|
||||
const [subscriptionBuilder, setSubscriptionBuilder] = useState<any>(null)
|
||||
const [verificationError, setVerificationError] = useState<string>('')
|
||||
|
||||
// Form refs
|
||||
const credentialsFormRef = React.useRef<FormRefObject>(null)
|
||||
const parametersFormRef = React.useRef<FormRefObject>(null)
|
||||
|
||||
// API mutations
|
||||
const { mutate: createBuilder, isPending: isCreatingBuilder } = useCreateTriggerSubscriptionBuilder()
|
||||
const { mutate: verifyBuilder, isPending: isVerifying } = useVerifyTriggerSubscriptionBuilder()
|
||||
const { mutate: buildSubscription, isPending: isBuilding } = useBuildTriggerSubscription()
|
||||
|
||||
// Get provider name and schemas
|
||||
const providerName = `${detail?.plugin_id}/${detail?.declaration.name}`
|
||||
const credentialsSchema = detail?.declaration.trigger?.credentials_schema || []
|
||||
const parametersSchema = detail?.declaration.trigger?.subscription_schema?.parameters_schema || []
|
||||
|
||||
const handleVerify = () => {
|
||||
const credentialsFormValues = credentialsFormRef.current?.getFormValues({}) || { values: {}, isCheckValidated: false }
|
||||
const credentials = credentialsFormValues.values
|
||||
|
||||
if (!Object.keys(credentials).length) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: 'Please fill in all required credentials',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
setVerificationError('')
|
||||
|
||||
// First create builder
|
||||
createBuilder(
|
||||
{
|
||||
provider: providerName,
|
||||
credential_type: TriggerCredentialTypeEnum.ApiKey,
|
||||
},
|
||||
{
|
||||
onSuccess: (response) => {
|
||||
const builder = response.subscription_builder
|
||||
setSubscriptionBuilder(builder)
|
||||
|
||||
// setCurrentStep('configuration')
|
||||
|
||||
verifyBuilder(
|
||||
{
|
||||
provider: providerName,
|
||||
subscriptionBuilderId: builder.id,
|
||||
credentials,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
Toast.notify({
|
||||
type: 'success',
|
||||
message: t('pluginTrigger.modal.apiKey.verify.success'),
|
||||
})
|
||||
setCurrentStep(ApiKeyStep.Configuration)
|
||||
},
|
||||
onError: (error: any) => {
|
||||
setVerificationError(error?.message || t('pluginTrigger.modal.apiKey.verify.error'))
|
||||
},
|
||||
},
|
||||
)
|
||||
},
|
||||
onError: (error: any) => {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: error?.message || t('pluginTrigger.modal.errors.verifyFailed'),
|
||||
})
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const handleCreate = () => {
|
||||
if (!subscriptionName.trim()) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: t('pluginTrigger.modal.form.subscriptionName.required'),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!subscriptionBuilder)
|
||||
return
|
||||
|
||||
buildSubscription(
|
||||
{
|
||||
provider: providerName,
|
||||
subscriptionBuilderId: subscriptionBuilder.id,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
Toast.notify({
|
||||
type: 'success',
|
||||
message: 'Subscription created successfully',
|
||||
})
|
||||
onSuccess()
|
||||
onClose()
|
||||
},
|
||||
onError: (error: any) => {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: error?.message || t('modal.errors.createFailed'),
|
||||
})
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
const handleBack = () => {
|
||||
setCurrentStep(ApiKeyStep.Verify)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isShow
|
||||
onClose={onClose}
|
||||
className='!max-w-[520px] !p-0'
|
||||
wrapperClassName='!z-[1002]'
|
||||
>
|
||||
<div className='flex items-center justify-between border-b border-divider-subtle p-6 pb-4'>
|
||||
<div className='flex items-center gap-3'>
|
||||
{currentStep === ApiKeyStep.Configuration && (
|
||||
<Button variant='ghost' size='small' onClick={handleBack}>
|
||||
<RiArrowLeftLine className='h-4 w-4' />
|
||||
</Button>
|
||||
)}
|
||||
<h3 className='text-lg font-semibold text-text-primary'>
|
||||
{t('pluginTrigger.modal.apiKey.title')}
|
||||
</h3>
|
||||
</div>
|
||||
<Button variant='ghost' size='small' onClick={onClose}>
|
||||
<RiCloseLine className='h-4 w-4' />
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{/* Step indicator */}
|
||||
<div className='border-b border-divider-subtle px-6 py-4'>
|
||||
<div className='flex items-center gap-4'>
|
||||
<div className={`flex items-center gap-2 ${currentStep === ApiKeyStep.Verify ? 'text-text-accent' : currentStep === ApiKeyStep.Configuration ? 'text-text-success' : 'text-text-tertiary'}`}>
|
||||
<div className={`flex h-6 w-6 items-center justify-center rounded-full text-xs font-medium ${currentStep === ApiKeyStep.Verify
|
||||
? 'bg-util-accent-light-blue text-util-accent-blue'
|
||||
: currentStep === ApiKeyStep.Configuration
|
||||
? 'bg-state-success-bg text-state-success-text'
|
||||
: 'bg-background-default-subtle text-text-tertiary'}`}>
|
||||
1
|
||||
</div>
|
||||
<span className='system-sm-medium'>{t('pluginTrigger.modal.steps.verify')}</span>
|
||||
</div>
|
||||
|
||||
<div className='h-px flex-1 bg-divider-subtle'></div>
|
||||
|
||||
<div className={`flex items-center gap-2 ${currentStep === ApiKeyStep.Configuration ? 'text-text-accent' : 'text-text-tertiary'}`}>
|
||||
<div className={`flex h-6 w-6 items-center justify-center rounded-full text-xs font-medium ${currentStep === ApiKeyStep.Configuration
|
||||
? 'bg-util-accent-light-blue text-util-accent-blue'
|
||||
: 'bg-background-default-subtle text-text-tertiary'}`}>
|
||||
2
|
||||
</div>
|
||||
<span className='system-sm-medium'>{t('pluginTrigger.modal.steps.configuration')}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='p-6'>
|
||||
{currentStep === ApiKeyStep.Verify ? (
|
||||
// Step 1: Verify Credentials
|
||||
<div>
|
||||
|
||||
{credentialsSchema.length > 0 && (
|
||||
<div className='mb-4'>
|
||||
<Form
|
||||
formSchemas={credentialsSchema}
|
||||
ref={credentialsFormRef}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{verificationError && (
|
||||
<div className='bg-state-destructive-bg mb-4 rounded-lg border border-state-destructive-border p-3'>
|
||||
<div className='text-state-destructive-text system-xs-medium'>
|
||||
{verificationError}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
// Step 2: Configuration
|
||||
<div>
|
||||
{/* <div className='mb-4'>
|
||||
<h4 className='system-sm-semibold mb-2 text-text-primary'>
|
||||
{t('pluginTrigger.modal.apiKey.configuration.title')}
|
||||
</h4>
|
||||
<p className='system-xs-regular text-text-secondary'>
|
||||
{t('pluginTrigger.modal.apiKey.configuration.description')}
|
||||
</p>
|
||||
</div> */}
|
||||
|
||||
{/* Subscription Name */}
|
||||
<div className='mb-4'>
|
||||
<label className='system-sm-medium mb-2 block text-text-primary'>
|
||||
{t('pluginTrigger.modal.form.subscriptionName.label')}
|
||||
</label>
|
||||
<Input
|
||||
value={subscriptionName}
|
||||
onChange={e => setSubscriptionName(e.target.value)}
|
||||
placeholder={t('pluginTrigger.modal.form.subscriptionName.placeholder')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Callback URL (read-only) */}
|
||||
{subscriptionBuilder?.endpoint && (
|
||||
<div className='mb-4'>
|
||||
<label className='system-sm-medium mb-2 block text-text-primary'>
|
||||
{t('pluginTrigger.modal.form.callbackUrl.label')}
|
||||
</label>
|
||||
<Input
|
||||
value={subscriptionBuilder.endpoint}
|
||||
readOnly
|
||||
className='bg-background-section'
|
||||
/>
|
||||
<div className='system-xs-regular mt-1 text-text-tertiary'>
|
||||
{t('pluginTrigger.modal.form.callbackUrl.description')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Dynamic Parameters Form */}
|
||||
{parametersSchema.length > 0 && (
|
||||
<div className='mb-4'>
|
||||
<div className='system-sm-medium mb-3 text-text-primary'>
|
||||
Subscription Parameters
|
||||
</div>
|
||||
<Form
|
||||
formSchemas={parametersSchema}
|
||||
ref={parametersFormRef}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className='flex justify-end gap-2 border-t border-divider-subtle p-6 pt-4'>
|
||||
<Button variant='secondary' onClick={onClose}>
|
||||
{t('pluginTrigger.modal.common.cancel')}
|
||||
</Button>
|
||||
|
||||
{currentStep === ApiKeyStep.Verify ? (
|
||||
<Button
|
||||
variant='primary'
|
||||
onClick={handleVerify}
|
||||
loading={isCreatingBuilder || isVerifying}
|
||||
// disabled={credentialsSchema.length > 0}
|
||||
>
|
||||
{isVerifying ? t('pluginTrigger.modal.common.verifying') : t('pluginTrigger.modal.common.verify')}
|
||||
<RiArrowRightLine className='ml-2 h-4 w-4' />
|
||||
</Button>
|
||||
) : (
|
||||
<Button
|
||||
variant='primary'
|
||||
onClick={handleCreate}
|
||||
loading={isBuilding}
|
||||
disabled={!subscriptionName.trim()}
|
||||
>
|
||||
{isBuilding ? t('pluginTrigger.modal.common.creating') : t('pluginTrigger.modal.common.create')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
|
@ -18,12 +18,13 @@ import {
|
|||
import { RiLoader2Line } from '@remixicon/react'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { usePluginStore } from '../../store'
|
||||
import { usePluginStore, usePluginSubscriptionStore } from '../../store'
|
||||
import LogViewer from '../log-viewer'
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
createType: SupportedCreationMethods
|
||||
builder?: TriggerSubscriptionBuilder
|
||||
}
|
||||
|
||||
const CREDENTIAL_TYPE_MAP: Record<SupportedCreationMethods, TriggerCredentialTypeEnum> = {
|
||||
|
|
@ -58,14 +59,15 @@ const MultiSteps = ({ currentStep }: { currentStep: ApiKeyStep }) => {
|
|||
</div>
|
||||
}
|
||||
|
||||
export const CommonCreateModal = ({ onClose, createType }: Props) => {
|
||||
export const CommonCreateModal = ({ onClose, createType, builder }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const detail = usePluginStore(state => state.detail)
|
||||
const { refresh } = usePluginSubscriptionStore()
|
||||
|
||||
const [currentStep, setCurrentStep] = useState<ApiKeyStep>(createType === SupportedCreationMethods.APIKEY ? ApiKeyStep.Verify : ApiKeyStep.Configuration)
|
||||
|
||||
const [subscriptionName, setSubscriptionName] = useState('')
|
||||
const [subscriptionBuilder, setSubscriptionBuilder] = useState<TriggerSubscriptionBuilder | undefined>()
|
||||
const [subscriptionBuilder, setSubscriptionBuilder] = useState<TriggerSubscriptionBuilder | undefined>(builder)
|
||||
const [verificationError, setVerificationError] = useState<string>('')
|
||||
|
||||
const { mutate: verifyCredentials, isPending: isVerifyingCredentials } = useVerifyTriggerSubscriptionBuilder()
|
||||
|
|
@ -184,8 +186,8 @@ export const CommonCreateModal = ({ onClose, createType }: Props) => {
|
|||
type: 'success',
|
||||
message: 'Subscription created successfully',
|
||||
})
|
||||
// onSuccess()
|
||||
onClose()
|
||||
refresh?.()
|
||||
},
|
||||
onError: (error: any) => {
|
||||
Toast.notify({
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import { ActionButton } from '@/app/components/base/action-button'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import { Button } from '@/app/components/base/button'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import { PortalSelect } from '@/app/components/base/select'
|
||||
import type { Option } from '@/app/components/base/select/custom'
|
||||
import CustomSelect from '@/app/components/base/select/custom'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
import type { TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types'
|
||||
import { openOAuthPopup } from '@/hooks/use-oauth'
|
||||
import { useInitiateTriggerOAuth, useTriggerOAuthConfig, useTriggerProviderInfo } from '@/service/use-triggers'
|
||||
import cn from '@/utils/classnames'
|
||||
import { RiAddLine, RiCloseLine, RiEqualizer2Line } from '@remixicon/react'
|
||||
import { RiAddLine, RiEqualizer2Line } from '@remixicon/react'
|
||||
import { useBoolean } from 'ahooks'
|
||||
import { useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
|
@ -16,30 +18,6 @@ import { usePluginStore } from '../../store'
|
|||
import { CommonCreateModal } from './common-modal'
|
||||
import { OAuthClientSettingsModal } from './oauth-client'
|
||||
|
||||
export const CreateModal = () => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isShow
|
||||
// onClose={onClose}
|
||||
className='!max-w-[520px] p-6'
|
||||
wrapperClassName='!z-[1002]'
|
||||
>
|
||||
<div className='flex items-center justify-between pb-3'>
|
||||
<h3 className='text-lg font-semibold text-text-primary'>
|
||||
{t('pluginTrigger.modal.oauth.title')}
|
||||
</h3>
|
||||
<ActionButton
|
||||
// onClick={onClose}
|
||||
>
|
||||
<RiCloseLine className='h-4 w-4' />
|
||||
</ActionButton>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export enum CreateButtonType {
|
||||
FULL_BUTTON = 'full-button',
|
||||
ICON_BUTTON = 'icon-button',
|
||||
|
|
@ -67,7 +45,7 @@ export const DEFAULT_METHOD = 'default'
|
|||
*/
|
||||
export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BUTTON }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const [selectedCreateType, setSelectedCreateType] = useState<SupportedCreationMethods | null>(null)
|
||||
const [selectedCreateInfo, setSelectedCreateInfo] = useState<{ type: SupportedCreationMethods, builder?: TriggerSubscriptionBuilder } | null>(null)
|
||||
|
||||
const detail = usePluginStore(state => state.detail)
|
||||
const provider = `${detail?.plugin_id}/${detail?.declaration.name}`
|
||||
|
|
@ -99,25 +77,32 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU
|
|||
showClientSettingsModal()
|
||||
}
|
||||
|
||||
const allOptions = [
|
||||
{
|
||||
value: SupportedCreationMethods.OAUTH,
|
||||
name: t('pluginTrigger.subscription.addType.options.oauth.title'),
|
||||
extra: <ActionButton onClick={onClickClientSettings}><RiEqualizer2Line className='h-4 w-4 text-text-tertiary' /></ActionButton>,
|
||||
show: supportedMethods.includes(SupportedCreationMethods.OAUTH),
|
||||
},
|
||||
{
|
||||
value: SupportedCreationMethods.APIKEY,
|
||||
name: t('pluginTrigger.subscription.addType.options.apiKey.title'),
|
||||
show: supportedMethods.includes(SupportedCreationMethods.APIKEY),
|
||||
},
|
||||
{
|
||||
value: SupportedCreationMethods.MANUAL,
|
||||
name: t('pluginTrigger.subscription.addType.options.manual.description'), // 使用 description 作为标题
|
||||
tooltip: <Tooltip popupContent={t('pluginTrigger.subscription.addType.options.manual.tip')} />,
|
||||
show: supportedMethods.includes(SupportedCreationMethods.MANUAL),
|
||||
},
|
||||
]
|
||||
const allOptions = useMemo(() => {
|
||||
const showCustomBadge = oauthConfig?.custom_enabled && oauthConfig?.custom_configured
|
||||
|
||||
return [
|
||||
{
|
||||
value: SupportedCreationMethods.OAUTH,
|
||||
label: t('pluginTrigger.subscription.addType.options.oauth.title'),
|
||||
tag: !showCustomBadge ? null : <Badge className='ml-1 mr-0.5'>
|
||||
{t('plugin.auth.custom')}
|
||||
</Badge>,
|
||||
extra: <ActionButton onClick={onClickClientSettings}><RiEqualizer2Line className='h-4 w-4 text-text-tertiary' /></ActionButton>,
|
||||
show: supportedMethods.includes(SupportedCreationMethods.OAUTH),
|
||||
},
|
||||
{
|
||||
value: SupportedCreationMethods.APIKEY,
|
||||
label: t('pluginTrigger.subscription.addType.options.apiKey.title'),
|
||||
show: supportedMethods.includes(SupportedCreationMethods.APIKEY),
|
||||
},
|
||||
{
|
||||
value: SupportedCreationMethods.MANUAL,
|
||||
label: t('pluginTrigger.subscription.addType.options.manual.description'), // 使用 description 作为标题
|
||||
extra: <Tooltip popupContent={t('pluginTrigger.subscription.addType.options.manual.tip')} />,
|
||||
show: supportedMethods.includes(SupportedCreationMethods.MANUAL),
|
||||
},
|
||||
]
|
||||
}, [t, oauthConfig, supportedMethods, methodType])
|
||||
|
||||
const onChooseCreateType = (type: SupportedCreationMethods) => {
|
||||
if (type === SupportedCreationMethods.OAUTH) {
|
||||
|
|
@ -130,7 +115,7 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU
|
|||
type: 'success',
|
||||
message: t('pluginTrigger.modal.oauth.authorized'),
|
||||
})
|
||||
setSelectedCreateType(SupportedCreationMethods.OAUTH)
|
||||
setSelectedCreateInfo({ type: SupportedCreationMethods.OAUTH, builder: response.subscription_builder })
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
@ -147,7 +132,7 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU
|
|||
}
|
||||
}
|
||||
else {
|
||||
setSelectedCreateType(type)
|
||||
setSelectedCreateInfo({ type })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,9 +149,23 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU
|
|||
return null
|
||||
|
||||
return <>
|
||||
<PortalSelect
|
||||
readonly={methodType !== DEFAULT_METHOD}
|
||||
renderTrigger={() => {
|
||||
<CustomSelect<Option & { show: boolean; extra?: React.ReactNode; tag?: React.ReactNode }>
|
||||
options={allOptions.filter(option => option.show)}
|
||||
value={methodType}
|
||||
onChange={value => onChooseCreateType(value as any)}
|
||||
containerProps={{
|
||||
open: methodType === DEFAULT_METHOD ? undefined : false,
|
||||
placement: 'bottom-start',
|
||||
offset: 4,
|
||||
triggerPopupSameWidth: buttonType === CreateButtonType.FULL_BUTTON,
|
||||
}}
|
||||
triggerProps={{
|
||||
className: cn('h-8 bg-transparent px-0 hover:bg-transparent', methodType !== DEFAULT_METHOD && 'pointer-events-none', buttonType === CreateButtonType.FULL_BUTTON && 'grow'),
|
||||
}}
|
||||
popupProps={{
|
||||
wrapperClassName: 'z-[1000]',
|
||||
}}
|
||||
CustomTrigger={() => {
|
||||
return buttonType === CreateButtonType.FULL_BUTTON ? (
|
||||
<Button
|
||||
variant='primary'
|
||||
|
|
@ -174,35 +173,47 @@ export const CreateSubscriptionButton = ({ buttonType = CreateButtonType.FULL_BU
|
|||
className='w-full'
|
||||
onClick={onClickCreate}
|
||||
>
|
||||
<RiAddLine className='mr-2 h-4 w-4' />
|
||||
<RiAddLine className='mr-2 size-4' />
|
||||
{buttonTextMap[methodType]}
|
||||
{methodType === SupportedCreationMethods.OAUTH && oauthConfig?.custom_enabled && oauthConfig?.custom_configured && <Badge
|
||||
className='ml-1 mr-0.5 border-text-primary-on-surface bg-components-badge-bg-dimm text-text-primary-on-surface'
|
||||
>
|
||||
{t('plugin.auth.custom')}
|
||||
</Badge>}
|
||||
{methodType === SupportedCreationMethods.OAUTH
|
||||
&& <ActionButton onClick={onClickClientSettings}>
|
||||
<RiEqualizer2Line className='h-4 w-4 text-text-tertiary' />
|
||||
<RiEqualizer2Line className='size-4 text-text-tertiary' />
|
||||
</ActionButton>
|
||||
}
|
||||
</Button>
|
||||
) : <ActionButton onClick={onClickCreate}>
|
||||
<RiAddLine className='h-4 w-4' />
|
||||
</ActionButton>
|
||||
) : (
|
||||
<ActionButton onClick={onClickCreate} className='float-right'>
|
||||
<RiAddLine className='size-4' />
|
||||
</ActionButton>
|
||||
)
|
||||
}}
|
||||
triggerClassName='h-8'
|
||||
popupClassName={cn('z-[1000]')}
|
||||
value={methodType}
|
||||
items={allOptions.filter(option => option.show)}
|
||||
onSelect={item => onChooseCreateType(item.value as any)}
|
||||
CustomOption={option => (
|
||||
<>
|
||||
<div className='mr-8 flex grow items-center gap-1 truncate px-1'>
|
||||
{option.label}
|
||||
{option.tag}
|
||||
</div>
|
||||
{option.extra}
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
{selectedCreateType && (
|
||||
{selectedCreateInfo && (
|
||||
<CommonCreateModal
|
||||
createType={selectedCreateType}
|
||||
onClose={() => setSelectedCreateType(null)}
|
||||
createType={selectedCreateInfo.type}
|
||||
builder={selectedCreateInfo.builder}
|
||||
onClose={() => setSelectedCreateInfo(null)}
|
||||
/>
|
||||
)}
|
||||
{isShowClientSettingsModal && (
|
||||
<OAuthClientSettingsModal
|
||||
oauthConfig={oauthConfig}
|
||||
onClose={hideClientSettingsModal}
|
||||
showOAuthCreateModal={() => setSelectedCreateType(SupportedCreationMethods.OAUTH)}
|
||||
showOAuthCreateModal={builder => setSelectedCreateInfo({ type: SupportedCreationMethods.OAUTH, builder })}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,219 +0,0 @@
|
|||
'use client'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiCloseLine,
|
||||
RiLoader2Line,
|
||||
} from '@remixicon/react'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import {
|
||||
useBuildTriggerSubscription,
|
||||
useCreateTriggerSubscriptionBuilder,
|
||||
useTriggerSubscriptionBuilderLogs,
|
||||
} from '@/service/use-triggers'
|
||||
import type { TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types'
|
||||
import { TriggerCredentialTypeEnum } from '@/app/components/workflow/block-selector/types'
|
||||
import { BaseForm } from '@/app/components/base/form/components/base'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import { CopyFeedbackNew } from '@/app/components/base/copy-feedback'
|
||||
import type { FormRefObject } from '@/app/components/base/form/types'
|
||||
import LogViewer from '../log-viewer'
|
||||
import { usePluginStore } from '../../store'
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
onSuccess: () => void
|
||||
}
|
||||
|
||||
export const ManualCreateModal = ({ onClose, onSuccess }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const detail = usePluginStore(state => state.detail)
|
||||
|
||||
const [subscriptionName, setSubscriptionName] = useState('')
|
||||
const [subscriptionBuilder, setSubscriptionBuilder] = useState<TriggerSubscriptionBuilder | undefined>()
|
||||
|
||||
const { mutate: createBuilder /* isPending: isCreatingBuilder */ } = useCreateTriggerSubscriptionBuilder()
|
||||
const { mutate: buildSubscription, isPending: isBuilding } = useBuildTriggerSubscription()
|
||||
|
||||
const providerName = `${detail?.plugin_id}/${detail?.declaration.name}`
|
||||
const propertiesSchema = detail?.declaration.trigger.subscription_schema.properties_schema || []
|
||||
const propertiesFormRef = React.useRef<FormRefObject>(null)
|
||||
|
||||
const { data: logData } = useTriggerSubscriptionBuilderLogs(
|
||||
providerName,
|
||||
subscriptionBuilder?.id || '',
|
||||
{
|
||||
enabled: !!subscriptionBuilder?.id,
|
||||
refetchInterval: 3000,
|
||||
},
|
||||
)
|
||||
|
||||
const logs = logData?.logs || []
|
||||
|
||||
useEffect(() => {
|
||||
if (!subscriptionBuilder) {
|
||||
createBuilder(
|
||||
{
|
||||
provider: providerName,
|
||||
credential_type: TriggerCredentialTypeEnum.Unauthorized,
|
||||
},
|
||||
{
|
||||
onSuccess: (response) => {
|
||||
const builder = response.subscription_builder
|
||||
setSubscriptionBuilder(builder)
|
||||
},
|
||||
onError: (error) => {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: t('pluginTrigger.modal.errors.createFailed'),
|
||||
})
|
||||
console.error('Failed to create subscription builder:', error)
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
}, [createBuilder, providerName, subscriptionBuilder, t])
|
||||
|
||||
const handleCreate = () => {
|
||||
if (!subscriptionName.trim()) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: t('pluginTrigger.modal.form.subscriptionName.required'),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!subscriptionBuilder)
|
||||
return
|
||||
|
||||
const formValues = propertiesFormRef.current?.getFormValues({}) || { values: {}, isCheckValidated: false }
|
||||
if (!formValues.isCheckValidated) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: t('pluginTrigger.modal.form.properties.required'),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
buildSubscription(
|
||||
{
|
||||
provider: providerName,
|
||||
subscriptionBuilderId: subscriptionBuilder.id,
|
||||
params: {
|
||||
name: subscriptionName,
|
||||
properties: formValues.values,
|
||||
},
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
Toast.notify({
|
||||
type: 'success',
|
||||
message: 'Subscription created successfully',
|
||||
})
|
||||
onSuccess()
|
||||
onClose()
|
||||
},
|
||||
onError: (error: any) => {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: error?.message || t('pluginTrigger.modal.errors.createFailed'),
|
||||
})
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isShow
|
||||
onClose={onClose}
|
||||
className='!max-w-[640px] !p-0'
|
||||
wrapperClassName='!z-[1002]'
|
||||
>
|
||||
<div className='flex items-center justify-between p-6 pb-3'>
|
||||
<h3 className='text-lg font-semibold text-text-primary'>
|
||||
{t('pluginTrigger.modal.manual.title')}
|
||||
</h3>
|
||||
<ActionButton onClick={onClose} >
|
||||
<RiCloseLine className='h-4 w-4' />
|
||||
</ActionButton>
|
||||
</div>
|
||||
|
||||
<div className='max-h-[70vh] overflow-y-auto p-6 pt-2'>
|
||||
<div className='mb-6'>
|
||||
<label className='system-sm-medium mb-2 block text-text-primary'>
|
||||
{t('pluginTrigger.modal.form.subscriptionName.label')}
|
||||
</label>
|
||||
<Input
|
||||
value={subscriptionName}
|
||||
onChange={e => setSubscriptionName(e.target.value)}
|
||||
placeholder={t('pluginTrigger.modal.form.subscriptionName.placeholder')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className='mb-6'>
|
||||
<label className='system-sm-medium mb-2 block text-text-primary'>
|
||||
{t('pluginTrigger.modal.form.callbackUrl.label')}
|
||||
</label>
|
||||
<div className='relative'>
|
||||
<Input
|
||||
value={subscriptionBuilder?.endpoint}
|
||||
readOnly
|
||||
className='pr-12'
|
||||
placeholder={t('pluginTrigger.modal.form.callbackUrl.placeholder')}
|
||||
/>
|
||||
<CopyFeedbackNew className='absolute right-1 top-1/2 h-4 w-4 -translate-y-1/2 text-text-tertiary' content={subscriptionBuilder?.endpoint || ''} />
|
||||
</div>
|
||||
<div className='system-xs-regular mt-1 text-text-tertiary'>
|
||||
{t('pluginTrigger.modal.form.callbackUrl.description')}
|
||||
</div>
|
||||
</div>
|
||||
{propertiesSchema.length > 0 && (
|
||||
<div className='mb-6'>
|
||||
<BaseForm
|
||||
formSchemas={propertiesSchema}
|
||||
ref={propertiesFormRef}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className='mb-6'>
|
||||
<div className='mb-3 flex items-center gap-2'>
|
||||
<div className='system-xs-medium-uppercase text-text-tertiary'>
|
||||
REQUESTS HISTORY
|
||||
</div>
|
||||
<div className='h-px flex-1 bg-gradient-to-r from-divider-regular to-transparent' />
|
||||
</div>
|
||||
|
||||
<div className='mb-1 flex items-center justify-center gap-1 rounded-lg bg-background-section p-3'>
|
||||
<div className='h-3.5 w-3.5'>
|
||||
<RiLoader2Line className='h-full w-full animate-spin' />
|
||||
</div>
|
||||
<div className='system-xs-regular text-text-tertiary'>
|
||||
Awaiting request from {detail?.declaration.name}...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LogViewer logs={logs} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='flex justify-end gap-2 p-6 pt-4'>
|
||||
<Button variant='secondary' onClick={onClose}>
|
||||
{t('pluginTrigger.modal.common.cancel')}
|
||||
</Button>
|
||||
<Button
|
||||
variant='primary'
|
||||
onClick={handleCreate}
|
||||
loading={isBuilding}
|
||||
disabled={!subscriptionName.trim() || !subscriptionBuilder}
|
||||
>
|
||||
{isBuilding ? t('pluginTrigger.modal.common.creating') : t('pluginTrigger.modal.common.create')}
|
||||
</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ import { usePluginStore } from '../../store'
|
|||
type Props = {
|
||||
oauthConfig?: TriggerOAuthConfig
|
||||
onClose: () => void
|
||||
showOAuthCreateModal: () => void
|
||||
showOAuthCreateModal: (builder: TriggerSubscriptionBuilder) => void
|
||||
}
|
||||
|
||||
enum AuthorizationStatusEnum {
|
||||
|
|
@ -68,7 +68,7 @@ export const OAuthClientSettingsModal = ({ oauthConfig, onClose, showOAuthCreate
|
|||
message: t('pluginTrigger.modal.oauth.authorization.authSuccess'),
|
||||
})
|
||||
onClose()
|
||||
showOAuthCreateModal()
|
||||
showOAuthCreateModal(response.subscription_builder)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
|
@ -198,7 +198,7 @@ export const OAuthClientSettingsModal = ({ oauthConfig, onClose, showOAuthCreate
|
|||
/>
|
||||
))}
|
||||
</div>
|
||||
{oauthConfig?.redirect_uri && (
|
||||
{clientType === ClientTypeEnum.Custom && oauthConfig?.redirect_uri && (
|
||||
<div className='mb-4 flex items-start gap-3 rounded-xl bg-background-section-burn p-4'>
|
||||
<div className='rounded-lg border-[0.5px] border-components-card-border bg-components-card-bg p-2 shadow-xs shadow-shadow-shadow-3'>
|
||||
<RiInformation2Fill className='h-5 w-5 shrink-0 text-text-accent' />
|
||||
|
|
|
|||
|
|
@ -1,289 +0,0 @@
|
|||
'use client'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
RiClipboardLine,
|
||||
RiCloseLine,
|
||||
RiInformation2Fill,
|
||||
} from '@remixicon/react'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
import Button from '@/app/components/base/button'
|
||||
import Input from '@/app/components/base/input'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import Form from '@/app/components/base/form/form-scenarios/auth'
|
||||
import type { FormRefObject } from '@/app/components/base/form/types'
|
||||
import {
|
||||
useBuildTriggerSubscription,
|
||||
useInitiateTriggerOAuth,
|
||||
useVerifyTriggerSubscriptionBuilder,
|
||||
} from '@/service/use-triggers'
|
||||
import ActionButton from '@/app/components/base/action-button'
|
||||
import type { TriggerOAuthConfig, TriggerSubscriptionBuilder } from '@/app/components/workflow/block-selector/types'
|
||||
import { usePluginStore } from '../../store'
|
||||
|
||||
type Props = {
|
||||
oauthConfig?: TriggerOAuthConfig
|
||||
onClose: () => void
|
||||
onSuccess: () => void
|
||||
}
|
||||
|
||||
enum OAuthStepEnum {
|
||||
Setup = 'setup',
|
||||
Configuration = 'configuration',
|
||||
}
|
||||
|
||||
enum AuthorizationStatusEnum {
|
||||
Pending = 'pending',
|
||||
Success = 'success',
|
||||
Failed = 'failed',
|
||||
}
|
||||
|
||||
export const OAuthCreateModal = ({ oauthConfig, onClose, onSuccess }: Props) => {
|
||||
const { t } = useTranslation()
|
||||
const detail = usePluginStore(state => state.detail)
|
||||
const [currentStep, setCurrentStep] = useState<OAuthStepEnum>(OAuthStepEnum.Setup)
|
||||
const [subscriptionName, setSubscriptionName] = useState('')
|
||||
const [authorizationUrl, setAuthorizationUrl] = useState('')
|
||||
const [subscriptionBuilder, setSubscriptionBuilder] = useState<TriggerSubscriptionBuilder | undefined>()
|
||||
const [authorizationStatus, setAuthorizationStatus] = useState<AuthorizationStatusEnum>()
|
||||
|
||||
const clientFormRef = React.useRef<FormRefObject>(null)
|
||||
const parametersFormRef = React.useRef<FormRefObject>(null)
|
||||
|
||||
const providerName = `${detail?.plugin_id}/${detail?.declaration.name}`
|
||||
const clientSchema = detail?.declaration.trigger?.oauth_schema?.client_schema || []
|
||||
const parametersSchema = detail?.declaration.trigger?.subscription_schema?.parameters_schema || []
|
||||
|
||||
const { mutate: initiateOAuth } = useInitiateTriggerOAuth()
|
||||
const { mutate: verifyBuilder } = useVerifyTriggerSubscriptionBuilder()
|
||||
const { mutate: buildSubscription, isPending: isBuilding } = useBuildTriggerSubscription()
|
||||
|
||||
useEffect(() => {
|
||||
initiateOAuth(providerName, {
|
||||
onSuccess: (response) => {
|
||||
setAuthorizationUrl(response.authorization_url)
|
||||
setSubscriptionBuilder(response.subscription_builder)
|
||||
},
|
||||
onError: (error: any) => {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: error?.message || t('pluginTrigger.modal.errors.authFailed'),
|
||||
})
|
||||
},
|
||||
})
|
||||
}, [initiateOAuth, providerName, t])
|
||||
|
||||
useEffect(() => {
|
||||
if (currentStep === OAuthStepEnum.Setup && subscriptionBuilder && authorizationStatus === AuthorizationStatusEnum.Pending) {
|
||||
const pollInterval = setInterval(() => {
|
||||
verifyBuilder(
|
||||
{
|
||||
provider: providerName,
|
||||
subscriptionBuilderId: subscriptionBuilder.id,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
setAuthorizationStatus(AuthorizationStatusEnum.Success)
|
||||
setCurrentStep(OAuthStepEnum.Configuration)
|
||||
Toast.notify({
|
||||
type: 'success',
|
||||
message: t('pluginTrigger.modal.oauth.authorization.authSuccess'),
|
||||
})
|
||||
clearInterval(pollInterval)
|
||||
},
|
||||
onError: () => {
|
||||
// Continue polling - auth might still be in progress
|
||||
},
|
||||
},
|
||||
)
|
||||
}, 3000)
|
||||
|
||||
return () => clearInterval(pollInterval)
|
||||
}
|
||||
}, [currentStep, subscriptionBuilder, authorizationStatus, verifyBuilder, providerName, t])
|
||||
|
||||
const handleAuthorize = () => {
|
||||
const clientFormValues = clientFormRef.current?.getFormValues({}) || { values: {}, isCheckValidated: false }
|
||||
const clientParams = clientFormValues.values
|
||||
|
||||
if (!Object.keys(clientParams).length) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: t('pluginTrigger.modal.oauth.authorization.authFailed'),
|
||||
})
|
||||
return
|
||||
}
|
||||
setAuthorizationStatus(AuthorizationStatusEnum.Pending)
|
||||
if (authorizationUrl) {
|
||||
// Open authorization URL in new window
|
||||
window.open(authorizationUrl, '_blank', 'width=500,height=600')
|
||||
}
|
||||
}
|
||||
|
||||
const handleCreate = () => {
|
||||
if (!subscriptionName.trim()) {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: t('pluginTrigger.modal.form.subscriptionName.required'),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if (!subscriptionBuilder)
|
||||
return
|
||||
|
||||
const parameters = parametersFormRef.current?.getFormValues({})?.values
|
||||
|
||||
buildSubscription(
|
||||
{
|
||||
provider: providerName,
|
||||
subscriptionBuilderId: subscriptionBuilder.id,
|
||||
params: {
|
||||
name: subscriptionName,
|
||||
parameters,
|
||||
} as Record<string, any>,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
Toast.notify({
|
||||
type: 'success',
|
||||
message: t('pluginTrigger.modal.oauth.configuration.success'),
|
||||
})
|
||||
onSuccess()
|
||||
onClose()
|
||||
},
|
||||
onError: (error: any) => {
|
||||
Toast.notify({
|
||||
type: 'error',
|
||||
message: error?.message || t('pluginTrigger.modal.errors.createFailed'),
|
||||
})
|
||||
},
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
isShow
|
||||
onClose={onClose}
|
||||
className='!max-w-[520px] p-6'
|
||||
wrapperClassName='!z-[1002]'
|
||||
>
|
||||
<div className='flex items-center justify-between pb-3'>
|
||||
<h3 className='text-lg font-semibold text-text-primary'>
|
||||
{t('pluginTrigger.modal.oauth.title')}
|
||||
</h3>
|
||||
<ActionButton onClick={onClose}>
|
||||
<RiCloseLine className='h-4 w-4' />
|
||||
</ActionButton>
|
||||
</div>
|
||||
|
||||
<div className='py-3'>
|
||||
{currentStep === OAuthStepEnum.Setup && (
|
||||
<>
|
||||
{oauthConfig?.redirect_uri && (
|
||||
<div className='mb-4 flex items-start gap-3 rounded-xl bg-background-section-burn p-4'>
|
||||
<div className='rounded-lg border-[0.5px] border-components-card-border bg-components-card-bg p-2 shadow-xs shadow-shadow-shadow-3'>
|
||||
<RiInformation2Fill className='h-5 w-5 shrink-0 text-text-accent' />
|
||||
</div>
|
||||
<div className='flex-1 text-text-secondary'>
|
||||
<div className='system-sm-regular whitespace-pre-wrap leading-4'>
|
||||
{t('pluginTrigger.modal.oauthRedirectInfo')}
|
||||
</div>
|
||||
<div className='system-sm-medium my-1.5 break-all leading-4'>
|
||||
{oauthConfig.redirect_uri}
|
||||
</div>
|
||||
<Button
|
||||
variant='secondary'
|
||||
size='small'
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(oauthConfig.redirect_uri)
|
||||
Toast.notify({
|
||||
type: 'success',
|
||||
message: t('common.actionMsg.copySuccessfully'),
|
||||
})
|
||||
}}>
|
||||
<RiClipboardLine className='mr-1 h-[14px] w-[14px]' />
|
||||
{t('common.operation.copy')}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{clientSchema.length > 0 && (
|
||||
<Form
|
||||
formSchemas={clientSchema}
|
||||
ref={clientFormRef}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{currentStep === OAuthStepEnum.Configuration && (
|
||||
<div>
|
||||
<div className='mb-4'>
|
||||
<label className='system-sm-medium mb-2 block text-text-primary'>
|
||||
{t('pluginTrigger.modal.form.subscriptionName.label')}
|
||||
</label>
|
||||
<Input
|
||||
value={subscriptionName}
|
||||
onChange={e => setSubscriptionName(e.target.value)}
|
||||
placeholder={t('pluginTrigger.modal.form.subscriptionName.placeholder')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{subscriptionBuilder?.endpoint && (
|
||||
<div className='mb-4'>
|
||||
<label className='system-sm-medium mb-2 block text-text-primary'>
|
||||
{t('pluginTrigger.modal.form.callbackUrl.label')}
|
||||
</label>
|
||||
<Input
|
||||
value={subscriptionBuilder.endpoint}
|
||||
readOnly
|
||||
className='bg-background-section'
|
||||
/>
|
||||
<div className='system-xs-regular mt-1 text-text-tertiary'>
|
||||
{t('pluginTrigger.modal.form.callbackUrl.description')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{parametersSchema.length > 0 && (
|
||||
<Form
|
||||
formSchemas={parametersSchema}
|
||||
ref={parametersFormRef}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className='flex justify-end gap-2 pt-5'>
|
||||
<Button variant='secondary' onClick={onClose}>
|
||||
{t('pluginTrigger.modal.common.cancel')}
|
||||
</Button>
|
||||
|
||||
{currentStep === OAuthStepEnum.Setup && (
|
||||
<Button
|
||||
variant='primary'
|
||||
onClick={handleAuthorize}
|
||||
loading={authorizationStatus === AuthorizationStatusEnum.Pending}
|
||||
>
|
||||
{authorizationStatus === AuthorizationStatusEnum.Pending ? t('pluginTrigger.modal.common.authorizing') : t('pluginTrigger.modal.common.authorize')}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{currentStep === OAuthStepEnum.Configuration && (
|
||||
<Button
|
||||
variant='primary'
|
||||
onClick={handleCreate}
|
||||
loading={isBuilding}
|
||||
disabled={!subscriptionName.trim()}
|
||||
>
|
||||
{isBuilding ? t('pluginTrigger.modal.common.creating') : t('pluginTrigger.modal.common.create')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
|
@ -40,7 +40,7 @@ export const SubscriptionList = () => {
|
|||
<div className='relative mb-3 flex items-center justify-between'>
|
||||
{
|
||||
hasSubscriptions
|
||||
&& <div className='flex items-center gap-1'>
|
||||
&& <div className='flex shrink-0 items-center gap-1'>
|
||||
<span className='system-sm-semibold-uppercase text-text-secondary'>
|
||||
{t('pluginTrigger.subscription.listNum', { num: subscriptions?.length || 0 })}
|
||||
</span>
|
||||
|
|
|
|||
Loading…
Reference in New Issue