use base ui toast

This commit is contained in:
yyh
2026-03-25 20:38:44 +08:00
parent a7178b4d5c
commit 20dea1faa2
274 changed files with 3597 additions and 8129 deletions

View File

@ -28,7 +28,7 @@ vi.mock('@/service/annotation', () => ({
addAnnotation: (...args: unknown[]) => mockAddAnnotation(...args),
}))
vi.mock('@/app/components/base/toast', () => ({
vi.mock('@/app/components/base/ui/toast', () => ({
default: { notify: vi.fn() },
}))

View File

@ -1,5 +1,5 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import ConfigParamModal from '../config-param-modal'
let mockHooksReturn: {
@ -31,10 +31,6 @@ vi.mock('@/app/components/header/account-setting/model-provider-page/model-selec
),
}))
vi.mock('@/app/components/base/toast', () => ({
default: { notify: vi.fn() },
}))
vi.mock('@/config', () => ({
ANNOTATION_DEFAULT: { score_threshold: 0.9 },
}))
@ -63,8 +59,11 @@ const defaultAnnotationConfig = {
}
describe('ConfigParamModal', () => {
const toastErrorSpy = vi.spyOn(toast, 'error').mockReturnValue('toast-error')
beforeEach(() => {
vi.clearAllMocks()
toastErrorSpy.mockClear()
mockHooksReturn = {
modelList: [{ provider: { provider: 'openai' }, models: [{ model: 'text-embedding-ada-002' }] }],
defaultModel: { provider: { provider: 'openai' }, model: 'text-embedding-ada-002' },
@ -241,7 +240,7 @@ describe('ConfigParamModal', () => {
const saveBtn = buttons.find(b => b.textContent?.includes('initSetup'))
fireEvent.click(saveBtn!)
expect(Toast.notify).toHaveBeenCalledWith(
expect(toastErrorSpy).toHaveBeenCalledWith(
expect.objectContaining({ type: 'error' }),
)
})

View File

@ -1,14 +1,11 @@
'use client'
import type { FC } from 'react'
import {
RiEditLine,
RiFileEditLine,
} from '@remixicon/react'
import { RiEditLine, RiFileEditLine } from '@remixicon/react'
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import ActionButton from '@/app/components/base/action-button'
import Toast from '@/app/components/base/toast'
import Tooltip from '@/app/components/base/tooltip'
import { toast } from '@/app/components/base/ui/toast'
import { useModalContext } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
import { addAnnotation } from '@/service/annotation'
@ -22,16 +19,7 @@ type Props = {
onAdded: (annotationId: string, authorName: string) => void
onEdit: () => void
}
const AnnotationCtrlButton: FC<Props> = ({
cached,
query,
answer,
appId,
messageId,
onAdded,
onEdit,
}) => {
const AnnotationCtrlButton: FC<Props> = ({ cached, query, answer, appId, messageId, onAdded, onEdit }) => {
const { t } = useTranslation()
const { plan, enableBilling } = useProviderContext()
const isAnnotationFull = (enableBilling && plan.usage.annotatedResponse >= plan.total.annotatedResponse)
@ -46,28 +34,20 @@ const AnnotationCtrlButton: FC<Props> = ({
question: query,
answer,
})
Toast.notify({
message: t('api.actionSuccess', { ns: 'common' }) as string,
type: 'success',
})
toast.success(t('api.actionSuccess', { ns: 'common' }) as string)
onAdded(res.id, res.account?.name ?? '')
}
return (
<>
{cached && (
<Tooltip
popupContent={t('feature.annotation.edit', { ns: 'appDebug' })}
>
<Tooltip popupContent={t('feature.annotation.edit', { ns: 'appDebug' })}>
<ActionButton onClick={onEdit}>
<RiEditLine className="h-4 w-4" />
</ActionButton>
</Tooltip>
)}
{!cached && answer && (
<Tooltip
popupContent={t('feature.annotation.add', { ns: 'appDebug' })}
>
<Tooltip popupContent={t('feature.annotation.add', { ns: 'appDebug' })}>
<ActionButton onClick={handleAdd}>
<RiFileEditLine className="h-4 w-4" />
</ActionButton>

View File

@ -6,7 +6,7 @@ import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import Modal from '@/app/components/base/modal'
import Toast from '@/app/components/base/toast'
import { toast } from '@/app/components/base/ui/toast'
import { ModelTypeEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import { useModelListAndDefaultModelAndCurrentProviderAndModel } from '@/app/components/header/account-setting/model-provider-page/hooks'
import ModelSelector from '@/app/components/header/account-setting/model-provider-page/model-selector'
@ -25,22 +25,10 @@ type Props = {
isInit?: boolean
annotationConfig: AnnotationReplyConfig
}
const ConfigParamModal: FC<Props> = ({
isShow,
onHide: doHide,
onSave,
isInit,
annotationConfig: oldAnnotationConfig,
}) => {
const ConfigParamModal: FC<Props> = ({ isShow, onHide: doHide, onSave, isInit, annotationConfig: oldAnnotationConfig }) => {
const { t } = useTranslation()
const {
modelList: embeddingsModelList,
defaultModel: embeddingsDefaultModel,
currentModel: isEmbeddingsDefaultModelValid,
} = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.textEmbedding)
const { modelList: embeddingsModelList, defaultModel: embeddingsDefaultModel, currentModel: isEmbeddingsDefaultModelValid } = useModelListAndDefaultModelAndCurrentProviderAndModel(ModelTypeEnum.textEmbedding)
const [annotationConfig, setAnnotationConfig] = useState(oldAnnotationConfig)
const [isLoading, setLoading] = useState(false)
const [embeddingModel, setEmbeddingModel] = useState(oldAnnotationConfig.embedding_model
? {
@ -57,13 +45,9 @@ const ConfigParamModal: FC<Props> = ({
if (!isLoading)
doHide()
}
const handleSave = async () => {
if (!embeddingModel || !embeddingModel.modelName || (embeddingModel.modelName === embeddingsDefaultModel?.model && !isEmbeddingsDefaultModelValid)) {
Toast.notify({
message: t('modelProvider.embeddingModel.required', { ns: 'common' }),
type: 'error',
})
toast.error(t('modelProvider.embeddingModel.required', { ns: 'common' }))
return
}
setLoading(true)
@ -73,22 +57,14 @@ const ConfigParamModal: FC<Props> = ({
}, annotationConfig.score_threshold)
setLoading(false)
}
return (
<Modal
isShow={isShow}
onClose={onHide}
className="!mt-14 !w-[640px] !max-w-none !p-6"
>
<Modal isShow={isShow} onClose={onHide} className="!mt-14 !w-[640px] !max-w-none !p-6">
<div className="mb-2 text-text-primary title-2xl-semi-bold">
{t(`initSetup.${isInit ? 'title' : 'configTitle'}`, { ns: 'appAnnotation' })}
</div>
<div className="mt-6 space-y-3">
<Item
title={t('feature.annotation.scoreThreshold.title', { ns: 'appDebug' })}
tooltip={t('feature.annotation.scoreThreshold.description', { ns: 'appDebug' })}
>
<Item title={t('feature.annotation.scoreThreshold.title', { ns: 'appDebug' })} tooltip={t('feature.annotation.scoreThreshold.description', { ns: 'appDebug' })}>
<ScoreSlider
className="mt-1"
value={(annotationConfig.score_threshold || ANNOTATION_DEFAULT.score_threshold) * 100}
@ -101,10 +77,7 @@ const ConfigParamModal: FC<Props> = ({
/>
</Item>
<Item
title={t('modelProvider.embeddingModel.key', { ns: 'common' })}
tooltip={t('embeddingModelSwitchTip', { ns: 'appAnnotation' })}
>
<Item title={t('modelProvider.embeddingModel.key', { ns: 'common' })} tooltip={t('embeddingModelSwitchTip', { ns: 'appAnnotation' })}>
<div className="pt-1">
<ModelSelector
defaultModel={embeddingModel && {
@ -125,11 +98,7 @@ const ConfigParamModal: FC<Props> = ({
<div className="mt-6 flex justify-end gap-2">
<Button onClick={onHide}>{t('operation.cancel', { ns: 'common' })}</Button>
<Button
variant="primary"
onClick={handleSave}
loading={isLoading}
>
<Button variant="primary" onClick={handleSave} loading={isLoading}>
<div></div>
<div>{t(`initSetup.${isInit ? 'confirmBtn' : 'configConfirmBtn'}`, { ns: 'appAnnotation' })}</div>
</Button>

View File

@ -4,8 +4,8 @@ import * as i18n from 'react-i18next'
import ModerationSettingModal from '../moderation-setting-modal'
const mockNotify = vi.fn()
vi.mock('@/app/components/base/toast/context', () => ({
useToastContext: () => ({ notify: mockNotify }),
vi.mock('@/app/components/base/ui/toast', () => ({
}))
const mockSetShowAccountSettingModal = vi.fn()

View File

@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import Divider from '@/app/components/base/divider'
import Modal from '@/app/components/base/modal'
import { useToastContext } from '@/app/components/base/toast/context'
import { toast } from '@/app/components/base/ui/toast'
import ApiBasedExtensionSelector from '@/app/components/header/account-setting/api-based-extension-page/selector'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
import { CustomConfigurationStatusEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
@ -20,27 +20,19 @@ import FormGeneration from './form-generation'
import ModerationContent from './moderation-content'
const systemTypes = ['openai_moderation', 'keywords', 'api']
type Provider = {
key: string
name: string
form_schema?: CodeBasedExtensionItem['form_schema']
}
type ModerationSettingModalProps = {
data: ModerationConfig
onCancel: () => void
onSave: (moderationConfig: ModerationConfig) => void
}
const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
data,
onCancel,
onSave,
}) => {
const ModerationSettingModal: FC<ModerationSettingModalProps> = ({ data, onCancel, onSave }) => {
const { t } = useTranslation()
const docLink = useDocLink()
const { notify } = useToastContext()
const locale = useLocale()
const { data: modelProviders, isPending: isLoading, refetch: refetchModelProviders } = useModelProviders()
const [localeData, setLocaleData] = useState<ModerationConfig>(data)
@ -73,25 +65,20 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
key: 'api',
name: t('apiBasedExtension.selector.title', { ns: 'common' }),
},
...(
codeBasedExtensionList
? codeBasedExtensionList.data.map((item) => {
return {
key: item.name,
name: locale === 'zh-Hans' ? item.label['zh-Hans'] : item.label['en-US'],
form_schema: item.form_schema,
}
})
: []
),
...(codeBasedExtensionList
? codeBasedExtensionList.data.map((item) => {
return {
key: item.name,
name: locale === 'zh-Hans' ? item.label['zh-Hans'] : item.label['en-US'],
form_schema: item.form_schema,
}
})
: []),
]
const currentProvider = providers.find(provider => provider.key === localeData.type)
const handleDataTypeChange = (type: string) => {
let config: undefined | Record<string, any>
const currProvider = providers.find(provider => provider.key === type)
if (systemTypes.findIndex(t => t === type) < 0 && currProvider?.form_schema) {
config = currProvider?.form_schema.reduce((prev, next) => {
prev[next.variable] = next.default
@ -104,19 +91,15 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
config,
})
}
const handleDataKeywordsChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
const value = e.target.value
const arr = value.split('\n').reduce((prev: string[], next: string) => {
if (next !== '')
prev.push(next.slice(0, 100))
if (next === '' && prev[prev.length - 1] !== '')
prev.push(next)
return prev
}, [])
setLocaleData({
...localeData,
config: {
@ -125,7 +108,6 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
},
})
}
const handleDataContentChange = (contentType: string, contentConfig: ModerationContentConfig) => {
setLocaleData({
...localeData,
@ -135,7 +117,6 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
},
})
}
const handleDataApiBasedChange = (apiBasedExtensionId: string) => {
setLocaleData({
...localeData,
@ -145,7 +126,6 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
},
})
}
const handleDataExtraChange = (extraValue: Record<string, string>) => {
setLocaleData({
...localeData,
@ -155,24 +135,19 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
},
})
}
const formatData = (originData: ModerationConfig) => {
const { enabled, type, config } = originData
const { inputs_config, outputs_config } = config!
const params: Record<string, string | undefined> = {}
if (type === 'keywords')
params.keywords = config?.keywords
if (type === 'api')
params.api_based_extension_id = config?.api_based_extension_id
if (systemTypes.findIndex(t => t === type) < 0 && currentProvider?.form_schema) {
currentProvider.form_schema.forEach((form) => {
params[form.variable] = config?.[form.variable]
})
}
return {
type,
enabled,
@ -183,58 +158,42 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
},
}
}
const handleSave = () => {
/* v8 ignore next -- UI-invariant guard: same condition is used in Save button disabled logic, so when true handleSave has no user-triggerable invocation path. @preserve */
if (localeData.type === 'openai_moderation' && !isOpenAIProviderConfigured)
return
if (!localeData.config?.inputs_config?.enabled && !localeData.config?.outputs_config?.enabled) {
notify({ type: 'error', message: t('feature.moderation.modal.content.condition', { ns: 'appDebug' }) })
toast.error(t('feature.moderation.modal.content.condition', { ns: 'appDebug' }))
return
}
if (localeData.type === 'keywords' && !localeData.config.keywords) {
notify({ type: 'error', message: t('errorMessage.valueOfVarRequired', { ns: 'appDebug', key: locale !== LanguagesSupported[1] ? 'keywords' : '关键词' }) })
toast.error(t('errorMessage.valueOfVarRequired', { ns: 'appDebug', key: locale !== LanguagesSupported[1] ? 'keywords' : '关键词' }))
return
}
if (localeData.type === 'api' && !localeData.config.api_based_extension_id) {
notify({ type: 'error', message: t('errorMessage.valueOfVarRequired', { ns: 'appDebug', key: locale !== LanguagesSupported[1] ? 'API Extension' : 'API 扩展' }) })
toast.error(t('errorMessage.valueOfVarRequired', { ns: 'appDebug', key: locale !== LanguagesSupported[1] ? 'API Extension' : 'API 扩展' }))
return
}
if (systemTypes.findIndex(t => t === localeData.type) < 0 && currentProvider?.form_schema) {
for (let i = 0; i < currentProvider.form_schema.length; i++) {
if (!localeData.config?.[currentProvider.form_schema[i].variable] && currentProvider.form_schema[i].required) {
notify({
type: 'error',
message: t('errorMessage.valueOfVarRequired', { ns: 'appDebug', key: locale !== LanguagesSupported[1] ? currentProvider.form_schema[i].label['en-US'] : currentProvider.form_schema[i].label['zh-Hans'] }),
})
toast.error(t('errorMessage.valueOfVarRequired', { ns: 'appDebug', key: locale !== LanguagesSupported[1] ? currentProvider.form_schema[i].label['en-US'] : currentProvider.form_schema[i].label['zh-Hans'] }))
return
}
}
}
if (localeData.config.inputs_config?.enabled && !localeData.config.inputs_config.preset_response && localeData.type !== 'api') {
notify({ type: 'error', message: t('feature.moderation.modal.content.errorMessage', { ns: 'appDebug' }) })
toast.error(t('feature.moderation.modal.content.errorMessage', { ns: 'appDebug' }))
return
}
if (localeData.config.outputs_config?.enabled && !localeData.config.outputs_config.preset_response && localeData.type !== 'api') {
notify({ type: 'error', message: t('feature.moderation.modal.content.errorMessage', { ns: 'appDebug' }) })
toast.error(t('feature.moderation.modal.content.errorMessage', { ns: 'appDebug' }))
return
}
onSave(formatData(localeData))
}
return (
<Modal
isShow
onClose={noop}
className="!mt-14 !w-[600px] !max-w-none !p-6"
>
<Modal isShow onClose={noop} className="!mt-14 !w-[600px] !max-w-none !p-6">
<div className="flex items-center justify-between">
<div className="text-text-primary title-2xl-semi-bold">{t('feature.moderation.modal.title', { ns: 'appDebug' })}</div>
<div
@ -257,139 +216,74 @@ const ModerationSettingModal: FC<ModerationSettingModalProps> = ({
{t('feature.moderation.modal.provider.title', { ns: 'appDebug' })}
</div>
<div className="grid grid-cols-3 gap-2.5">
{
providers.map(provider => (
<div
key={provider.key}
className={cn(
'flex h-8 cursor-default items-center rounded-md border border-components-option-card-option-border bg-components-option-card-option-bg px-2 text-text-secondary system-sm-regular',
localeData.type !== provider.key && 'cursor-pointer hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs',
localeData.type === provider.key && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg shadow-xs system-sm-medium',
localeData.type === 'openai_moderation' && provider.key === 'openai_moderation' && !isOpenAIProviderConfigured && 'text-text-disabled',
)}
onClick={() => handleDataTypeChange(provider.key)}
>
<div className={cn(
'mr-2 h-4 w-4 rounded-full border border-components-radio-border bg-components-radio-bg shadow-xs',
localeData.type === provider.key && 'border-[5px] border-components-radio-border-checked',
)}
>
</div>
{provider.name}
{providers.map(provider => (
<div key={provider.key} className={cn('flex h-8 cursor-default items-center rounded-md border border-components-option-card-option-border bg-components-option-card-option-bg px-2 text-text-secondary system-sm-regular', localeData.type !== provider.key && 'cursor-pointer hover:border-components-option-card-option-border-hover hover:bg-components-option-card-option-bg-hover hover:shadow-xs', localeData.type === provider.key && 'border-[1.5px] border-components-option-card-option-selected-border bg-components-option-card-option-selected-bg shadow-xs system-sm-medium', localeData.type === 'openai_moderation' && provider.key === 'openai_moderation' && !isOpenAIProviderConfigured && 'text-text-disabled')} onClick={() => handleDataTypeChange(provider.key)}>
<div className={cn('mr-2 h-4 w-4 rounded-full border border-components-radio-border bg-components-radio-bg shadow-xs', localeData.type === provider.key && 'border-[5px] border-components-radio-border-checked')}>
</div>
))
}
{provider.name}
</div>
))}
</div>
{
!isLoading && !isOpenAIProviderConfigured && localeData.type === 'openai_moderation' && (
<div className="mt-2 flex items-center rounded-lg border border-[#FEF0C7] bg-[#FFFAEB] px-3 py-2">
<span className="i-custom-vender-line-general-info-circle mr-1 h-4 w-4 text-[#F79009]" />
<div className="flex items-center text-xs font-medium text-gray-700">
{t('feature.moderation.modal.openaiNotConfig.before', { ns: 'appDebug' })}
<span
className="cursor-pointer text-primary-600"
onClick={handleOpenSettingsModal}
>
{!isLoading && !isOpenAIProviderConfigured && localeData.type === 'openai_moderation' && (
<div className="mt-2 flex items-center rounded-lg border border-[#FEF0C7] bg-[#FFFAEB] px-3 py-2">
<span className="i-custom-vender-line-general-info-circle mr-1 h-4 w-4 text-[#F79009]" />
<div className="flex items-center text-xs font-medium text-gray-700">
{t('feature.moderation.modal.openaiNotConfig.before', { ns: 'appDebug' })}
<span className="cursor-pointer text-primary-600" onClick={handleOpenSettingsModal}>
&nbsp;
{t('settings.provider', { ns: 'common' })}
{t('settings.provider', { ns: 'common' })}
&nbsp;
</span>
{t('feature.moderation.modal.openaiNotConfig.after', { ns: 'appDebug' })}
</div>
</span>
{t('feature.moderation.modal.openaiNotConfig.after', { ns: 'appDebug' })}
</div>
)
}
</div>
)}
</div>
{
localeData.type === 'keywords' && (
<div className="py-2">
<div className="mb-1 text-sm font-medium text-text-primary">{t('feature.moderation.modal.provider.keywords', { ns: 'appDebug' })}</div>
<div className="mb-2 text-xs text-text-tertiary">{t('feature.moderation.modal.keywords.tip', { ns: 'appDebug' })}</div>
<div className="relative h-[88px] rounded-lg bg-components-input-bg-normal px-3 py-2">
<textarea
value={localeData.config?.keywords || ''}
onChange={handleDataKeywordsChange}
className="block h-full w-full resize-none appearance-none bg-transparent text-sm text-text-secondary outline-none"
placeholder={t('feature.moderation.modal.keywords.placeholder', { ns: 'appDebug' }) || ''}
/>
<div className="absolute bottom-2 right-2 flex h-5 items-center rounded-md bg-background-section px-1 text-xs font-medium text-text-quaternary">
<span>{(localeData.config?.keywords || '').split('\n').filter(Boolean).length}</span>
/
<span className="text-text-tertiary">
100
{t('feature.moderation.modal.keywords.line', { ns: 'appDebug' })}
</span>
</div>
{localeData.type === 'keywords' && (
<div className="py-2">
<div className="mb-1 text-sm font-medium text-text-primary">{t('feature.moderation.modal.provider.keywords', { ns: 'appDebug' })}</div>
<div className="mb-2 text-xs text-text-tertiary">{t('feature.moderation.modal.keywords.tip', { ns: 'appDebug' })}</div>
<div className="relative h-[88px] rounded-lg bg-components-input-bg-normal px-3 py-2">
<textarea value={localeData.config?.keywords || ''} onChange={handleDataKeywordsChange} className="block h-full w-full resize-none appearance-none bg-transparent text-sm text-text-secondary outline-none" placeholder={t('feature.moderation.modal.keywords.placeholder', { ns: 'appDebug' }) || ''} />
<div className="absolute bottom-2 right-2 flex h-5 items-center rounded-md bg-background-section px-1 text-xs font-medium text-text-quaternary">
<span>{(localeData.config?.keywords || '').split('\n').filter(Boolean).length}</span>
/
<span className="text-text-tertiary">
100
{t('feature.moderation.modal.keywords.line', { ns: 'appDebug' })}
</span>
</div>
</div>
)
}
{
localeData.type === 'api' && (
<div className="py-2">
<div className="flex h-9 items-center justify-between">
<div className="text-sm font-medium text-text-primary">{t('apiBasedExtension.selector.title', { ns: 'common' })}</div>
<a
href={docLink('/use-dify/workspace/api-extension/api-extension')}
target="_blank"
rel="noopener noreferrer"
className="group flex items-center text-xs text-text-tertiary hover:text-primary-600"
>
<span className="i-custom-vender-line-education-book-open-01 mr-1 h-3 w-3 text-text-tertiary group-hover:text-primary-600" />
{t('apiBasedExtension.link', { ns: 'common' })}
</a>
</div>
<ApiBasedExtensionSelector
value={localeData.config?.api_based_extension_id || ''}
onChange={handleDataApiBasedChange}
/>
</div>
)}
{localeData.type === 'api' && (
<div className="py-2">
<div className="flex h-9 items-center justify-between">
<div className="text-sm font-medium text-text-primary">{t('apiBasedExtension.selector.title', { ns: 'common' })}</div>
<a href={docLink('/use-dify/workspace/api-extension/api-extension')} target="_blank" rel="noopener noreferrer" className="group flex items-center text-xs text-text-tertiary hover:text-primary-600">
<span className="i-custom-vender-line-education-book-open-01 mr-1 h-3 w-3 text-text-tertiary group-hover:text-primary-600" />
{t('apiBasedExtension.link', { ns: 'common' })}
</a>
</div>
)
}
{
systemTypes.findIndex(t => t === localeData.type) < 0
<ApiBasedExtensionSelector value={localeData.config?.api_based_extension_id || ''} onChange={handleDataApiBasedChange} />
</div>
)}
{systemTypes.findIndex(t => t === localeData.type) < 0
&& currentProvider?.form_schema
&& (
<FormGeneration
forms={currentProvider?.form_schema}
value={localeData.config}
onChange={handleDataExtraChange}
/>
)
}
&& (<FormGeneration forms={currentProvider?.form_schema} value={localeData.config} onChange={handleDataExtraChange} />)}
<Divider bgStyle="gradient" className="my-3 h-px" />
<ModerationContent
title={t('feature.moderation.modal.content.input', { ns: 'appDebug' }) || ''}
config={localeData.config?.inputs_config || { enabled: false, preset_response: '' }}
onConfigChange={config => handleDataContentChange('inputs_config', config)}
info={(localeData.type === 'api' && t('feature.moderation.modal.content.fromApi', { ns: 'appDebug' })) || ''}
showPreset={localeData.type !== 'api'}
/>
<ModerationContent
title={t('feature.moderation.modal.content.output', { ns: 'appDebug' }) || ''}
config={localeData.config?.outputs_config || { enabled: false, preset_response: '' }}
onConfigChange={config => handleDataContentChange('outputs_config', config)}
info={(localeData.type === 'api' && t('feature.moderation.modal.content.fromApi', { ns: 'appDebug' })) || ''}
showPreset={localeData.type !== 'api'}
/>
<ModerationContent title={t('feature.moderation.modal.content.input', { ns: 'appDebug' }) || ''} config={localeData.config?.inputs_config || { enabled: false, preset_response: '' }} onConfigChange={config => handleDataContentChange('inputs_config', config)} info={(localeData.type === 'api' && t('feature.moderation.modal.content.fromApi', { ns: 'appDebug' })) || ''} showPreset={localeData.type !== 'api'} />
<ModerationContent title={t('feature.moderation.modal.content.output', { ns: 'appDebug' }) || ''} config={localeData.config?.outputs_config || { enabled: false, preset_response: '' }} onConfigChange={config => handleDataContentChange('outputs_config', config)} info={(localeData.type === 'api' && t('feature.moderation.modal.content.fromApi', { ns: 'appDebug' })) || ''} showPreset={localeData.type !== 'api'} />
<div className="mb-8 mt-1 text-xs font-medium text-text-tertiary">{t('feature.moderation.modal.content.condition', { ns: 'appDebug' })}</div>
<div className="flex items-center justify-end">
<Button
onClick={onCancel}
className="mr-2"
>
<Button onClick={onCancel} className="mr-2">
{t('operation.cancel', { ns: 'common' })}
</Button>
<Button
variant="primary"
onClick={handleSave}
disabled={localeData.type === 'openai_moderation' && !isOpenAIProviderConfigured}
>
<Button variant="primary" onClick={handleSave} disabled={localeData.type === 'openai_moderation' && !isOpenAIProviderConfigured}>
{t('operation.save', { ns: 'common' })}
</Button>
</div>
</Modal>
)
}
export default ModerationSettingModal