diff --git a/web/app/components/datasets/external-knowledge-base/connector/__tests__/index.spec.tsx b/web/app/components/datasets/external-knowledge-base/connector/__tests__/index.spec.tsx
index c948450f1b..46235256ce 100644
--- a/web/app/components/datasets/external-knowledge-base/connector/__tests__/index.spec.tsx
+++ b/web/app/components/datasets/external-knowledge-base/connector/__tests__/index.spec.tsx
@@ -164,7 +164,7 @@ describe('ExternalKnowledgeBaseConnector', () => {
// Verify success notification
expect(mockNotify).toHaveBeenCalledWith({
type: 'success',
- title: 'External Knowledge Base Connected Successfully',
+ title: 'dataset.externalKnowledgeForm.connectedSuccess',
})
// Verify navigation back
@@ -206,7 +206,7 @@ describe('ExternalKnowledgeBaseConnector', () => {
await waitFor(() => {
expect(mockNotify).toHaveBeenCalledWith({
type: 'error',
- title: 'Failed to connect External Knowledge Base',
+ title: 'dataset.externalKnowledgeForm.connectedFailed',
})
})
@@ -228,7 +228,7 @@ describe('ExternalKnowledgeBaseConnector', () => {
await waitFor(() => {
expect(mockNotify).toHaveBeenCalledWith({
type: 'error',
- title: 'Failed to connect External Knowledge Base',
+ title: 'dataset.externalKnowledgeForm.connectedFailed',
})
})
@@ -274,7 +274,7 @@ describe('ExternalKnowledgeBaseConnector', () => {
await waitFor(() => {
expect(mockNotify).toHaveBeenCalledWith({
type: 'success',
- title: 'External Knowledge Base Connected Successfully',
+ title: 'dataset.externalKnowledgeForm.connectedSuccess',
})
})
})
diff --git a/web/app/components/datasets/external-knowledge-base/connector/index.tsx b/web/app/components/datasets/external-knowledge-base/connector/index.tsx
index 6ff7014f47..adf9be0104 100644
--- a/web/app/components/datasets/external-knowledge-base/connector/index.tsx
+++ b/web/app/components/datasets/external-knowledge-base/connector/index.tsx
@@ -3,6 +3,7 @@
import type { CreateKnowledgeBaseReq } from '@/app/components/datasets/external-knowledge-base/create/declarations'
import * as React from 'react'
import { useState } from 'react'
+import { useTranslation } from 'react-i18next'
import { trackEvent } from '@/app/components/base/amplitude'
import { toast } from '@/app/components/base/ui/toast'
import ExternalKnowledgeBaseCreate from '@/app/components/datasets/external-knowledge-base/create'
@@ -12,13 +13,14 @@ import { createExternalKnowledgeBase } from '@/service/datasets'
const ExternalKnowledgeBaseConnector = () => {
const [loading, setLoading] = useState(false)
const router = useRouter()
+ const { t } = useTranslation()
const handleConnect = async (formValue: CreateKnowledgeBaseReq) => {
try {
setLoading(true)
const result = await createExternalKnowledgeBase({ body: formValue })
if (result && result.id) {
- toast.add({ type: 'success', title: 'External Knowledge Base Connected Successfully' })
+ toast.add({ type: 'success', title: t('externalKnowledgeForm.connectedSuccess', { ns: 'dataset' }) })
trackEvent('create_external_knowledge_base', {
provider: formValue.provider,
name: formValue.name,
@@ -29,7 +31,7 @@ const ExternalKnowledgeBaseConnector = () => {
}
catch (error) {
console.error('Error creating external knowledge base:', error)
- toast.add({ type: 'error', title: 'Failed to connect External Knowledge Base' })
+ toast.add({ type: 'error', title: t('externalKnowledgeForm.connectedFailed', { ns: 'dataset' }) })
}
setLoading(false)
}
diff --git a/web/app/components/tools/edit-custom-collection-modal/__tests__/get-schema.spec.tsx b/web/app/components/tools/edit-custom-collection-modal/__tests__/get-schema.spec.tsx
index edd2d3dc43..b19a234dc6 100644
--- a/web/app/components/tools/edit-custom-collection-modal/__tests__/get-schema.spec.tsx
+++ b/web/app/components/tools/edit-custom-collection-modal/__tests__/get-schema.spec.tsx
@@ -1,21 +1,24 @@
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { importSchemaFromURL } from '@/service/tools'
-import Toast from '../../../base/toast'
import examples from '../examples'
import GetSchema from '../get-schema'
vi.mock('@/service/tools', () => ({
importSchemaFromURL: vi.fn(),
}))
+const mockToastAdd = vi.hoisted(() => vi.fn())
+vi.mock('@/app/components/base/ui/toast', () => ({
+ toast: {
+ add: mockToastAdd,
+ },
+}))
const importSchemaFromURLMock = vi.mocked(importSchemaFromURL)
describe('GetSchema', () => {
- const notifySpy = vi.spyOn(Toast, 'notify')
const mockOnChange = vi.fn()
beforeEach(() => {
vi.clearAllMocks()
- notifySpy.mockClear()
importSchemaFromURLMock.mockReset()
render()
})
@@ -27,9 +30,9 @@ describe('GetSchema', () => {
fireEvent.change(input, { target: { value: 'ftp://invalid' } })
fireEvent.click(screen.getByText('common.operation.ok'))
- expect(notifySpy).toHaveBeenCalledWith({
+ expect(mockToastAdd).toHaveBeenCalledWith({
type: 'error',
- message: 'tools.createTool.urlError',
+ title: 'tools.createTool.urlError',
})
})
diff --git a/web/app/components/tools/edit-custom-collection-modal/get-schema.tsx b/web/app/components/tools/edit-custom-collection-modal/get-schema.tsx
index 7ad8050a2d..7d34658dec 100644
--- a/web/app/components/tools/edit-custom-collection-modal/get-schema.tsx
+++ b/web/app/components/tools/edit-custom-collection-modal/get-schema.tsx
@@ -10,8 +10,8 @@ import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import Input from '@/app/components/base/input'
+import { toast } from '@/app/components/base/ui/toast'
import { importSchemaFromURL } from '@/service/tools'
-import Toast from '../../base/toast'
import examples from './examples'
type Props = {
@@ -27,9 +27,9 @@ const GetSchema: FC = ({
const [isParsing, setIsParsing] = useState(false)
const handleImportFromUrl = async () => {
if (!importUrl.startsWith('http://') && !importUrl.startsWith('https://')) {
- Toast.notify({
+ toast.add({
type: 'error',
- message: t('createTool.urlError', { ns: 'tools' }),
+ title: t('createTool.urlError', { ns: 'tools' }),
})
return
}
diff --git a/web/app/components/tools/mcp/__tests__/modal.spec.tsx b/web/app/components/tools/mcp/__tests__/modal.spec.tsx
index af24ba6061..6b396cae7c 100644
--- a/web/app/components/tools/mcp/__tests__/modal.spec.tsx
+++ b/web/app/components/tools/mcp/__tests__/modal.spec.tsx
@@ -3,7 +3,7 @@ import type { ToolWithProvider } from '@/app/components/workflow/types'
import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
-import { describe, expect, it, vi } from 'vitest'
+import { beforeEach, describe, expect, it, vi } from 'vitest'
import MCPModal from '../modal'
// Mock the service API
@@ -48,7 +48,18 @@ vi.mock('@/service/use-plugins', () => ({
}),
}))
+const mockToastAdd = vi.hoisted(() => vi.fn())
+vi.mock('@/app/components/base/ui/toast', () => ({
+ toast: {
+ add: mockToastAdd,
+ },
+}))
+
describe('MCPModal', () => {
+ beforeEach(() => {
+ vi.clearAllMocks()
+ })
+
const createWrapper = () => {
const queryClient = new QueryClient({
defaultOptions: {
@@ -299,6 +310,10 @@ describe('MCPModal', () => {
// Wait a bit and verify onConfirm was not called
await new Promise(resolve => setTimeout(resolve, 100))
expect(onConfirm).not.toHaveBeenCalled()
+ expect(mockToastAdd).toHaveBeenCalledWith({
+ type: 'error',
+ title: 'tools.mcp.modal.invalidServerUrl',
+ })
})
it('should not call onConfirm with invalid server identifier', async () => {
@@ -320,6 +335,10 @@ describe('MCPModal', () => {
// Wait a bit and verify onConfirm was not called
await new Promise(resolve => setTimeout(resolve, 100))
expect(onConfirm).not.toHaveBeenCalled()
+ expect(mockToastAdd).toHaveBeenCalledWith({
+ type: 'error',
+ title: 'tools.mcp.modal.invalidServerIdentifier',
+ })
})
})
diff --git a/web/app/components/tools/mcp/modal.tsx b/web/app/components/tools/mcp/modal.tsx
index 76ba42f2bf..0f21214d34 100644
--- a/web/app/components/tools/mcp/modal.tsx
+++ b/web/app/components/tools/mcp/modal.tsx
@@ -14,7 +14,7 @@ import { Mcp } from '@/app/components/base/icons/src/vender/other'
import Input from '@/app/components/base/input'
import Modal from '@/app/components/base/modal'
import TabSlider from '@/app/components/base/tab-slider'
-import Toast from '@/app/components/base/toast'
+import { toast } from '@/app/components/base/ui/toast'
import { MCPAuthMethod } from '@/app/components/tools/types'
import { cn } from '@/utils/classnames'
import { shouldUseMcpIconForAppIcon } from '@/utils/mcp'
@@ -82,11 +82,11 @@ const MCPModalContent: FC = ({
const submit = async () => {
if (!isValidUrl(state.url)) {
- Toast.notify({ type: 'error', message: 'invalid server url' })
+ toast.add({ type: 'error', title: t('mcp.modal.invalidServerUrl', { ns: 'tools' }) })
return
}
if (!isValidServerID(state.serverIdentifier.trim())) {
- Toast.notify({ type: 'error', message: 'invalid server identifier' })
+ toast.add({ type: 'error', title: t('mcp.modal.invalidServerIdentifier', { ns: 'tools' }) })
return
}
const formattedHeaders = state.headers.reduce((acc, item) => {
diff --git a/web/app/components/tools/provider/__tests__/custom-create-card.spec.tsx b/web/app/components/tools/provider/__tests__/custom-create-card.spec.tsx
index 3643b769f7..63e2531a7f 100644
--- a/web/app/components/tools/provider/__tests__/custom-create-card.spec.tsx
+++ b/web/app/components/tools/provider/__tests__/custom-create-card.spec.tsx
@@ -70,11 +70,11 @@ vi.mock('@/app/components/tools/edit-custom-collection-modal', () => ({
},
}))
-// Mock Toast
+// Mock toast
const mockToastNotify = vi.fn()
-vi.mock('@/app/components/base/toast', () => ({
- default: {
- notify: (options: { type: string, message: string }) => mockToastNotify(options),
+vi.mock('@/app/components/base/ui/toast', () => ({
+ toast: {
+ add: (options: { type: string, title: string }) => mockToastNotify(options),
},
}))
@@ -200,7 +200,7 @@ describe('CustomCreateCard', () => {
await waitFor(() => {
expect(mockToastNotify).toHaveBeenCalledWith({
type: 'success',
- message: expect.any(String),
+ title: expect.any(String),
})
})
})
diff --git a/web/app/components/tools/provider/__tests__/detail.spec.tsx b/web/app/components/tools/provider/__tests__/detail.spec.tsx
index f2d47f8e43..7f8c415c16 100644
--- a/web/app/components/tools/provider/__tests__/detail.spec.tsx
+++ b/web/app/components/tools/provider/__tests__/detail.spec.tsx
@@ -92,8 +92,9 @@ vi.mock('@/app/components/base/confirm', () => ({
: null,
}))
-vi.mock('@/app/components/base/toast', () => ({
- default: { notify: vi.fn() },
+const mockToastAdd = vi.hoisted(() => vi.fn())
+vi.mock('@/app/components/base/ui/toast', () => ({
+ toast: { add: mockToastAdd },
}))
vi.mock('@/app/components/header/indicator', () => ({
diff --git a/web/app/components/tools/provider/custom-create-card.tsx b/web/app/components/tools/provider/custom-create-card.tsx
index bf86a1f833..f09d8e45d9 100644
--- a/web/app/components/tools/provider/custom-create-card.tsx
+++ b/web/app/components/tools/provider/custom-create-card.tsx
@@ -5,7 +5,7 @@ import {
} from '@remixicon/react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
-import Toast from '@/app/components/base/toast'
+import { toast } from '@/app/components/base/ui/toast'
import EditCustomToolModal from '@/app/components/tools/edit-custom-collection-modal'
import { useAppContext } from '@/context/app-context'
import { createCustomCollection } from '@/service/tools'
@@ -21,9 +21,9 @@ const Contribute = ({ onRefreshData }: Props) => {
const [isShowEditCollectionToolModal, setIsShowEditCustomCollectionModal] = useState(false)
const doCreateCustomToolCollection = async (data: CustomCollectionBackend) => {
await createCustomCollection(data)
- Toast.notify({
+ toast.add({
type: 'success',
- message: t('api.actionSuccess', { ns: 'common' }),
+ title: t('api.actionSuccess', { ns: 'common' }),
})
setIsShowEditCustomCollectionModal(false)
onRefreshData()
diff --git a/web/app/components/tools/provider/detail.tsx b/web/app/components/tools/provider/detail.tsx
index e25bcacb9b..626a80a57b 100644
--- a/web/app/components/tools/provider/detail.tsx
+++ b/web/app/components/tools/provider/detail.tsx
@@ -13,7 +13,7 @@ import Confirm from '@/app/components/base/confirm'
import Drawer from '@/app/components/base/drawer'
import { LinkExternal02, Settings01 } from '@/app/components/base/icons/src/vender/line/general'
import Loading from '@/app/components/base/loading'
-import Toast from '@/app/components/base/toast'
+import { toast } from '@/app/components/base/ui/toast'
import { ConfigurationMethodEnum } from '@/app/components/header/account-setting/model-provider-page/declarations'
import Indicator from '@/app/components/header/indicator'
import Icon from '@/app/components/plugins/card/base/card-icon'
@@ -122,18 +122,18 @@ const ProviderDetail = ({
await getCustomProvider()
// Use fresh data from form submission to avoid race condition with collection.labels
setCustomCollection(prev => prev ? { ...prev, labels: data.labels } : null)
- Toast.notify({
+ toast.add({
type: 'success',
- message: t('api.actionSuccess', { ns: 'common' }),
+ title: t('api.actionSuccess', { ns: 'common' }),
})
setIsShowEditCustomCollectionModal(false)
}
const doRemoveCustomToolCollection = async () => {
await removeCustomCollection(collection?.name as string)
onRefreshData()
- Toast.notify({
+ toast.add({
type: 'success',
- message: t('api.actionSuccess', { ns: 'common' }),
+ title: t('api.actionSuccess', { ns: 'common' }),
})
setIsShowEditCustomCollectionModal(false)
}
@@ -161,9 +161,9 @@ const ProviderDetail = ({
const removeWorkflowToolProvider = async () => {
await deleteWorkflowTool(collection.id)
onRefreshData()
- Toast.notify({
+ toast.add({
type: 'success',
- message: t('api.actionSuccess', { ns: 'common' }),
+ title: t('api.actionSuccess', { ns: 'common' }),
})
setIsShowEditWorkflowToolModal(false)
}
@@ -175,9 +175,9 @@ const ProviderDetail = ({
invalidateAllWorkflowTools()
onRefreshData()
getWorkflowToolProvider()
- Toast.notify({
+ toast.add({
type: 'success',
- message: t('api.actionSuccess', { ns: 'common' }),
+ title: t('api.actionSuccess', { ns: 'common' }),
})
setIsShowEditWorkflowToolModal(false)
}
@@ -385,18 +385,18 @@ const ProviderDetail = ({
onCancel={() => setShowSettingAuth(false)}
onSaved={async (value) => {
await updateBuiltInToolCredential(collection.name, value)
- Toast.notify({
+ toast.add({
type: 'success',
- message: t('api.actionSuccess', { ns: 'common' }),
+ title: t('api.actionSuccess', { ns: 'common' }),
})
await onRefreshData()
setShowSettingAuth(false)
}}
onRemove={async () => {
await removeBuiltInToolCredential(collection.name)
- Toast.notify({
+ toast.add({
type: 'success',
- message: t('api.actionSuccess', { ns: 'common' }),
+ title: t('api.actionSuccess', { ns: 'common' }),
})
await onRefreshData()
setShowSettingAuth(false)
diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json
index 1b4b9c2ff8..fb0da9b649 100644
--- a/web/eslint-suppressions.json
+++ b/web/eslint-suppressions.json
@@ -5928,9 +5928,6 @@
}
},
"app/components/tools/edit-custom-collection-modal/get-schema.tsx": {
- "no-restricted-imports": {
- "count": 1
- },
"tailwindcss/enforce-consistent-class-order": {
"count": 3
},
@@ -6056,7 +6053,7 @@
},
"app/components/tools/mcp/modal.tsx": {
"no-restricted-imports": {
- "count": 2
+ "count": 1
},
"tailwindcss/enforce-consistent-class-order": {
"count": 7
@@ -6097,16 +6094,13 @@
}
},
"app/components/tools/provider/custom-create-card.tsx": {
- "no-restricted-imports": {
- "count": 1
- },
"tailwindcss/enforce-consistent-class-order": {
"count": 1
}
},
"app/components/tools/provider/detail.tsx": {
"no-restricted-imports": {
- "count": 2
+ "count": 1
},
"tailwindcss/enforce-consistent-class-order": {
"count": 10
diff --git a/web/i18n/en-US/dataset.json b/web/i18n/en-US/dataset.json
index 538517dccd..72d0a7b909 100644
--- a/web/i18n/en-US/dataset.json
+++ b/web/i18n/en-US/dataset.json
@@ -77,6 +77,8 @@
"externalKnowledgeDescriptionPlaceholder": "Describe what's in this Knowledge Base (optional)",
"externalKnowledgeForm.cancel": "Cancel",
"externalKnowledgeForm.connect": "Connect",
+ "externalKnowledgeForm.connectedFailed": "Failed to connect External Knowledge Base",
+ "externalKnowledgeForm.connectedSuccess": "External Knowledge Base Connected Successfully",
"externalKnowledgeId": "External Knowledge ID",
"externalKnowledgeIdPlaceholder": "Please enter the Knowledge ID",
"externalKnowledgeName": "External Knowledge Name",
diff --git a/web/i18n/en-US/tools.json b/web/i18n/en-US/tools.json
index 30ee4f58df..391e109317 100644
--- a/web/i18n/en-US/tools.json
+++ b/web/i18n/en-US/tools.json
@@ -126,6 +126,8 @@
"mcp.modal.headerValuePlaceholder": "e.g., Bearer token123",
"mcp.modal.headers": "Headers",
"mcp.modal.headersTip": "Additional HTTP headers to send with MCP server requests",
+ "mcp.modal.invalidServerIdentifier": "Please enter a valid server identifier",
+ "mcp.modal.invalidServerUrl": "Please enter a valid server URL",
"mcp.modal.maskedHeadersTip": "Header values are masked for security. Changes will update the actual values.",
"mcp.modal.name": "Name & Icon",
"mcp.modal.namePlaceholder": "Name your MCP server",