mirror of
https://github.com/langgenius/dify.git
synced 2026-06-07 16:32:01 +08:00
234 lines
8.1 KiB
TypeScript
234 lines
8.1 KiB
TypeScript
import type { FC } from 'react'
|
|
import type {
|
|
ExternalDataTool,
|
|
} from '@/models/common'
|
|
import { Button } from '@langgenius/dify-ui/button'
|
|
import { Dialog, DialogContent } from '@langgenius/dify-ui/dialog'
|
|
import { Select, SelectContent, SelectItem, SelectItemIndicator, SelectItemText, SelectTrigger, SelectValue } from '@langgenius/dify-ui/select'
|
|
import { toast } from '@langgenius/dify-ui/toast'
|
|
import { noop } from 'es-toolkit/function'
|
|
import { useState } from 'react'
|
|
import { useTranslation } from 'react-i18next'
|
|
import AppIcon from '@/app/components/base/app-icon'
|
|
import EmojiPicker from '@/app/components/base/emoji-picker'
|
|
import FormGeneration from '@/app/components/base/features/new-feature-panel/moderation/form-generation'
|
|
import { BookOpen01 } from '@/app/components/base/icons/src/vender/line/education'
|
|
import { ApiBasedExtensionSelector } from '@/app/components/header/account-setting/api-based-extension-page/selector'
|
|
import { useDocLink, useLocale } from '@/context/i18n'
|
|
import { useCodeBasedExtensions } from '@/service/use-common'
|
|
import {
|
|
buildProviders,
|
|
formatExternalDataTool,
|
|
getProviderDefaultConfig,
|
|
getValidationError,
|
|
} from './external-data-tool-modal-utils'
|
|
|
|
type ExternalDataToolModalProps = {
|
|
data: ExternalDataTool
|
|
onCancel: () => void
|
|
onSave: (externalDataTool: ExternalDataTool) => void
|
|
onValidateBeforeSave?: (externalDataTool: ExternalDataTool) => boolean
|
|
}
|
|
const ExternalDataToolModal: FC<ExternalDataToolModalProps> = ({
|
|
data,
|
|
onCancel,
|
|
onSave,
|
|
onValidateBeforeSave,
|
|
}) => {
|
|
const { t } = useTranslation()
|
|
const docLink = useDocLink()
|
|
const locale = useLocale()
|
|
const [localeData, setLocaleData] = useState(data.type ? data : { ...data, type: 'api' })
|
|
const [showEmojiPicker, setShowEmojiPicker] = useState(false)
|
|
const { data: codeBasedExtensionList } = useCodeBasedExtensions('external_data_tool')
|
|
|
|
const providers = buildProviders({
|
|
codeBasedExtensionList,
|
|
locale,
|
|
t,
|
|
})
|
|
const currentProvider = providers.find(provider => provider.key === localeData.type)
|
|
|
|
const handleDataTypeChange = (type: string) => {
|
|
setLocaleData({
|
|
...localeData,
|
|
type,
|
|
config: getProviderDefaultConfig(type, providers),
|
|
})
|
|
}
|
|
|
|
const handleDataExtraChange = (extraValue: Record<string, string>) => {
|
|
setLocaleData({
|
|
...localeData,
|
|
config: {
|
|
...localeData.config,
|
|
...extraValue,
|
|
},
|
|
})
|
|
}
|
|
|
|
const handleValueChange = (value: Record<string, string>) => {
|
|
setLocaleData({
|
|
...localeData,
|
|
...value,
|
|
})
|
|
}
|
|
|
|
const handleDataApiBasedChange = (apiBasedExtensionId: string) => {
|
|
setLocaleData({
|
|
...localeData,
|
|
config: {
|
|
...localeData.config,
|
|
api_based_extension_id: apiBasedExtensionId,
|
|
},
|
|
})
|
|
}
|
|
|
|
const handleSave = () => {
|
|
const validationError = getValidationError({
|
|
currentProvider,
|
|
locale,
|
|
localeData,
|
|
t,
|
|
})
|
|
if (validationError) {
|
|
toast.error(validationError)
|
|
return
|
|
}
|
|
|
|
const formattedData = formatExternalDataTool(localeData, currentProvider, !!data.type)
|
|
|
|
if (onValidateBeforeSave && !onValidateBeforeSave(formattedData))
|
|
return
|
|
|
|
onSave(formattedData)
|
|
}
|
|
|
|
const action = data.type ? t('operation.edit', { ns: 'common' }) : t('operation.add', { ns: 'common' })
|
|
|
|
return (
|
|
<Dialog
|
|
open
|
|
onOpenChange={noop}
|
|
>
|
|
<DialogContent className="w-[640px]! max-w-none! p-8! pb-6!">
|
|
<div className="mb-2 text-xl font-semibold text-text-primary">
|
|
{`${action} ${t('variableConfig.apiBasedVar', { ns: 'appDebug' })}`}
|
|
</div>
|
|
<div className="py-2">
|
|
<div className="text-sm/9 font-medium text-text-primary">
|
|
{t('apiBasedExtension.type', { ns: 'common' })}
|
|
</div>
|
|
<Select
|
|
defaultValue={localeData.type}
|
|
onValueChange={value => value && handleDataTypeChange(value)}
|
|
>
|
|
<SelectTrigger className="w-full" aria-label={t('apiBasedExtension.type', { ns: 'common' })}>
|
|
<SelectValue />
|
|
</SelectTrigger>
|
|
<SelectContent popupClassName="w-[354px]">
|
|
{providers.map(option => (
|
|
<SelectItem key={option.key} value={option.key}>
|
|
<SelectItemText>{option.name}</SelectItemText>
|
|
<SelectItemIndicator />
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
</div>
|
|
<div className="py-2">
|
|
<div className="text-sm/9 font-medium text-text-primary">
|
|
{t('feature.tools.modal.name.title', { ns: 'appDebug' })}
|
|
</div>
|
|
<div className="flex items-center">
|
|
<input
|
|
value={localeData.label || ''}
|
|
onChange={e => handleValueChange({ label: e.target.value })}
|
|
className="mr-2 block h-9 grow appearance-none rounded-lg bg-components-input-bg-normal px-3 text-sm text-components-input-text-filled outline-hidden"
|
|
placeholder={t('feature.tools.modal.name.placeholder', { ns: 'appDebug' }) || ''}
|
|
/>
|
|
<AppIcon
|
|
size="large"
|
|
onClick={() => { setShowEmojiPicker(true) }}
|
|
className="h-9! w-9! cursor-pointer rounded-lg border-[0.5px] border-components-panel-border"
|
|
icon={localeData.icon}
|
|
background={localeData.icon_background}
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div className="py-2">
|
|
<div className="text-sm/9 font-medium text-text-primary">
|
|
{t('feature.tools.modal.variableName.title', { ns: 'appDebug' })}
|
|
</div>
|
|
<input
|
|
value={localeData.variable || ''}
|
|
onChange={e => handleValueChange({ variable: e.target.value })}
|
|
className="block h-9 w-full appearance-none rounded-lg bg-components-input-bg-normal px-3 text-sm text-components-input-text-filled outline-hidden"
|
|
placeholder={t('feature.tools.modal.variableName.placeholder', { ns: 'appDebug' }) || ''}
|
|
/>
|
|
</div>
|
|
{
|
|
localeData.type === 'api' && (
|
|
<div className="py-2">
|
|
<div className="flex h-9 items-center justify-between text-sm font-medium text-text-primary">
|
|
{t('apiBasedExtension.selector.title', { ns: 'common' })}
|
|
<a
|
|
href={docLink('/use-dify/workspace/api-extension/api-extension')}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
className="group flex items-center text-xs font-normal text-text-tertiary hover:text-text-accent"
|
|
>
|
|
<BookOpen01 className="mr-1 size-3 text-text-tertiary group-hover:text-text-accent" />
|
|
{t('apiBasedExtension.link', { ns: 'common' })}
|
|
</a>
|
|
</div>
|
|
<ApiBasedExtensionSelector
|
|
value={localeData.config?.api_based_extension_id || ''}
|
|
onChange={handleDataApiBasedChange}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|
|
{
|
|
localeData.type !== 'api'
|
|
&& currentProvider?.form_schema
|
|
&& (
|
|
<FormGeneration
|
|
forms={currentProvider?.form_schema}
|
|
value={localeData.config}
|
|
onChange={handleDataExtraChange}
|
|
/>
|
|
)
|
|
}
|
|
<div className="mt-6 flex items-center justify-end">
|
|
<Button
|
|
onClick={onCancel}
|
|
className="mr-2"
|
|
>
|
|
{t('operation.cancel', { ns: 'common' })}
|
|
</Button>
|
|
<Button
|
|
variant="primary"
|
|
onClick={handleSave}
|
|
>
|
|
{t('operation.save', { ns: 'common' })}
|
|
</Button>
|
|
</div>
|
|
{
|
|
showEmojiPicker && (
|
|
<EmojiPicker
|
|
open={showEmojiPicker}
|
|
onOpenChange={setShowEmojiPicker}
|
|
onSelect={(icon, icon_background) => {
|
|
handleValueChange({ icon, icon_background })
|
|
}}
|
|
/>
|
|
)
|
|
}
|
|
</DialogContent>
|
|
</Dialog>
|
|
)
|
|
}
|
|
|
|
export default ExternalDataToolModal
|