setCurrentPassword(e.target.value)}
+ />
-
+ {userProfile.is_password_set ? t('account.newPassword', { ns: 'common' }) : t('account.password', { ns: 'common' })}
+
+
setPassword(e.target.value)}
+ />
+
+
- >
- )}
-
- {userProfile.is_password_set ? t('account.newPassword', { ns: 'common' }) : t('account.password', { ns: 'common' })}
-
-
-
setPassword(e.target.value)}
- />
-
+
+
{t('account.confirmPassword', { ns: 'common' })}
+
+
setConfirmPassword(e.target.value)}
+ />
+
+
+
+
+
+
-
-
{t('account.confirmPassword', { ns: 'common' })}
-
-
setConfirmPassword(e.target.value)}
- />
-
-
-
-
-
-
-
-
-
+
+
)
}
{
diff --git a/web/app/account/(commonLayout)/delete-account/components/feed-back.tsx b/web/app/account/(commonLayout)/delete-account/components/feed-back.tsx
index af82d4bc62..0fd06599d2 100644
--- a/web/app/account/(commonLayout)/delete-account/components/feed-back.tsx
+++ b/web/app/account/(commonLayout)/delete-account/components/feed-back.tsx
@@ -4,7 +4,7 @@ import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import CustomDialog from '@/app/components/base/dialog'
import Textarea from '@/app/components/base/textarea'
-import Toast from '@/app/components/base/toast'
+import { toast } from '@/app/components/base/ui/toast'
import { useAppContext } from '@/context/app-context'
import { useRouter } from '@/next/navigation'
import { useLogout } from '@/service/use-common'
@@ -28,7 +28,7 @@ export default function FeedBack(props: DeleteAccountProps) {
await logout()
// Tokens are now stored in cookies and cleared by backend
router.push('/signin')
- Toast.notify({ type: 'info', message: t('account.deleteSuccessTip', { ns: 'common' }) })
+ toast.info(t('account.deleteSuccessTip', { ns: 'common' }))
}
catch (error) { console.error(error) }
}, [router, t])
diff --git a/web/app/components/app-sidebar/app-info.tsx b/web/app/components/app-sidebar/app-info.tsx
index 38b3cc3108..02cf281224 100644
--- a/web/app/components/app-sidebar/app-info.tsx
+++ b/web/app/components/app-sidebar/app-info.tsx
@@ -16,8 +16,8 @@ import { useCallback, useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import CardView from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view'
import { useStore as useAppStore } from '@/app/components/app/store'
-
import Button from '@/app/components/base/button'
+
import ContentDialog from '@/app/components/base/content-dialog'
import { toast } from '@/app/components/base/ui/toast'
import { collaborationManager } from '@/app/components/workflow/collaboration/core/collaboration-manager'
diff --git a/web/app/components/app-sidebar/app-info/__tests__/use-app-info-actions.spec.ts b/web/app/components/app-sidebar/app-info/__tests__/use-app-info-actions.spec.ts
index deea28ce3e..1fe0b6ddb5 100644
--- a/web/app/components/app-sidebar/app-info/__tests__/use-app-info-actions.spec.ts
+++ b/web/app/components/app-sidebar/app-info/__tests__/use-app-info-actions.spec.ts
@@ -27,10 +27,6 @@ vi.mock('@/next/navigation', () => ({
useRouter: () => ({ replace: mockReplace }),
}))
-vi.mock('use-context-selector', () => ({
- useContext: () => ({ notify: mockNotify }),
-}))
-
vi.mock('@/context/provider-context', () => ({
useProviderContext: () => ({ onPlanInfoChanged: mockOnPlanInfoChanged }),
}))
@@ -42,8 +38,16 @@ vi.mock('@/app/components/app/store', () => ({
}),
}))
-vi.mock('@/app/components/base/toast/context', () => ({
- ToastContext: {},
+vi.mock('@/app/components/base/ui/toast', () => ({
+ toast: Object.assign(mockNotify, {
+ success: vi.fn((message, options) => mockNotify({ type: 'success', message, ...options })),
+ error: vi.fn((message, options) => mockNotify({ type: 'error', message, ...options })),
+ warning: vi.fn((message, options) => mockNotify({ type: 'warning', message, ...options })),
+ info: vi.fn((message, options) => mockNotify({ type: 'info', message, ...options })),
+ dismiss: vi.fn(),
+ update: vi.fn(),
+ promise: vi.fn(),
+ }),
}))
vi.mock('@/service/use-apps', () => ({
diff --git a/web/app/components/app-sidebar/app-info/use-app-info-actions.ts b/web/app/components/app-sidebar/app-info/use-app-info-actions.ts
index 55ec13e506..8b559f7bba 100644
--- a/web/app/components/app-sidebar/app-info/use-app-info-actions.ts
+++ b/web/app/components/app-sidebar/app-info/use-app-info-actions.ts
@@ -3,9 +3,8 @@ import type { CreateAppModalProps } from '@/app/components/explore/create-app-mo
import type { EnvironmentVariable } from '@/app/components/workflow/types'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { useContext } from 'use-context-selector'
import { useStore as useAppStore } from '@/app/components/app/store'
-import { ToastContext } from '@/app/components/base/toast/context'
+import { toast } from '@/app/components/base/ui/toast'
import { NEED_REFRESH_APP_LIST_KEY } from '@/config'
import { useProviderContext } from '@/context/provider-context'
import { useRouter } from '@/next/navigation'
@@ -24,7 +23,6 @@ type UseAppInfoActionsParams = {
export function useAppInfoActions({ onDetailExpand }: UseAppInfoActionsParams) {
const { t } = useTranslation()
- const { notify } = useContext(ToastContext)
const { replace } = useRouter()
const { onPlanInfoChanged } = useProviderContext()
const appDetail = useAppStore(state => state.appDetail)
@@ -72,13 +70,13 @@ export function useAppInfoActions({ onDetailExpand }: UseAppInfoActionsParams) {
max_active_requests,
})
closeModal()
- notify({ type: 'success', message: t('editDone', { ns: 'app' }) })
+ toast(t('editDone', { ns: 'app' }), { type: 'success' })
setAppDetail(app)
}
catch {
- notify({ type: 'error', message: t('editFailed', { ns: 'app' }) })
+ toast(t('editFailed', { ns: 'app' }), { type: 'error' })
}
- }, [appDetail, closeModal, notify, setAppDetail, t])
+ }, [appDetail, closeModal, setAppDetail, t])
const onCopy: DuplicateAppModalProps['onConfirm'] = useCallback(async ({
name,
@@ -98,15 +96,15 @@ export function useAppInfoActions({ onDetailExpand }: UseAppInfoActionsParams) {
mode: appDetail.mode,
})
closeModal()
- notify({ type: 'success', message: t('newApp.appCreated', { ns: 'app' }) })
+ toast(t('newApp.appCreated', { ns: 'app' }), { type: 'success' })
localStorage.setItem(NEED_REFRESH_APP_LIST_KEY, '1')
onPlanInfoChanged()
getRedirection(true, newApp, replace)
}
catch {
- notify({ type: 'error', message: t('newApp.appCreateFailed', { ns: 'app' }) })
+ toast(t('newApp.appCreateFailed', { ns: 'app' }), { type: 'error' })
}
- }, [appDetail, closeModal, notify, onPlanInfoChanged, replace, t])
+ }, [appDetail, closeModal, onPlanInfoChanged, replace, t])
const onExport = useCallback(async (include = false) => {
if (!appDetail)
@@ -117,9 +115,9 @@ export function useAppInfoActions({ onDetailExpand }: UseAppInfoActionsParams) {
downloadBlob({ data: file, fileName: `${appDetail.name}.yml` })
}
catch {
- notify({ type: 'error', message: t('exportFailed', { ns: 'app' }) })
+ toast(t('exportFailed', { ns: 'app' }), { type: 'error' })
}
- }, [appDetail, notify, t])
+ }, [appDetail, t])
const exportCheck = useCallback(async () => {
if (!appDetail)
@@ -145,29 +143,26 @@ export function useAppInfoActions({ onDetailExpand }: UseAppInfoActionsParams) {
setSecretEnvList(list)
}
catch {
- notify({ type: 'error', message: t('exportFailed', { ns: 'app' }) })
+ toast(t('exportFailed', { ns: 'app' }), { type: 'error' })
}
- }, [appDetail, closeModal, notify, onExport, t])
+ }, [appDetail, closeModal, onExport, t])
const onConfirmDelete = useCallback(async () => {
if (!appDetail)
return
try {
await deleteApp(appDetail.id)
- notify({ type: 'success', message: t('appDeleted', { ns: 'app' }) })
+ toast(t('appDeleted', { ns: 'app' }), { type: 'success' })
invalidateAppList()
onPlanInfoChanged()
setAppDetail()
replace('/apps')
}
catch (e: unknown) {
- notify({
- type: 'error',
- message: `${t('appDeleteFailed', { ns: 'app' })}${e instanceof Error && e.message ? `: ${e.message}` : ''}`,
- })
+ toast(`${t('appDeleteFailed', { ns: 'app' })}${e instanceof Error && e.message ? `: ${e.message}` : ''}`, { type: 'error' })
}
closeModal()
- }, [appDetail, closeModal, invalidateAppList, notify, onPlanInfoChanged, replace, setAppDetail, t])
+ }, [appDetail, closeModal, invalidateAppList, onPlanInfoChanged, replace, setAppDetail, t])
return {
appDetail,
diff --git a/web/app/components/app-sidebar/dataset-info/dropdown.tsx b/web/app/components/app-sidebar/dataset-info/dropdown.tsx
index 528bac831f..1d1208e7d3 100644
--- a/web/app/components/app-sidebar/dataset-info/dropdown.tsx
+++ b/web/app/components/app-sidebar/dataset-info/dropdown.tsx
@@ -3,6 +3,7 @@ import { RiMoreFill } from '@remixicon/react'
import * as React from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
+import { toast } from '@/app/components/base/ui/toast'
import { useSelector as useAppContextWithSelector } from '@/context/app-context'
import { useDatasetDetailContextWithSelector } from '@/context/dataset-detail'
import { useRouter } from '@/next/navigation'
@@ -15,7 +16,6 @@ import { downloadBlob } from '@/utils/download'
import ActionButton from '../../base/action-button'
import Confirm from '../../base/confirm'
import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '../../base/portal-to-follow-elem'
-import Toast from '../../base/toast'
import RenameDatasetModal from '../../datasets/rename-modal'
import Menu from './menu'
@@ -69,7 +69,7 @@ const DropDown = ({
downloadBlob({ data: file, fileName: `${name}.pipeline` })
}
catch {
- Toast.notify({ type: 'error', message: t('exportFailed', { ns: 'app' }) })
+ toast(t('exportFailed', { ns: 'app' }), { type: 'error' })
}
}, [dataset, exportPipelineConfig, handleTrigger, t])
@@ -81,7 +81,7 @@ const DropDown = ({
}
catch (e: any) {
const res = await e.json()
- Toast.notify({ type: 'error', message: res?.message || 'Unknown error' })
+ toast(res?.message || 'Unknown error', { type: 'error' })
}
finally {
handleTrigger()
@@ -91,7 +91,7 @@ const DropDown = ({
const onConfirmDelete = useCallback(async () => {
try {
await deleteDataset(dataset.id)
- Toast.notify({ type: 'success', message: t('datasetDeleted', { ns: 'dataset' }) })
+ toast(t('datasetDeleted', { ns: 'dataset' }), { type: 'success' })
invalidDatasetList()
replace('/datasets')
}
diff --git a/web/app/components/app/annotation/add-annotation-modal/index.spec.tsx b/web/app/components/app/annotation/add-annotation-modal/index.spec.tsx
index bad3ceefdf..be6f17b88e 100644
--- a/web/app/components/app/annotation/add-annotation-modal/index.spec.tsx
+++ b/web/app/components/app/annotation/add-annotation-modal/index.spec.tsx
@@ -9,7 +9,7 @@ vi.mock('@/context/provider-context', () => ({
}))
const mockToastNotify = vi.fn()
-vi.mock('@/app/components/base/toast', () => ({
+vi.mock('@/app/components/base/ui/toast', () => ({
default: {
notify: vi.fn(args => mockToastNotify(args)),
},
diff --git a/web/app/components/app/annotation/add-annotation-modal/index.tsx b/web/app/components/app/annotation/add-annotation-modal/index.tsx
index ca808779c6..28e2c24382 100644
--- a/web/app/components/app/annotation/add-annotation-modal/index.tsx
+++ b/web/app/components/app/annotation/add-annotation-modal/index.tsx
@@ -7,7 +7,7 @@ import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import Checkbox from '@/app/components/base/checkbox'
import Drawer from '@/app/components/base/drawer-plus'
-import Toast from '@/app/components/base/toast'
+import { toast } from '@/app/components/base/ui/toast'
import AnnotationFull from '@/app/components/billing/annotation-full'
import { useProviderContext } from '@/context/provider-context'
import EditItem, { EditItemType } from './edit-item'
@@ -47,10 +47,7 @@ const AddAnnotationModal: FC
= ({
answer,
}
if (isValid(payload) !== true) {
- Toast.notify({
- type: 'error',
- message: isValid(payload) as string,
- })
+ toast.error(isValid(payload) as string)
return
}
diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.spec.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.spec.tsx
index 55f5ee0564..603611ba9c 100644
--- a/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.spec.tsx
+++ b/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.spec.tsx
@@ -1,11 +1,23 @@
import type { Props } from './csv-uploader'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
-import { ToastContext } from '@/app/components/base/toast/context'
import CSVUploader from './csv-uploader'
describe('CSVUploader', () => {
const notify = vi.fn()
+ const mockToast = {
+ success: (message: string, options?: Record) => notify({ type: 'success', message, ...options }),
+ error: (message: string, options?: Record) => notify({ type: 'error', message, ...options }),
+ warning: (message: string, options?: Record) => notify({ type: 'warning', message, ...options }),
+ info: (message: string, options?: Record) => notify({ type: 'info', message, ...options }),
+ dismiss: vi.fn(),
+ update: vi.fn(),
+ promise: vi.fn(),
+ }
+
+ vi.mock('@/app/components/base/ui/toast', () => ({
+ toast: mockToast,
+ }))
const updateFile = vi.fn()
const getDropElements = () => {
@@ -24,9 +36,8 @@ describe('CSVUploader', () => {
...props,
}
return render(
-
-
- ,
+ ,
+
)
}
diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx
index a969b3d491..0fbd3974aa 100644
--- a/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx
+++ b/web/app/components/app/annotation/batch-add-annotation-modal/csv-uploader.tsx
@@ -4,10 +4,9 @@ import { RiDeleteBinLine } from '@remixicon/react'
import * as React from 'react'
import { useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
-import { useContext } from 'use-context-selector'
import Button from '@/app/components/base/button'
import { Csv as CSVIcon } from '@/app/components/base/icons/src/public/files'
-import { ToastContext } from '@/app/components/base/toast/context'
+import { toast } from '@/app/components/base/ui/toast'
import { cn } from '@/utils/classnames'
export type Props = {
@@ -20,7 +19,6 @@ const CSVUploader: FC = ({
updateFile,
}) => {
const { t } = useTranslation()
- const { notify } = useContext(ToastContext)
const [dragging, setDragging] = useState(false)
const dropRef = useRef(null)
const dragRef = useRef(null)
@@ -50,7 +48,7 @@ const CSVUploader: FC = ({
return
const files = Array.from(e.dataTransfer.files)
if (files.length > 1) {
- notify({ type: 'error', message: t('stepOne.uploader.validation.count', { ns: 'datasetCreation' }) })
+ toast.error(t('stepOne.uploader.validation.count', { ns: 'datasetCreation' }))
return
}
updateFile(files[0])
diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/index.spec.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/index.spec.tsx
index 7fdb99fbab..e65632267c 100644
--- a/web/app/components/app/annotation/batch-add-annotation-modal/index.spec.tsx
+++ b/web/app/components/app/annotation/batch-add-annotation-modal/index.spec.tsx
@@ -2,17 +2,10 @@ import type { Mock } from 'vitest'
import type { IBatchModalProps } from './index'
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
-import Toast from '@/app/components/base/toast'
import { useProviderContext } from '@/context/provider-context'
import { annotationBatchImport, checkAnnotationBatchImportProgress } from '@/service/annotation'
import BatchModal, { ProcessStatus } from './index'
-vi.mock('@/app/components/base/toast', () => ({
- default: {
- notify: vi.fn(),
- },
-}))
-
vi.mock('@/service/annotation', () => ({
annotationBatchImport: vi.fn(),
checkAnnotationBatchImportProgress: vi.fn(),
@@ -49,7 +42,7 @@ vi.mock('@/app/components/billing/annotation-full', () => ({
default: () => ,
}))
-const mockNotify = Toast.notify as Mock
+const mockNotify = vi.fn()
const useProviderContextMock = useProviderContext as Mock
const annotationBatchImportMock = annotationBatchImport as Mock
const checkAnnotationBatchImportProgressMock = checkAnnotationBatchImportProgress as Mock
diff --git a/web/app/components/app/annotation/batch-add-annotation-modal/index.tsx b/web/app/components/app/annotation/batch-add-annotation-modal/index.tsx
index aaa8b6638e..47332e72d6 100644
--- a/web/app/components/app/annotation/batch-add-annotation-modal/index.tsx
+++ b/web/app/components/app/annotation/batch-add-annotation-modal/index.tsx
@@ -7,7 +7,7 @@ import { useEffect, 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 AnnotationFull from '@/app/components/billing/annotation-full'
import { useProviderContext } from '@/context/provider-context'
import { annotationBatchImport, checkAnnotationBatchImportProgress } from '@/service/annotation'
@@ -46,7 +46,6 @@ const BatchModal: FC = ({
}, [isShow])
const [importStatus, setImportStatus] = useState()
- const notify = Toast.notify
const checkProcess = async (jobID: string) => {
try {
const res = await checkAnnotationBatchImportProgress({ jobID, appId })
@@ -54,15 +53,15 @@ const BatchModal: FC = ({
if (res.job_status === ProcessStatus.WAITING || res.job_status === ProcessStatus.PROCESSING)
setTimeout(() => checkProcess(res.job_id), 2500)
if (res.job_status === ProcessStatus.ERROR)
- notify({ type: 'error', message: `${t('batchModal.runError', { ns: 'appAnnotation' })}` })
+ toast.error(`${t('batchModal.runError', { ns: 'appAnnotation' })}`)
if (res.job_status === ProcessStatus.COMPLETED) {
- notify({ type: 'success', message: `${t('batchModal.completed', { ns: 'appAnnotation' })}` })
+ toast.success(`${t('batchModal.completed', { ns: 'appAnnotation' })}`)
onAdded()
onCancel()
}
}
catch (e: any) {
- notify({ type: 'error', message: `${t('batchModal.runError', { ns: 'appAnnotation' })}${'message' in e ? `: ${e.message}` : ''}` })
+ toast.error(`${t('batchModal.runError', { ns: 'appAnnotation' })}${'message' in e ? `: ${e.message}` : ''}`)
}
}
@@ -78,7 +77,7 @@ const BatchModal: FC = ({
checkProcess(res.job_id)
}
catch (e: any) {
- notify({ type: 'error', message: `${t('batchModal.runError', { ns: 'appAnnotation' })}${'message' in e ? `: ${e.message}` : ''}` })
+ toast.error(`${t('batchModal.runError', { ns: 'appAnnotation' })}${'message' in e ? `: ${e.message}` : ''}`)
}
}
diff --git a/web/app/components/app/annotation/edit-annotation-modal/index.spec.tsx b/web/app/components/app/annotation/edit-annotation-modal/index.spec.tsx
index 0bbd1ab67d..8f6dec42cf 100644
--- a/web/app/components/app/annotation/edit-annotation-modal/index.spec.tsx
+++ b/web/app/components/app/annotation/edit-annotation-modal/index.spec.tsx
@@ -1,7 +1,6 @@
-import type { IToastProps, ToastHandle } from '@/app/components/base/toast'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
-import Toast from '@/app/components/base/toast'
+import { toast } from '@/app/components/base/ui/toast'
import EditAnnotationModal from './index'
const { mockAddAnnotation, mockEditAnnotation } = vi.hoisted(() => ({
@@ -37,10 +36,8 @@ vi.mock('@/app/components/billing/annotation-full', () => ({
default: () => ,
}))
-type ToastNotifyProps = Pick
-type ToastWithNotify = typeof Toast & { notify: (props: ToastNotifyProps) => ToastHandle }
-const toastWithNotify = Toast as unknown as ToastWithNotify
-const toastNotifySpy = vi.spyOn(toastWithNotify, 'notify').mockReturnValue({ clear: vi.fn() })
+const toastSuccessSpy = vi.spyOn(toast, 'success').mockReturnValue('toast-success')
+const toastErrorSpy = vi.spyOn(toast, 'error').mockReturnValue('toast-error')
describe('EditAnnotationModal', () => {
const defaultProps = {
@@ -55,7 +52,8 @@ describe('EditAnnotationModal', () => {
}
afterAll(() => {
- toastNotifySpy.mockRestore()
+ toastSuccessSpy.mockRestore()
+ toastErrorSpy.mockRestore()
})
beforeEach(() => {
@@ -437,10 +435,7 @@ describe('EditAnnotationModal', () => {
// Assert
await waitFor(() => {
- expect(toastNotifySpy).toHaveBeenCalledWith({
- message: 'API Error',
- type: 'error',
- })
+ expect(toastErrorSpy).toHaveBeenCalledWith('API Error')
})
expect(mockOnAdded).not.toHaveBeenCalled()
@@ -475,10 +470,7 @@ describe('EditAnnotationModal', () => {
// Assert
await waitFor(() => {
- expect(toastNotifySpy).toHaveBeenCalledWith({
- message: 'common.api.actionFailed',
- type: 'error',
- })
+ expect(toastErrorSpy).toHaveBeenCalledWith('common.api.actionFailed')
})
expect(mockOnAdded).not.toHaveBeenCalled()
@@ -517,10 +509,7 @@ describe('EditAnnotationModal', () => {
// Assert
await waitFor(() => {
- expect(toastNotifySpy).toHaveBeenCalledWith({
- message: 'API Error',
- type: 'error',
- })
+ expect(toastErrorSpy).toHaveBeenCalledWith('API Error')
})
expect(mockOnEdited).not.toHaveBeenCalled()
@@ -557,10 +546,7 @@ describe('EditAnnotationModal', () => {
// Assert
await waitFor(() => {
- expect(toastNotifySpy).toHaveBeenCalledWith({
- message: 'common.api.actionFailed',
- type: 'error',
- })
+ expect(toastErrorSpy).toHaveBeenCalledWith('common.api.actionFailed')
})
expect(mockOnEdited).not.toHaveBeenCalled()
@@ -641,10 +627,7 @@ describe('EditAnnotationModal', () => {
// Assert
await waitFor(() => {
- expect(toastNotifySpy).toHaveBeenCalledWith({
- message: 'common.api.actionSuccess',
- type: 'success',
- })
+ expect(toastSuccessSpy).toHaveBeenCalledWith('common.api.actionSuccess')
})
})
})
diff --git a/web/app/components/app/annotation/edit-annotation-modal/index.tsx b/web/app/components/app/annotation/edit-annotation-modal/index.tsx
index c8da20a44a..855b827835 100644
--- a/web/app/components/app/annotation/edit-annotation-modal/index.tsx
+++ b/web/app/components/app/annotation/edit-annotation-modal/index.tsx
@@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'
import Confirm from '@/app/components/base/confirm'
import Drawer from '@/app/components/base/drawer-plus'
import { MessageCheckRemove } from '@/app/components/base/icons/src/vender/line/communication'
-import Toast from '@/app/components/base/toast'
+import { toast } from '@/app/components/base/ui/toast'
import AnnotationFull from '@/app/components/billing/annotation-full'
import { useProviderContext } from '@/context/provider-context'
import useTimestamp from '@/hooks/use-timestamp'
@@ -72,18 +72,12 @@ const EditAnnotationModal: FC = ({
onAdded(res.id, res.account?.name ?? '', postQuery, postAnswer)
}
- Toast.notify({
- message: t('api.actionSuccess', { ns: 'common' }) as string,
- type: 'success',
- })
+ toast.success(t('api.actionSuccess', { ns: 'common' }) as string)
}
catch (error) {
const fallbackMessage = t('api.actionFailed', { ns: 'common' }) as string
const message = error instanceof Error && error.message ? error.message : fallbackMessage
- Toast.notify({
- message,
- type: 'error',
- })
+ toast.error(message)
// Re-throw to preserve edit mode behavior for UI components
throw error
}
diff --git a/web/app/components/app/annotation/index.spec.tsx b/web/app/components/app/annotation/index.spec.tsx
index d62b60d33d..5f5e9f74c0 100644
--- a/web/app/components/app/annotation/index.spec.tsx
+++ b/web/app/components/app/annotation/index.spec.tsx
@@ -3,7 +3,7 @@ import type { AnnotationItem } from './type'
import type { App } from '@/types/app'
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react'
import * as React from 'react'
-import Toast from '@/app/components/base/toast'
+import { toast } from '@/app/components/base/ui/toast'
import { useProviderContext } from '@/context/provider-context'
import {
addAnnotation,
@@ -17,10 +17,6 @@ import { AppModeEnum } from '@/types/app'
import Annotation from './index'
import { JobStatus } from './type'
-vi.mock('@/app/components/base/toast', () => ({
- default: { notify: vi.fn() },
-}))
-
vi.mock('ahooks', () => ({
useDebounce: (value: any) => value,
}))
@@ -95,7 +91,23 @@ vi.mock('./view-annotation-modal', () => ({
vi.mock('@/app/components/base/features/new-feature-panel/annotation-reply/config-param-modal', () => ({ default: (props: any) => props.isShow ? : null }))
vi.mock('@/app/components/billing/annotation-full/modal', () => ({ default: (props: any) => props.show ? : null }))
-const mockNotify = Toast.notify as Mock
+const mockNotify = vi.fn()
+vi.spyOn(toast, 'success').mockImplementation((message, options) => {
+ mockNotify({ type: 'success', message, ...options })
+ return 'toast-success-id'
+})
+vi.spyOn(toast, 'error').mockImplementation((message, options) => {
+ mockNotify({ type: 'error', message, ...options })
+ return 'toast-error-id'
+})
+vi.spyOn(toast, 'warning').mockImplementation((message, options) => {
+ mockNotify({ type: 'warning', message, ...options })
+ return 'toast-warning-id'
+})
+vi.spyOn(toast, 'info').mockImplementation((message, options) => {
+ mockNotify({ type: 'info', message, ...options })
+ return 'toast-info-id'
+})
const addAnnotationMock = addAnnotation as Mock
const delAnnotationMock = delAnnotation as Mock
const delAnnotationsMock = delAnnotations as Mock
diff --git a/web/app/components/app/annotation/index.tsx b/web/app/components/app/annotation/index.tsx
index ee276603cc..0ea25744ff 100644
--- a/web/app/components/app/annotation/index.tsx
+++ b/web/app/components/app/annotation/index.tsx
@@ -15,6 +15,7 @@ import { MessageFast } from '@/app/components/base/icons/src/vender/solid/commun
import Loading from '@/app/components/base/loading'
import Pagination from '@/app/components/base/pagination'
import Switch from '@/app/components/base/switch'
+import { toast } from '@/app/components/base/ui/toast'
import AnnotationFullModal from '@/app/components/billing/annotation-full/modal'
import { APP_PAGE_LIMIT } from '@/config'
import { useProviderContext } from '@/context/provider-context'
@@ -22,7 +23,6 @@ import { addAnnotation, delAnnotation, delAnnotations, fetchAnnotationConfig as
import { AppModeEnum } from '@/types/app'
import { sleep } from '@/utils'
import { cn } from '@/utils/classnames'
-import Toast from '../../base/toast'
import EmptyElement from './empty-element'
import Filter from './filter'
import HeaderOpts from './header-opts'
@@ -98,14 +98,14 @@ const Annotation: FC = (props) => {
const handleAdd = async (payload: AnnotationItemBasic) => {
await addAnnotation(appDetail.id, payload)
- Toast.notify({ message: t('api.actionSuccess', { ns: 'common' }), type: 'success' })
+ toast.success(t('api.actionSuccess', { ns: 'common' }))
fetchList()
setControlUpdateList(Date.now())
}
const handleRemove = async (id: string) => {
await delAnnotation(appDetail.id, id)
- Toast.notify({ message: t('api.actionSuccess', { ns: 'common' }), type: 'success' })
+ toast.success(t('api.actionSuccess', { ns: 'common' }))
fetchList()
setControlUpdateList(Date.now())
}
@@ -113,13 +113,13 @@ const Annotation: FC = (props) => {
const handleBatchDelete = async () => {
try {
await delAnnotations(appDetail.id, selectedIds)
- Toast.notify({ message: t('api.actionSuccess', { ns: 'common' }), type: 'success' })
+ toast.success(t('api.actionSuccess', { ns: 'common' }))
fetchList()
setControlUpdateList(Date.now())
setSelectedIds([])
}
catch (e: any) {
- Toast.notify({ type: 'error', message: e.message || t('api.actionFailed', { ns: 'common' }) })
+ toast.error(e.message || t('api.actionFailed', { ns: 'common' }))
}
}
@@ -132,7 +132,7 @@ const Annotation: FC = (props) => {
if (!currItem)
return
await editAnnotation(appDetail.id, currItem.id, { question, answer })
- Toast.notify({ message: t('api.actionSuccess', { ns: 'common' }), type: 'success' })
+ toast.success(t('api.actionSuccess', { ns: 'common' }))
fetchList()
setControlUpdateList(Date.now())
}
@@ -170,10 +170,7 @@ const Annotation: FC = (props) => {
const { job_id: jobId }: any = await updateAnnotationStatus(appDetail.id, AnnotationEnableStatus.disable, annotationConfig?.embedding_model, annotationConfig?.score_threshold)
await ensureJobCompleted(jobId, AnnotationEnableStatus.disable)
await fetchAnnotationConfig()
- Toast.notify({
- message: t('api.actionSuccess', { ns: 'common' }),
- type: 'success',
- })
+ toast.success(t('api.actionSuccess', { ns: 'common' }))
}
}}
>
@@ -263,10 +260,7 @@ const Annotation: FC = (props) => {
await updateAnnotationScore(appDetail.id, annotationId, score)
await fetchAnnotationConfig()
- Toast.notify({
- message: t('api.actionSuccess', { ns: 'common' }),
- type: 'success',
- })
+ toast.success(t('api.actionSuccess', { ns: 'common' }))
setIsShowEdit(false)
}}
annotationConfig={annotationConfig!}
diff --git a/web/app/components/app/app-access-control/access-control.spec.tsx b/web/app/components/app/app-access-control/access-control.spec.tsx
index 3950bdf7ee..289eccebb2 100644
--- a/web/app/components/app/app-access-control/access-control.spec.tsx
+++ b/web/app/components/app/app-access-control/access-control.spec.tsx
@@ -2,9 +2,9 @@ import type { AccessControlAccount, AccessControlGroup, Subject } from '@/models
import type { App } from '@/types/app'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
+import { toast } from '@/app/components/base/ui/toast'
import useAccessControlStore from '@/context/access-control-store'
import { AccessMode, SubjectType } from '@/models/access-control'
-import Toast from '../../base/toast'
import AccessControlDialog from './access-control-dialog'
import AccessControlItem from './access-control-item'
import AddMemberOrGroupDialog from './add-member-or-group-pop'
@@ -303,7 +303,7 @@ describe('AccessControl', () => {
it('should initialize menu from app and call update on confirm', async () => {
const onClose = vi.fn()
const onConfirm = vi.fn()
- const toastSpy = vi.spyOn(Toast, 'notify').mockReturnValue({})
+ const toastSpy = vi.spyOn(toast, 'success').mockReturnValue('toast-success')
useAccessControlStore.setState({
specificGroups: [baseGroup],
specificMembers: [baseMember],
@@ -336,7 +336,7 @@ describe('AccessControl', () => {
{ subjectId: baseMember.id, subjectType: SubjectType.ACCOUNT },
],
})
- expect(toastSpy).toHaveBeenCalled()
+ expect(toastSpy).toHaveBeenCalledWith('app.accessControlDialog.updateSuccess')
expect(onConfirm).toHaveBeenCalled()
})
})
diff --git a/web/app/components/app/app-access-control/index.tsx b/web/app/components/app/app-access-control/index.tsx
index 70f77729ca..316433f41d 100644
--- a/web/app/components/app/app-access-control/index.tsx
+++ b/web/app/components/app/app-access-control/index.tsx
@@ -5,12 +5,12 @@ import { Description as DialogDescription, DialogTitle } from '@headlessui/react
import { RiBuildingLine, RiGlobalLine, RiVerifiedBadgeLine } from '@remixicon/react'
import { useCallback, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
+import { toast } from '@/app/components/base/ui/toast'
import { useGlobalPublicStore } from '@/context/global-public-context'
import { AccessMode, SubjectType } from '@/models/access-control'
import { useUpdateAccessMode } from '@/service/access-control'
import useAccessControlStore from '../../../../context/access-control-store'
import Button from '../../base/button'
-import Toast from '../../base/toast'
import AccessControlDialog from './access-control-dialog'
import AccessControlItem from './access-control-item'
import SpecificGroupsOrMembers, { WebAppSSONotEnabledTip } from './specific-groups-or-members'
@@ -61,7 +61,7 @@ export default function AccessControl(props: AccessControlProps) {
submitData.subjects = subjects
}
await updateAccessMode(submitData)
- Toast.notify({ type: 'success', message: t('accessControlDialog.updateSuccess', { ns: 'app' }) })
+ toast.success(t('accessControlDialog.updateSuccess', { ns: 'app' }))
onConfirm?.()
}, [updateAccessMode, app, specificGroups, specificMembers, t, onConfirm, currentMenu])
return (
diff --git a/web/app/components/app/app-publisher/index.tsx b/web/app/components/app/app-publisher/index.tsx
index dd988b89df..72d6490fa7 100644
--- a/web/app/components/app/app-publisher/index.tsx
+++ b/web/app/components/app/app-publisher/index.tsx
@@ -28,6 +28,7 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
+import { toast } from '@/app/components/base/ui/toast'
import UpgradeBtn from '@/app/components/billing/upgrade-btn'
import WorkflowToolConfigureButton from '@/app/components/tools/workflow-tool/configure-button'
import { collaborationManager } from '@/app/components/workflow/collaboration/core/collaboration-manager'
@@ -47,7 +48,6 @@ import { AppModeEnum } from '@/types/app'
import { basePath } from '@/utils/var'
import Divider from '../../base/divider'
import Loading from '../../base/loading'
-import Toast from '../../base/toast'
import Tooltip from '../../base/tooltip'
import ShortcutsName from '../../workflow/shortcuts-name'
import { getKeyboardKeyCodeBySystem } from '../../workflow/utils'
@@ -264,7 +264,7 @@ const AppPublisher = ({
throw new Error('No app found in Explore')
}, {
onError: (err) => {
- Toast.notify({ type: 'error', message: `${err.message || err}` })
+ toast.error(`${err.message || err}`)
},
})
}, [appDetail?.id, openAsyncWindow])
@@ -290,7 +290,7 @@ const AppPublisher = ({
window.open(result.redirect_url, '_blank')
}
catch (error: any) {
- Toast.notify({ type: 'error', message: error.message || t('common.publishToMarketplaceFailed', { ns: 'workflow' }) })
+ toast.error(error.message || t('common.publishToMarketplaceFailed', { ns: 'workflow' }))
}
finally {
setPublishingToMarketplace(false)
diff --git a/web/app/components/app/app-publisher/version-info-modal.tsx b/web/app/components/app/app-publisher/version-info-modal.tsx
index 45509d89ec..9713685d90 100644
--- a/web/app/components/app/app-publisher/version-info-modal.tsx
+++ b/web/app/components/app/app-publisher/version-info-modal.tsx
@@ -5,7 +5,7 @@ import * as React from 'react'
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Modal from '@/app/components/base/modal'
-import Toast from '@/app/components/base/toast'
+import { toast } from '@/app/components/base/ui/toast'
import Button from '../../base/button'
import Input from '../../base/input'
import Textarea from '../../base/textarea'
@@ -35,10 +35,7 @@ const VersionInfoModal: FC = ({
const handlePublish = () => {
if (title.length > TITLE_MAX_LENGTH) {
setTitleError(true)
- Toast.notify({
- type: 'error',
- message: t('versionHistory.editField.titleLengthLimit', { ns: 'workflow', limit: TITLE_MAX_LENGTH }),
- })
+ toast.error(t('versionHistory.editField.titleLengthLimit', { ns: 'workflow', limit: TITLE_MAX_LENGTH }))
return
}
else {
@@ -48,10 +45,7 @@ const VersionInfoModal: FC = ({
if (releaseNotes.length > RELEASE_NOTES_MAX_LENGTH) {
setReleaseNotesError(true)
- Toast.notify({
- type: 'error',
- message: t('versionHistory.editField.releaseNotesLengthLimit', { ns: 'workflow', limit: RELEASE_NOTES_MAX_LENGTH }),
- })
+ toast.error(t('versionHistory.editField.releaseNotesLengthLimit', { ns: 'workflow', limit: RELEASE_NOTES_MAX_LENGTH }))
return
}
else {
diff --git a/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx b/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx
index 9625204d81..482f61bb82 100644
--- a/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx
+++ b/web/app/components/app/configuration/config-prompt/advanced-prompt-input.tsx
@@ -20,8 +20,12 @@ import {
} from '@/app/components/base/icons/src/vender/line/files'
import PromptEditor from '@/app/components/base/prompt-editor'
import { INSERT_VARIABLE_VALUE_BLOCK_COMMAND } from '@/app/components/base/prompt-editor/plugins/variable-block'
-import { useToastContext } from '@/app/components/base/toast/context'
-import Tooltip from '@/app/components/base/tooltip'
+import { toast } from '@/app/components/base/ui/toast'
+import {
+ Tooltip,
+ TooltipContent,
+ TooltipTrigger,
+} from '@/app/components/base/ui/tooltip'
import ConfigContext from '@/context/debug-configuration'
import { useEventEmitterContextContext } from '@/context/event-emitter'
import { useModalContext } from '@/context/modal-context'
@@ -74,7 +78,6 @@ const AdvancedPromptInput: FC = ({
showSelectDataSet,
externalDataToolsConfig,
} = useContext(ConfigContext)
- const { notify } = useToastContext()
const { setShowExternalDataToolModal } = useModalContext()
const handleOpenExternalDataToolModal = () => {
setShowExternalDataToolModal({
@@ -94,7 +97,7 @@ const AdvancedPromptInput: FC = ({
onValidateBeforeSaveCallback: (newExternalDataTool: ExternalDataTool) => {
for (let i = 0; i < promptVariables.length; i++) {
if (promptVariables[i].key === newExternalDataTool.variable) {
- notify({ type: 'error', message: t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: promptVariables[i].key }) })
+ toast.error(t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: promptVariables[i].key }))
return false
}
}
@@ -180,13 +183,18 @@ const AdvancedPromptInput: FC = ({
{t('pageTitle.line1', { ns: 'appDebug' })}
-
+
+ )}
+ />
+
{t('promptTip', { ns: 'appDebug' })}
- )}
- />
+
+