mirror of https://github.com/langgenius/dify.git
refactor(web): update frontend toast call sites to use the new shortcut API (#33808)
Signed-off-by: yyh <yuanyouhuilyz@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
This commit is contained in:
parent
ac87704685
commit
27ed40225d
|
|
@ -95,7 +95,7 @@ describe('Cloud Plan Payment Flow', () => {
|
|||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
cleanup()
|
||||
toast.close()
|
||||
toast.dismiss()
|
||||
setupAppContext()
|
||||
mockFetchSubscriptionUrls.mockResolvedValue({ url: 'https://pay.example.com/checkout' })
|
||||
mockInvoices.mockResolvedValue({ url: 'https://billing.example.com/invoices' })
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ describe('Self-Hosted Plan Flow', () => {
|
|||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
cleanup()
|
||||
toast.close()
|
||||
toast.dismiss()
|
||||
setupAppContext()
|
||||
|
||||
// Mock window.location with minimal getter/setter (Location props are non-enumerable)
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import SideBar from '@/app/components/explore/sidebar'
|
|||
import { MediaType } from '@/hooks/use-breakpoints'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
|
||||
const { mockToastAdd } = vi.hoisted(() => ({
|
||||
mockToastAdd: vi.fn(),
|
||||
const { mockToastSuccess } = vi.hoisted(() => ({
|
||||
mockToastSuccess: vi.fn(),
|
||||
}))
|
||||
|
||||
let mockMediaType: string = MediaType.pc
|
||||
|
|
@ -53,14 +53,16 @@ vi.mock('@/service/use-explore', () => ({
|
|||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
add: mockToastAdd,
|
||||
close: vi.fn(),
|
||||
update: vi.fn(),
|
||||
promise: vi.fn(),
|
||||
},
|
||||
}))
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/app/components/base/ui/toast')>()
|
||||
return {
|
||||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
success: mockToastSuccess,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const createInstalledApp = (overrides: Partial<InstalledApp> = {}): InstalledApp => ({
|
||||
id: overrides.id ?? 'app-1',
|
||||
|
|
@ -105,9 +107,7 @@ describe('Sidebar Lifecycle Flow', () => {
|
|||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdatePinStatus).toHaveBeenCalledWith({ appId: 'app-1', isPinned: true })
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(expect.objectContaining({
|
||||
type: 'success',
|
||||
}))
|
||||
expect(mockToastSuccess).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
// Step 2: Simulate refetch returning pinned state, then unpin
|
||||
|
|
@ -124,9 +124,7 @@ describe('Sidebar Lifecycle Flow', () => {
|
|||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdatePinStatus).toHaveBeenCalledWith({ appId: 'app-1', isPinned: false })
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(expect.objectContaining({
|
||||
type: 'success',
|
||||
}))
|
||||
expect(mockToastSuccess).toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -150,10 +148,7 @@ describe('Sidebar Lifecycle Flow', () => {
|
|||
// Step 4: Uninstall API called and success toast shown
|
||||
await waitFor(() => {
|
||||
expect(mockUninstall).toHaveBeenCalledWith('app-1')
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(expect.objectContaining({
|
||||
type: 'success',
|
||||
title: 'common.api.remove',
|
||||
}))
|
||||
expect(mockToastSuccess).toHaveBeenCalledWith('common.api.remove')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -24,17 +24,11 @@ export default function CheckCode() {
|
|||
const verify = async () => {
|
||||
try {
|
||||
if (!code.trim()) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('checkCode.emptyCode', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('checkCode.emptyCode', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
if (!/\d{6}/.test(code)) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('checkCode.invalidCode', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('checkCode.invalidCode', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
|
|
|||
|
|
@ -27,15 +27,12 @@ export default function CheckCode() {
|
|||
const handleGetEMailVerificationCode = async () => {
|
||||
try {
|
||||
if (!email) {
|
||||
toast.add({ type: 'error', title: t('error.emailEmpty', { ns: 'login' }) })
|
||||
toast.error(t('error.emailEmpty', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
|
||||
if (!emailRegex.test(email)) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.emailInValid', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.emailInValid', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
|
@ -48,16 +45,10 @@ export default function CheckCode() {
|
|||
router.push(`/webapp-reset-password/check-code?${params.toString()}`)
|
||||
}
|
||||
else if (res.code === 'account_not_found') {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.registrationNotAllowed', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.registrationNotAllowed', { ns: 'login' }))
|
||||
}
|
||||
else {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: res.data,
|
||||
})
|
||||
toast.error(res.data)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
|
|
|
|||
|
|
@ -24,10 +24,7 @@ const ChangePasswordForm = () => {
|
|||
const [showConfirmPassword, setShowConfirmPassword] = useState(false)
|
||||
|
||||
const showErrorMessage = useCallback((message: string) => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: message,
|
||||
})
|
||||
toast.error(message)
|
||||
}, [])
|
||||
|
||||
const getSignInUrl = () => {
|
||||
|
|
|
|||
|
|
@ -43,24 +43,15 @@ export default function CheckCode() {
|
|||
try {
|
||||
const appCode = getAppCodeFromRedirectUrl()
|
||||
if (!code.trim()) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('checkCode.emptyCode', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('checkCode.emptyCode', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
if (!/\d{6}/.test(code)) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('checkCode.invalidCode', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('checkCode.invalidCode', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
if (!redirectUrl || !appCode) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.redirectUrlMissing', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.redirectUrlMissing', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
|
|
|||
|
|
@ -17,10 +17,7 @@ const ExternalMemberSSOAuth = () => {
|
|||
const redirectUrl = searchParams.get('redirect_url')
|
||||
|
||||
const showErrorToast = (message: string) => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: message,
|
||||
})
|
||||
toast.error(message)
|
||||
}
|
||||
|
||||
const getAppCodeFromRedirectUrl = useCallback(() => {
|
||||
|
|
|
|||
|
|
@ -22,15 +22,12 @@ export default function MailAndCodeAuth() {
|
|||
const handleGetEMailVerificationCode = async () => {
|
||||
try {
|
||||
if (!email) {
|
||||
toast.add({ type: 'error', title: t('error.emailEmpty', { ns: 'login' }) })
|
||||
toast.error(t('error.emailEmpty', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
|
||||
if (!emailRegex.test(email)) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.emailInValid', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.emailInValid', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
|
|
|||
|
|
@ -46,26 +46,20 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut
|
|||
const appCode = getAppCodeFromRedirectUrl()
|
||||
const handleEmailPasswordLogin = async () => {
|
||||
if (!email) {
|
||||
toast.add({ type: 'error', title: t('error.emailEmpty', { ns: 'login' }) })
|
||||
toast.error(t('error.emailEmpty', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
if (!emailRegex.test(email)) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.emailInValid', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.emailInValid', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
if (!password?.trim()) {
|
||||
toast.add({ type: 'error', title: t('error.passwordEmpty', { ns: 'login' }) })
|
||||
toast.error(t('error.passwordEmpty', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
|
||||
if (!redirectUrl || !appCode) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.redirectUrlMissing', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.redirectUrlMissing', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
try {
|
||||
|
|
@ -94,15 +88,12 @@ export default function MailAndPasswordAuth({ isEmailSetup }: MailAndPasswordAut
|
|||
router.replace(decodeURIComponent(redirectUrl))
|
||||
}
|
||||
else {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: res.data,
|
||||
})
|
||||
toast.error(res.data)
|
||||
}
|
||||
}
|
||||
catch (e: any) {
|
||||
if (e.code === 'authentication_failed')
|
||||
toast.add({ type: 'error', title: e.message })
|
||||
toast.error(e.message)
|
||||
}
|
||||
finally {
|
||||
setIsLoading(false)
|
||||
|
|
|
|||
|
|
@ -37,10 +37,7 @@ const SSOAuth: FC<SSOAuthProps> = ({
|
|||
const handleSSOLogin = () => {
|
||||
const appCode = getAppCodeFromRedirectUrl()
|
||||
if (!redirectUrl || !appCode) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.invalidRedirectUrlOrAppCode', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.invalidRedirectUrlOrAppCode', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
|
@ -66,10 +63,7 @@ const SSOAuth: FC<SSOAuthProps> = ({
|
|||
})
|
||||
}
|
||||
else {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.invalidSSOProtocol', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.invalidSSOProtocol', { ns: 'login' }))
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,10 +91,7 @@ export default function OAuthAuthorize() {
|
|||
globalThis.location.href = url.toString()
|
||||
}
|
||||
catch (err: any) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: `${t('error.authorizeFailed', { ns: 'oauth' })}: ${err.message}`,
|
||||
})
|
||||
toast.error(`${t('error.authorizeFailed', { ns: 'oauth' })}: ${err.message}`)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -102,11 +99,10 @@ export default function OAuthAuthorize() {
|
|||
const invalidParams = !client_id || !redirect_uri
|
||||
if ((invalidParams || isError) && !hasNotifiedRef.current) {
|
||||
hasNotifiedRef.current = true
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: invalidParams ? t('error.invalidParams', { ns: 'oauth' }) : t('error.authAppInfoFetchFailed', { ns: 'oauth' }),
|
||||
timeout: 0,
|
||||
})
|
||||
toast.error(
|
||||
invalidParams ? t('error.invalidParams', { ns: 'oauth' }) : t('error.authAppInfoFetchFailed', { ns: 'oauth' }),
|
||||
{ timeout: 0 },
|
||||
)
|
||||
}
|
||||
}, [client_id, redirect_uri, isError])
|
||||
|
||||
|
|
|
|||
|
|
@ -137,10 +137,7 @@ const Apps = ({
|
|||
})
|
||||
|
||||
setIsShowCreateModal(false)
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('newApp.appCreated', { ns: 'app' }),
|
||||
})
|
||||
toast.success(t('newApp.appCreated', { ns: 'app' }))
|
||||
if (onSuccess)
|
||||
onSuccess()
|
||||
if (app.app_id)
|
||||
|
|
@ -149,7 +146,7 @@ const Apps = ({
|
|||
getRedirection(isCurrentWorkspaceEditor, { id: app.app_id!, mode }, push)
|
||||
}
|
||||
catch {
|
||||
toast.add({ type: 'error', title: t('newApp.appCreateFailed', { ns: 'app' }) })
|
||||
toast.error(t('newApp.appCreateFailed', { ns: 'app' }))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,27 +7,25 @@ describe('base/ui/toast', () => {
|
|||
vi.clearAllMocks()
|
||||
vi.useFakeTimers({ shouldAdvanceTime: true })
|
||||
act(() => {
|
||||
toast.close()
|
||||
toast.dismiss()
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
act(() => {
|
||||
toast.close()
|
||||
toast.dismiss()
|
||||
vi.runOnlyPendingTimers()
|
||||
})
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
// Core host and manager integration.
|
||||
it('should render a toast when add is called', async () => {
|
||||
it('should render a success toast when called through the typed shortcut', async () => {
|
||||
render(<ToastHost />)
|
||||
|
||||
act(() => {
|
||||
toast.add({
|
||||
title: 'Saved',
|
||||
toast.success('Saved', {
|
||||
description: 'Your changes are available now.',
|
||||
type: 'success',
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -47,20 +45,14 @@ describe('base/ui/toast', () => {
|
|||
render(<ToastHost />)
|
||||
|
||||
act(() => {
|
||||
toast.add({
|
||||
title: 'First toast',
|
||||
})
|
||||
toast('First toast')
|
||||
})
|
||||
|
||||
expect(await screen.findByText('First toast')).toBeInTheDocument()
|
||||
|
||||
act(() => {
|
||||
toast.add({
|
||||
title: 'Second toast',
|
||||
})
|
||||
toast.add({
|
||||
title: 'Third toast',
|
||||
})
|
||||
toast('Second toast')
|
||||
toast('Third toast')
|
||||
})
|
||||
|
||||
expect(await screen.findByText('Third toast')).toBeInTheDocument()
|
||||
|
|
@ -74,13 +66,25 @@ describe('base/ui/toast', () => {
|
|||
})
|
||||
})
|
||||
|
||||
// Neutral calls should map directly to a toast with only a title.
|
||||
it('should render a neutral toast when called directly', async () => {
|
||||
render(<ToastHost />)
|
||||
|
||||
act(() => {
|
||||
toast('Neutral toast')
|
||||
})
|
||||
|
||||
expect(await screen.findByText('Neutral toast')).toBeInTheDocument()
|
||||
expect(document.body.querySelector('[aria-hidden="true"].i-ri-information-2-fill')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
// Base UI limit should cap the visible stack and mark overflow toasts as limited.
|
||||
it('should mark overflow toasts as limited when the stack exceeds the configured limit', async () => {
|
||||
render(<ToastHost limit={1} />)
|
||||
|
||||
act(() => {
|
||||
toast.add({ title: 'First toast' })
|
||||
toast.add({ title: 'Second toast' })
|
||||
toast('First toast')
|
||||
toast('Second toast')
|
||||
})
|
||||
|
||||
expect(await screen.findByText('Second toast')).toBeInTheDocument()
|
||||
|
|
@ -88,13 +92,12 @@ describe('base/ui/toast', () => {
|
|||
})
|
||||
|
||||
// Closing should work through the public manager API.
|
||||
it('should close a toast when close(id) is called', async () => {
|
||||
it('should dismiss a toast when dismiss(id) is called', async () => {
|
||||
render(<ToastHost />)
|
||||
|
||||
let toastId = ''
|
||||
act(() => {
|
||||
toastId = toast.add({
|
||||
title: 'Closable',
|
||||
toastId = toast('Closable', {
|
||||
description: 'This toast can be removed.',
|
||||
})
|
||||
})
|
||||
|
|
@ -102,7 +105,7 @@ describe('base/ui/toast', () => {
|
|||
expect(await screen.findByText('Closable')).toBeInTheDocument()
|
||||
|
||||
act(() => {
|
||||
toast.close(toastId)
|
||||
toast.dismiss(toastId)
|
||||
})
|
||||
|
||||
await waitFor(() => {
|
||||
|
|
@ -117,8 +120,7 @@ describe('base/ui/toast', () => {
|
|||
render(<ToastHost />)
|
||||
|
||||
act(() => {
|
||||
toast.add({
|
||||
title: 'Dismiss me',
|
||||
toast('Dismiss me', {
|
||||
description: 'Manual dismissal path.',
|
||||
onClose,
|
||||
})
|
||||
|
|
@ -143,9 +145,7 @@ describe('base/ui/toast', () => {
|
|||
render(<ToastHost />)
|
||||
|
||||
act(() => {
|
||||
toast.add({
|
||||
title: 'Default timeout',
|
||||
})
|
||||
toast('Default timeout')
|
||||
})
|
||||
|
||||
expect(await screen.findByText('Default timeout')).toBeInTheDocument()
|
||||
|
|
@ -170,9 +170,7 @@ describe('base/ui/toast', () => {
|
|||
render(<ToastHost timeout={3000} />)
|
||||
|
||||
act(() => {
|
||||
toast.add({
|
||||
title: 'Configured timeout',
|
||||
})
|
||||
toast('Configured timeout')
|
||||
})
|
||||
|
||||
expect(await screen.findByText('Configured timeout')).toBeInTheDocument()
|
||||
|
|
@ -197,8 +195,7 @@ describe('base/ui/toast', () => {
|
|||
render(<ToastHost />)
|
||||
|
||||
act(() => {
|
||||
toast.add({
|
||||
title: 'Custom timeout',
|
||||
toast('Custom timeout', {
|
||||
timeout: 1000,
|
||||
})
|
||||
})
|
||||
|
|
@ -214,8 +211,7 @@ describe('base/ui/toast', () => {
|
|||
})
|
||||
|
||||
act(() => {
|
||||
toast.add({
|
||||
title: 'Persistent',
|
||||
toast('Persistent', {
|
||||
timeout: 0,
|
||||
})
|
||||
})
|
||||
|
|
@ -235,10 +231,8 @@ describe('base/ui/toast', () => {
|
|||
|
||||
let toastId = ''
|
||||
act(() => {
|
||||
toastId = toast.add({
|
||||
title: 'Loading',
|
||||
toastId = toast.info('Loading', {
|
||||
description: 'Preparing your data…',
|
||||
type: 'info',
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -264,8 +258,7 @@ describe('base/ui/toast', () => {
|
|||
render(<ToastHost />)
|
||||
|
||||
act(() => {
|
||||
toast.add({
|
||||
title: 'Action toast',
|
||||
toast('Action toast', {
|
||||
actionProps: {
|
||||
children: 'Undo',
|
||||
onClick: onAction,
|
||||
|
|
|
|||
|
|
@ -57,9 +57,8 @@ const VariantExamples = () => {
|
|||
},
|
||||
} as const
|
||||
|
||||
toast.add({
|
||||
type,
|
||||
...copy[type],
|
||||
toast[type](copy[type].title, {
|
||||
description: copy[type].description,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -103,14 +102,16 @@ const StackExamples = () => {
|
|||
title: 'Ready to publish',
|
||||
description: 'The newest toast stays frontmost while older items tuck behind it.',
|
||||
},
|
||||
].forEach(item => toast.add(item))
|
||||
].forEach((item) => {
|
||||
toast[item.type](item.title, {
|
||||
description: item.description,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const createBurst = () => {
|
||||
Array.from({ length: 5 }).forEach((_, index) => {
|
||||
toast.add({
|
||||
type: index % 2 === 0 ? 'info' : 'success',
|
||||
title: `Background task ${index + 1}`,
|
||||
toast[index % 2 === 0 ? 'info' : 'success'](`Background task ${index + 1}`, {
|
||||
description: 'Use this to inspect how the stack behaves near the host limit.',
|
||||
})
|
||||
})
|
||||
|
|
@ -191,16 +192,12 @@ const PromiseExamples = () => {
|
|||
|
||||
const ActionExamples = () => {
|
||||
const createActionToast = () => {
|
||||
toast.add({
|
||||
type: 'warning',
|
||||
title: 'Project archived',
|
||||
toast.warning('Project archived', {
|
||||
description: 'You can restore it from workspace settings for the next 30 days.',
|
||||
actionProps: {
|
||||
children: 'Undo',
|
||||
onClick: () => {
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: 'Project restored',
|
||||
toast.success('Project restored', {
|
||||
description: 'The workspace is active again.',
|
||||
})
|
||||
},
|
||||
|
|
@ -209,17 +206,12 @@ const ActionExamples = () => {
|
|||
}
|
||||
|
||||
const createLongCopyToast = () => {
|
||||
toast.add({
|
||||
type: 'info',
|
||||
title: 'Knowledge ingestion in progress',
|
||||
toast.info('Knowledge ingestion in progress', {
|
||||
description: 'This longer example helps validate line wrapping, close button alignment, and action button placement when the content spans multiple rows.',
|
||||
actionProps: {
|
||||
children: 'View details',
|
||||
onClick: () => {
|
||||
toast.add({
|
||||
type: 'info',
|
||||
title: 'Job details opened',
|
||||
})
|
||||
toast.info('Job details opened')
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
@ -243,9 +235,7 @@ const ActionExamples = () => {
|
|||
|
||||
const UpdateExamples = () => {
|
||||
const createUpdatableToast = () => {
|
||||
const toastId = toast.add({
|
||||
type: 'info',
|
||||
title: 'Import started',
|
||||
const toastId = toast.info('Import started', {
|
||||
description: 'Preparing assets and metadata for processing.',
|
||||
timeout: 0,
|
||||
})
|
||||
|
|
@ -261,7 +251,7 @@ const UpdateExamples = () => {
|
|||
}
|
||||
|
||||
const clearAll = () => {
|
||||
toast.close()
|
||||
toast.dismiss()
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import type {
|
|||
ToastManagerUpdateOptions,
|
||||
ToastObject,
|
||||
} from '@base-ui/react/toast'
|
||||
import type { ReactNode } from 'react'
|
||||
import { Toast as BaseToast } from '@base-ui/react/toast'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
|
@ -44,6 +45,9 @@ export type ToastUpdateOptions = Omit<ToastManagerUpdateOptions<ToastData>, 'dat
|
|||
type?: ToastType
|
||||
}
|
||||
|
||||
export type ToastOptions = Omit<ToastAddOptions, 'title'>
|
||||
export type TypedToastOptions = Omit<ToastOptions, 'type'>
|
||||
|
||||
type ToastPromiseResultOption<Value> = string | ToastUpdateOptions | ((value: Value) => string | ToastUpdateOptions)
|
||||
|
||||
export type ToastPromiseOptions<Value> = {
|
||||
|
|
@ -57,6 +61,21 @@ export type ToastHostProps = {
|
|||
limit?: number
|
||||
}
|
||||
|
||||
type ToastDismiss = (toastId?: string) => void
|
||||
type ToastCall = (title: ReactNode, options?: ToastOptions) => string
|
||||
type TypedToastCall = (title: ReactNode, options?: TypedToastOptions) => string
|
||||
|
||||
export type ToastApi = {
|
||||
(title: ReactNode, options?: ToastOptions): string
|
||||
success: TypedToastCall
|
||||
error: TypedToastCall
|
||||
warning: TypedToastCall
|
||||
info: TypedToastCall
|
||||
dismiss: ToastDismiss
|
||||
update: (toastId: string, options: ToastUpdateOptions) => void
|
||||
promise: <Value>(promiseValue: Promise<Value>, options: ToastPromiseOptions<Value>) => Promise<Value>
|
||||
}
|
||||
|
||||
const toastManager = BaseToast.createToastManager<ToastData>()
|
||||
|
||||
function isToastType(type: string): type is ToastType {
|
||||
|
|
@ -67,21 +86,48 @@ function getToastType(type?: string): ToastType | undefined {
|
|||
return type && isToastType(type) ? type : undefined
|
||||
}
|
||||
|
||||
export const toast = {
|
||||
add(options: ToastAddOptions) {
|
||||
return toastManager.add(options)
|
||||
},
|
||||
close(toastId?: string) {
|
||||
toastManager.close(toastId)
|
||||
},
|
||||
update(toastId: string, options: ToastUpdateOptions) {
|
||||
toastManager.update(toastId, options)
|
||||
},
|
||||
promise<Value>(promiseValue: Promise<Value>, options: ToastPromiseOptions<Value>) {
|
||||
return toastManager.promise(promiseValue, options)
|
||||
},
|
||||
function addToast(options: ToastAddOptions) {
|
||||
return toastManager.add(options)
|
||||
}
|
||||
|
||||
const showToast: ToastCall = (title, options) => addToast({
|
||||
...options,
|
||||
title,
|
||||
})
|
||||
|
||||
const dismissToast: ToastDismiss = (toastId) => {
|
||||
toastManager.close(toastId)
|
||||
}
|
||||
|
||||
function createTypedToast(type: ToastType): TypedToastCall {
|
||||
return (title, options) => addToast({
|
||||
...options,
|
||||
title,
|
||||
type,
|
||||
})
|
||||
}
|
||||
|
||||
function updateToast(toastId: string, options: ToastUpdateOptions) {
|
||||
toastManager.update(toastId, options)
|
||||
}
|
||||
|
||||
function promiseToast<Value>(promiseValue: Promise<Value>, options: ToastPromiseOptions<Value>) {
|
||||
return toastManager.promise(promiseValue, options)
|
||||
}
|
||||
|
||||
export const toast: ToastApi = Object.assign(
|
||||
showToast,
|
||||
{
|
||||
success: createTypedToast('success'),
|
||||
error: createTypedToast('error'),
|
||||
warning: createTypedToast('warning'),
|
||||
info: createTypedToast('info'),
|
||||
dismiss: dismissToast,
|
||||
update: updateToast,
|
||||
promise: promiseToast,
|
||||
},
|
||||
)
|
||||
|
||||
function ToastIcon({ type }: { type?: ToastType }) {
|
||||
return type
|
||||
? <span aria-hidden="true" className={cn('h-5 w-5', TOAST_TONE_STYLES[type].iconClassName)} />
|
||||
|
|
|
|||
|
|
@ -70,7 +70,7 @@ beforeAll(() => {
|
|||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
toast.close()
|
||||
toast.dismiss()
|
||||
mockUseAppContext.mockReturnValue({ isCurrentWorkspaceManager: true })
|
||||
mockUseAsyncWindowOpen.mockReturnValue(vi.fn(async open => await open()))
|
||||
mockBillingInvoices.mockResolvedValue({ url: 'https://billing.example' })
|
||||
|
|
|
|||
|
|
@ -66,10 +66,7 @@ const CloudPlanItem: FC<CloudPlanItemProps> = ({
|
|||
return
|
||||
|
||||
if (!isCurrentWorkspaceManager) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('buyPermissionDeniedTip', { ns: 'billing' }),
|
||||
})
|
||||
toast.error(t('buyPermissionDeniedTip', { ns: 'billing' }))
|
||||
return
|
||||
}
|
||||
setLoading(true)
|
||||
|
|
@ -82,7 +79,7 @@ const CloudPlanItem: FC<CloudPlanItemProps> = ({
|
|||
throw new Error('Failed to open billing page')
|
||||
}, {
|
||||
onError: (err) => {
|
||||
toast.add({ type: 'error', title: err.message || String(err) })
|
||||
toast.error(err.message || String(err))
|
||||
},
|
||||
})
|
||||
return
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ beforeAll(() => {
|
|||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
toast.close()
|
||||
toast.dismiss()
|
||||
mockUseAppContext.mockReturnValue({ isCurrentWorkspaceManager: true })
|
||||
assignedHref = ''
|
||||
})
|
||||
|
|
|
|||
|
|
@ -56,10 +56,7 @@ const SelfHostedPlanItem: FC<SelfHostedPlanItemProps> = ({
|
|||
const handleGetPayUrl = useCallback(() => {
|
||||
// Only workspace manager can buy plan
|
||||
if (!isCurrentWorkspaceManager) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('buyPermissionDeniedTip', { ns: 'billing' }),
|
||||
})
|
||||
toast.error(t('buyPermissionDeniedTip', { ns: 'billing' }))
|
||||
return
|
||||
}
|
||||
if (isFreePlan) {
|
||||
|
|
|
|||
|
|
@ -13,17 +13,20 @@ vi.mock('@/app/components/base/amplitude', () => ({
|
|||
trackEvent: vi.fn(),
|
||||
}))
|
||||
|
||||
const { mockToastNotify } = vi.hoisted(() => ({
|
||||
mockToastNotify: vi.fn(),
|
||||
const { mockToastSuccess, mockToastError } = vi.hoisted(() => ({
|
||||
mockToastSuccess: vi.fn(),
|
||||
mockToastError: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/toast', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/app/components/base/toast')>()
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/app/components/base/ui/toast')>()
|
||||
return {
|
||||
...actual,
|
||||
default: Object.assign(actual.default, {
|
||||
notify: mockToastNotify,
|
||||
}),
|
||||
toast: {
|
||||
...actual.toast,
|
||||
success: mockToastSuccess,
|
||||
error: mockToastError,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -45,8 +48,8 @@ vi.mock('@/service/knowledge/use-dataset', () => ({
|
|||
describe('CreateCard', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockToastNotify.mockReset()
|
||||
mockToastNotify.mockImplementation(() => ({ clear: vi.fn() }))
|
||||
mockToastSuccess.mockReset()
|
||||
mockToastError.mockReset()
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
|
|
|
|||
|
|
@ -20,10 +20,7 @@ const CreateCard = () => {
|
|||
onSuccess: (data) => {
|
||||
if (data) {
|
||||
const { id } = data
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('creation.successTip', { ns: 'datasetPipeline' }),
|
||||
})
|
||||
toast.success(t('creation.successTip', { ns: 'datasetPipeline' }))
|
||||
invalidDatasetList()
|
||||
trackEvent('create_datasets_from_scratch', {
|
||||
dataset_id: id,
|
||||
|
|
@ -32,10 +29,7 @@ const CreateCard = () => {
|
|||
}
|
||||
},
|
||||
onError: () => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('creation.errorTip', { ns: 'datasetPipeline' }),
|
||||
})
|
||||
toast.error(t('creation.errorTip', { ns: 'datasetPipeline' }))
|
||||
},
|
||||
})
|
||||
}, [createEmptyDataset, push, invalidDatasetList, t])
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ vi.mock('@/service/use-pipeline', () => ({
|
|||
useInvalidCustomizedTemplateList: () => mockInvalidCustomizedTemplateList,
|
||||
}))
|
||||
|
||||
const { mockToastAdd } = vi.hoisted(() => ({
|
||||
mockToastAdd: vi.fn(),
|
||||
const { mockToastError } = vi.hoisted(() => ({
|
||||
mockToastError: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
|
|
@ -24,7 +24,7 @@ vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
|||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
add: mockToastAdd,
|
||||
error: mockToastError,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
@ -95,7 +95,7 @@ describe('EditPipelineInfo', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockToastAdd.mockReset()
|
||||
mockToastError.mockReset()
|
||||
_mockOnSelect = undefined
|
||||
_mockOnClose = undefined
|
||||
})
|
||||
|
|
@ -243,10 +243,7 @@ describe('EditPipelineInfo', () => {
|
|||
fireEvent.click(saveButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: 'datasetPipeline.editPipelineInfoNameRequired',
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith('datasetPipeline.editPipelineInfoNameRequired')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -14,8 +14,9 @@ vi.mock('@/app/components/base/amplitude', () => ({
|
|||
trackEvent: vi.fn(),
|
||||
}))
|
||||
|
||||
const { mockToastAdd } = vi.hoisted(() => ({
|
||||
mockToastAdd: vi.fn(),
|
||||
const { mockToastSuccess, mockToastError } = vi.hoisted(() => ({
|
||||
mockToastSuccess: vi.fn(),
|
||||
mockToastError: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
|
|
@ -24,7 +25,8 @@ vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
|||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
add: mockToastAdd,
|
||||
success: mockToastSuccess,
|
||||
error: mockToastError,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
@ -182,7 +184,8 @@ describe('TemplateCard', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockToastAdd.mockReset()
|
||||
mockToastSuccess.mockReset()
|
||||
mockToastError.mockReset()
|
||||
mockIsExporting = false
|
||||
_capturedOnConfirm = undefined
|
||||
_capturedOnCancel = undefined
|
||||
|
|
@ -237,10 +240,7 @@ describe('TemplateCard', () => {
|
|||
fireEvent.click(chooseButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: expect.any(String),
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith(expect.any(String))
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -300,10 +300,7 @@ describe('TemplateCard', () => {
|
|||
fireEvent.click(chooseButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'success',
|
||||
title: expect.any(String),
|
||||
})
|
||||
expect(mockToastSuccess).toHaveBeenCalledWith(expect.any(String))
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -318,10 +315,7 @@ describe('TemplateCard', () => {
|
|||
fireEvent.click(chooseButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: expect.any(String),
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith(expect.any(String))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -467,10 +461,7 @@ describe('TemplateCard', () => {
|
|||
fireEvent.click(exportButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'success',
|
||||
title: expect.any(String),
|
||||
})
|
||||
expect(mockToastSuccess).toHaveBeenCalledWith(expect.any(String))
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -485,10 +476,7 @@ describe('TemplateCard', () => {
|
|||
fireEvent.click(exportButton)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: expect.any(String),
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith(expect.any(String))
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -67,10 +67,7 @@ const EditPipelineInfo = ({
|
|||
|
||||
const handleSave = useCallback(async () => {
|
||||
if (!name) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('editPipelineInfoNameRequired', { ns: 'datasetPipeline' }),
|
||||
})
|
||||
toast.error(t('editPipelineInfoNameRequired', { ns: 'datasetPipeline' }))
|
||||
return
|
||||
}
|
||||
const request = {
|
||||
|
|
|
|||
|
|
@ -50,10 +50,7 @@ const TemplateCard = ({
|
|||
const handleUseTemplate = useCallback(async () => {
|
||||
const { data: pipelineTemplateInfo } = await getPipelineTemplateInfo()
|
||||
if (!pipelineTemplateInfo) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('creation.errorTip', { ns: 'datasetPipeline' }),
|
||||
})
|
||||
toast.error(t('creation.errorTip', { ns: 'datasetPipeline' }))
|
||||
return
|
||||
}
|
||||
const request = {
|
||||
|
|
@ -61,10 +58,7 @@ const TemplateCard = ({
|
|||
}
|
||||
await createDataset(request, {
|
||||
onSuccess: async (newDataset) => {
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('creation.successTip', { ns: 'datasetPipeline' }),
|
||||
})
|
||||
toast.success(t('creation.successTip', { ns: 'datasetPipeline' }))
|
||||
invalidDatasetList()
|
||||
if (newDataset.pipeline_id)
|
||||
await handleCheckPluginDependencies(newDataset.pipeline_id, true)
|
||||
|
|
@ -76,10 +70,7 @@ const TemplateCard = ({
|
|||
push(`/datasets/${newDataset.dataset_id}/pipeline`)
|
||||
},
|
||||
onError: () => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('creation.errorTip', { ns: 'datasetPipeline' }),
|
||||
})
|
||||
toast.error(t('creation.errorTip', { ns: 'datasetPipeline' }))
|
||||
},
|
||||
})
|
||||
}, [getPipelineTemplateInfo, createDataset, t, handleCheckPluginDependencies, push, invalidDatasetList, pipeline.name, pipeline.id, type])
|
||||
|
|
@ -109,16 +100,10 @@ const TemplateCard = ({
|
|||
onSuccess: (res) => {
|
||||
const blob = new Blob([res.data], { type: 'application/yaml' })
|
||||
downloadBlob({ data: blob, fileName: `${pipeline.name}.pipeline` })
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('exportDSL.successTip', { ns: 'datasetPipeline' }),
|
||||
})
|
||||
toast.success(t('exportDSL.successTip', { ns: 'datasetPipeline' }))
|
||||
},
|
||||
onError: () => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('exportDSL.errorTip', { ns: 'datasetPipeline' }),
|
||||
})
|
||||
toast.error(t('exportDSL.errorTip', { ns: 'datasetPipeline' }))
|
||||
},
|
||||
})
|
||||
}, [t, isExporting, pipeline.id, pipeline.name, exportPipelineDSL])
|
||||
|
|
|
|||
|
|
@ -32,9 +32,9 @@ vi.mock('@/service/base', () => ({
|
|||
ssePost: mockSsePost,
|
||||
}))
|
||||
|
||||
// Mock toast.add because the component reports errors through the UI toast manager.
|
||||
const { mockToastAdd } = vi.hoisted(() => ({
|
||||
mockToastAdd: vi.fn(),
|
||||
// Mock toast.error because the component reports errors through the UI toast manager.
|
||||
const { mockToastError } = vi.hoisted(() => ({
|
||||
mockToastError: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
|
|
@ -43,7 +43,7 @@ vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
|||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
add: mockToastAdd,
|
||||
error: mockToastError,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
@ -197,7 +197,7 @@ const createDefaultProps = (overrides?: Partial<OnlineDocumentsProps>): OnlineDo
|
|||
describe('OnlineDocuments', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockToastAdd.mockReset()
|
||||
mockToastError.mockReset()
|
||||
|
||||
// Reset store state
|
||||
mockStoreState.documentsData = []
|
||||
|
|
@ -515,10 +515,7 @@ describe('OnlineDocuments', () => {
|
|||
render(<OnlineDocuments {...props} />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: 'Something went wrong',
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith('Something went wrong')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -780,10 +777,7 @@ describe('OnlineDocuments', () => {
|
|||
render(<OnlineDocuments {...props} />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: 'API Error Message',
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith('API Error Message')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -1100,10 +1094,7 @@ describe('OnlineDocuments', () => {
|
|||
render(<OnlineDocuments {...props} />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: 'Failed to fetch documents',
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith('Failed to fetch documents')
|
||||
})
|
||||
|
||||
// Should still show loading since documentsData is empty
|
||||
|
|
|
|||
|
|
@ -96,10 +96,7 @@ const OnlineDocuments = ({
|
|||
setDocumentsData(documentsData.data as DataSourceNotionWorkspace[])
|
||||
},
|
||||
onDataSourceNodeError: (error: DataSourceNodeErrorResponse) => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: error.error,
|
||||
})
|
||||
toast.error(error.error)
|
||||
},
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -45,8 +45,8 @@ vi.mock('@/service/use-datasource', () => ({
|
|||
useGetDataSourceAuth: mockUseGetDataSourceAuth,
|
||||
}))
|
||||
|
||||
const { mockToastAdd } = vi.hoisted(() => ({
|
||||
mockToastAdd: vi.fn(),
|
||||
const { mockToastError } = vi.hoisted(() => ({
|
||||
mockToastError: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
|
|
@ -55,7 +55,7 @@ vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
|||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
add: mockToastAdd,
|
||||
error: mockToastError,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
@ -236,7 +236,7 @@ const resetMockStoreState = () => {
|
|||
describe('OnlineDrive', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockToastAdd.mockReset()
|
||||
mockToastError.mockReset()
|
||||
|
||||
// Reset store state
|
||||
resetMockStoreState()
|
||||
|
|
@ -547,10 +547,7 @@ describe('OnlineDrive', () => {
|
|||
render(<OnlineDrive {...props} />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: errorMessage,
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith(errorMessage)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -921,10 +918,7 @@ describe('OnlineDrive', () => {
|
|||
render(<OnlineDrive {...props} />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: errorMessage,
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith(errorMessage)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -105,10 +105,7 @@ const OnlineDrive = ({
|
|||
isLoadingRef.current = false
|
||||
},
|
||||
onDataSourceNodeError: (error: DataSourceNodeErrorResponse) => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: error.error,
|
||||
})
|
||||
toast.error(error.error)
|
||||
setIsLoading(false)
|
||||
isLoadingRef.current = false
|
||||
},
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import { CrawlStep } from '@/models/datasets'
|
|||
import { PipelineInputVarType } from '@/models/pipeline'
|
||||
import Options from '../index'
|
||||
|
||||
const { mockToastAdd } = vi.hoisted(() => ({
|
||||
mockToastAdd: vi.fn(),
|
||||
const { mockToastError } = vi.hoisted(() => ({
|
||||
mockToastError: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
|
|
@ -16,7 +16,7 @@ vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
|||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
add: mockToastAdd,
|
||||
error: mockToastError,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
@ -131,7 +131,7 @@ const createDefaultProps = (overrides?: Partial<OptionsProps>): OptionsProps =>
|
|||
describe('Options', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockToastAdd.mockReset()
|
||||
mockToastError.mockReset()
|
||||
|
||||
// Reset mock form values
|
||||
Object.keys(mockFormValues).forEach(key => delete mockFormValues[key])
|
||||
|
|
@ -643,11 +643,7 @@ describe('Options', () => {
|
|||
fireEvent.click(screen.getByRole('button'))
|
||||
|
||||
// Assert - Toast should be called with error message
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'error',
|
||||
}),
|
||||
)
|
||||
expect(mockToastError).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should handle validation error and display field name in message', () => {
|
||||
|
|
@ -665,12 +661,7 @@ describe('Options', () => {
|
|||
fireEvent.click(screen.getByRole('button'))
|
||||
|
||||
// Assert - Toast message should contain field path
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'error',
|
||||
title: expect.stringContaining('email_address'),
|
||||
}),
|
||||
)
|
||||
expect(mockToastError).toHaveBeenCalledWith(expect.stringContaining('email_address'))
|
||||
})
|
||||
|
||||
it('should handle empty variables gracefully', () => {
|
||||
|
|
@ -719,12 +710,8 @@ describe('Options', () => {
|
|||
fireEvent.click(screen.getByRole('button'))
|
||||
|
||||
// Assert - Toast should be called once (only first error)
|
||||
expect(mockToastAdd).toHaveBeenCalledTimes(1)
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'error',
|
||||
}),
|
||||
)
|
||||
expect(mockToastError).toHaveBeenCalledTimes(1)
|
||||
expect(mockToastError).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should handle validation pass when all required fields have values', () => {
|
||||
|
|
@ -743,7 +730,7 @@ describe('Options', () => {
|
|||
fireEvent.click(screen.getByRole('button'))
|
||||
|
||||
// Assert - No toast error, onSubmit called
|
||||
expect(mockToastAdd).not.toHaveBeenCalled()
|
||||
expect(mockToastError).not.toHaveBeenCalled()
|
||||
expect(mockOnSubmit).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
|
@ -840,7 +827,7 @@ describe('Options', () => {
|
|||
fireEvent.click(screen.getByRole('button'))
|
||||
|
||||
expect(mockOnSubmit).toHaveBeenCalled()
|
||||
expect(mockToastAdd).not.toHaveBeenCalled()
|
||||
expect(mockToastError).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should fail validation with invalid data', () => {
|
||||
|
|
@ -859,7 +846,7 @@ describe('Options', () => {
|
|||
fireEvent.click(screen.getByRole('button'))
|
||||
|
||||
expect(mockOnSubmit).not.toHaveBeenCalled()
|
||||
expect(mockToastAdd).toHaveBeenCalled()
|
||||
expect(mockToastError).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should show error toast message when validation fails', () => {
|
||||
|
|
@ -876,12 +863,7 @@ describe('Options', () => {
|
|||
|
||||
fireEvent.click(screen.getByRole('button'))
|
||||
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'error',
|
||||
title: expect.any(String),
|
||||
}),
|
||||
)
|
||||
expect(mockToastError).toHaveBeenCalledWith(expect.any(String))
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -44,10 +44,7 @@ const Options = ({
|
|||
const issues = result.error.issues
|
||||
const firstIssue = issues[0]
|
||||
const errorMessage = `"${firstIssue.path.join('.')}" ${firstIssue.message}`
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: errorMessage,
|
||||
})
|
||||
toast.error(errorMessage)
|
||||
return errorMessage
|
||||
}
|
||||
return undefined
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@ import OnlineDocumentPreview from '../online-document-preview'
|
|||
|
||||
// Uses global react-i18next mock from web/vitest.setup.ts
|
||||
|
||||
const { mockToastAdd } = vi.hoisted(() => ({
|
||||
mockToastAdd: vi.fn(),
|
||||
const { mockToastError } = vi.hoisted(() => ({
|
||||
mockToastError: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
|
|
@ -15,7 +15,7 @@ vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
|||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
add: mockToastAdd,
|
||||
error: mockToastError,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
@ -67,7 +67,7 @@ const defaultProps = {
|
|||
describe('OnlineDocumentPreview', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockToastAdd.mockReset()
|
||||
mockToastError.mockReset()
|
||||
mockPipelineId.mockReturnValue('pipeline-123')
|
||||
mockUsePreviewOnlineDocument.mockReturnValue({
|
||||
mutateAsync: mockMutateAsync,
|
||||
|
|
@ -270,10 +270,7 @@ describe('OnlineDocumentPreview', () => {
|
|||
render(<OnlineDocumentPreview {...defaultProps} />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: errorMessage,
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith(errorMessage)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -288,10 +285,7 @@ describe('OnlineDocumentPreview', () => {
|
|||
render(<OnlineDocumentPreview {...defaultProps} />)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: 'Network Error',
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith('Network Error')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -44,10 +44,7 @@ const OnlineDocumentPreview = ({
|
|||
setContent(data.content)
|
||||
},
|
||||
onError(error) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: error.message,
|
||||
})
|
||||
toast.error(error.message)
|
||||
},
|
||||
})
|
||||
}, [currentPage.page_id])
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import Actions from '../actions'
|
|||
import Form from '../form'
|
||||
import Header from '../header'
|
||||
|
||||
const { mockToastAdd } = vi.hoisted(() => ({
|
||||
mockToastAdd: vi.fn(),
|
||||
const { mockToastError } = vi.hoisted(() => ({
|
||||
mockToastError: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
|
|
@ -17,7 +17,7 @@ vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
|||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
add: mockToastAdd,
|
||||
error: mockToastError,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
@ -346,7 +346,7 @@ describe('Form', () => {
|
|||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockToastAdd.mockReset()
|
||||
mockToastError.mockReset()
|
||||
})
|
||||
|
||||
describe('Rendering', () => {
|
||||
|
|
@ -455,10 +455,7 @@ describe('Form', () => {
|
|||
|
||||
// Assert - validation error should be shown
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: '"field1" is required',
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith('"field1" is required')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -577,10 +574,7 @@ describe('Form', () => {
|
|||
fireEvent.submit(form)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: '"field1" is required',
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith('"field1" is required')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -594,7 +588,7 @@ describe('Form', () => {
|
|||
|
||||
// Assert - wait a bit and verify onSubmit was not called
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalled()
|
||||
expect(mockToastError).toHaveBeenCalled()
|
||||
})
|
||||
expect(onSubmit).not.toHaveBeenCalled()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|||
import { z } from 'zod'
|
||||
import Form from '../form'
|
||||
|
||||
const { mockToastAdd } = vi.hoisted(() => ({
|
||||
mockToastAdd: vi.fn(),
|
||||
const { mockToastError } = vi.hoisted(() => ({
|
||||
mockToastError: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
|
|
@ -14,7 +14,7 @@ vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
|||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
add: mockToastAdd,
|
||||
error: mockToastError,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
|
@ -57,7 +57,7 @@ const defaultProps = {
|
|||
describe('Form (process-documents)', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
mockToastAdd.mockReset()
|
||||
mockToastError.mockReset()
|
||||
})
|
||||
|
||||
// Verify basic rendering of form structure
|
||||
|
|
@ -119,12 +119,7 @@ describe('Form (process-documents)', () => {
|
|||
fireEvent.submit(form)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'error',
|
||||
title: '"name" Name is required',
|
||||
}),
|
||||
)
|
||||
expect(mockToastError).toHaveBeenCalledWith('"name" Name is required')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -137,7 +132,7 @@ describe('Form (process-documents)', () => {
|
|||
await waitFor(() => {
|
||||
expect(defaultProps.onSubmit).toHaveBeenCalled()
|
||||
})
|
||||
expect(mockToastAdd).not.toHaveBeenCalled()
|
||||
expect(mockToastError).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -34,10 +34,7 @@ const Form = ({
|
|||
const issues = result.error.issues
|
||||
const firstIssue = issues[0]
|
||||
const errorMessage = `"${firstIssue.path.join('.')}" ${firstIssue.message}`
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: errorMessage,
|
||||
})
|
||||
toast.error(errorMessage)
|
||||
return errorMessage
|
||||
}
|
||||
return undefined
|
||||
|
|
|
|||
|
|
@ -13,7 +13,8 @@ vi.mock('@/next/navigation', () => ({
|
|||
}),
|
||||
}))
|
||||
|
||||
const toastAddSpy = vi.spyOn(toast, 'add')
|
||||
const toastErrorSpy = vi.spyOn(toast, 'error')
|
||||
const toastSuccessSpy = vi.spyOn(toast, 'success')
|
||||
|
||||
// Mock dataset detail context
|
||||
let mockIndexingTechnique = IndexingType.QUALIFIED
|
||||
|
|
@ -128,7 +129,7 @@ describe('NewSegmentModal', () => {
|
|||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.useRealTimers()
|
||||
toast.close()
|
||||
toast.dismiss()
|
||||
mockFullScreen = false
|
||||
mockIndexingTechnique = IndexingType.QUALIFIED
|
||||
})
|
||||
|
|
@ -248,11 +249,7 @@ describe('NewSegmentModal', () => {
|
|||
fireEvent.click(screen.getByTestId('save-btn'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(toastAddSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'error',
|
||||
}),
|
||||
)
|
||||
expect(toastErrorSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -262,11 +259,7 @@ describe('NewSegmentModal', () => {
|
|||
fireEvent.click(screen.getByTestId('save-btn'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(toastAddSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'error',
|
||||
}),
|
||||
)
|
||||
expect(toastErrorSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -277,11 +270,7 @@ describe('NewSegmentModal', () => {
|
|||
fireEvent.click(screen.getByTestId('save-btn'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(toastAddSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'error',
|
||||
}),
|
||||
)
|
||||
expect(toastErrorSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -327,11 +316,7 @@ describe('NewSegmentModal', () => {
|
|||
fireEvent.click(screen.getByTestId('save-btn'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(toastAddSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'success',
|
||||
}),
|
||||
)
|
||||
expect(toastSuccessSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ vi.mock('@/next/navigation', () => ({
|
|||
}),
|
||||
}))
|
||||
|
||||
const toastAddSpy = vi.spyOn(toast, 'add')
|
||||
const toastErrorSpy = vi.spyOn(toast, 'error')
|
||||
const toastSuccessSpy = vi.spyOn(toast, 'success')
|
||||
|
||||
// Mock document context
|
||||
let mockParentMode = 'paragraph'
|
||||
|
|
@ -93,7 +94,7 @@ describe('NewChildSegmentModal', () => {
|
|||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
vi.useRealTimers()
|
||||
toast.close()
|
||||
toast.dismiss()
|
||||
mockFullScreen = false
|
||||
mockParentMode = 'paragraph'
|
||||
})
|
||||
|
|
@ -189,11 +190,7 @@ describe('NewChildSegmentModal', () => {
|
|||
fireEvent.click(screen.getByTestId('save-btn'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(toastAddSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'error',
|
||||
}),
|
||||
)
|
||||
expect(toastErrorSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -244,11 +241,7 @@ describe('NewChildSegmentModal', () => {
|
|||
fireEvent.click(screen.getByTestId('save-btn'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(toastAddSpy).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: 'success',
|
||||
}),
|
||||
)
|
||||
expect(toastSuccessSpy).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -53,16 +53,14 @@ const NewChildSegmentModal: FC<NewChildSegmentModalProps> = ({
|
|||
const params: SegmentUpdater = { content: '' }
|
||||
|
||||
if (!content.trim())
|
||||
return toast.add({ type: 'error', title: t('segment.contentEmpty', { ns: 'datasetDocuments' }) })
|
||||
return toast.error(t('segment.contentEmpty', { ns: 'datasetDocuments' }))
|
||||
|
||||
params.content = content
|
||||
|
||||
setLoading(true)
|
||||
await addChildSegment({ datasetId, documentId, segmentId: chunkId, body: params }, {
|
||||
onSuccess(res) {
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('segment.childChunkAdded', { ns: 'datasetDocuments' }),
|
||||
toast.success(t('segment.childChunkAdded', { ns: 'datasetDocuments' }), {
|
||||
actionProps: isFullDocMode
|
||||
? {
|
||||
children: t('operation.view', { ns: 'common' }),
|
||||
|
|
|
|||
|
|
@ -63,16 +63,10 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
|
|||
const params: SegmentUpdater = { content: '', attachment_ids: [] }
|
||||
if (docForm === ChunkingMode.qa) {
|
||||
if (!question.trim()) {
|
||||
return toast.add({
|
||||
type: 'error',
|
||||
title: t('segment.questionEmpty', { ns: 'datasetDocuments' }),
|
||||
})
|
||||
return toast.error(t('segment.questionEmpty', { ns: 'datasetDocuments' }))
|
||||
}
|
||||
if (!answer.trim()) {
|
||||
return toast.add({
|
||||
type: 'error',
|
||||
title: t('segment.answerEmpty', { ns: 'datasetDocuments' }),
|
||||
})
|
||||
return toast.error(t('segment.answerEmpty', { ns: 'datasetDocuments' }))
|
||||
}
|
||||
|
||||
params.content = question
|
||||
|
|
@ -80,10 +74,7 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
|
|||
}
|
||||
else {
|
||||
if (!question.trim()) {
|
||||
return toast.add({
|
||||
type: 'error',
|
||||
title: t('segment.contentEmpty', { ns: 'datasetDocuments' }),
|
||||
})
|
||||
return toast.error(t('segment.contentEmpty', { ns: 'datasetDocuments' }))
|
||||
}
|
||||
|
||||
params.content = question
|
||||
|
|
@ -98,9 +89,7 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
|
|||
setLoading(true)
|
||||
await addSegment({ datasetId, documentId, body: params }, {
|
||||
onSuccess() {
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('segment.chunkAdded', { ns: 'datasetDocuments' }),
|
||||
toast.success(t('segment.chunkAdded', { ns: 'datasetDocuments' }), {
|
||||
actionProps: {
|
||||
children: t('operation.view', { ns: 'common' }),
|
||||
onClick: viewNewlyAddedChunk,
|
||||
|
|
|
|||
|
|
@ -21,12 +21,19 @@ vi.mock('@/context/i18n', () => ({
|
|||
useDocLink: () => (path?: string) => `https://docs.dify.ai/en${path || ''}`,
|
||||
}))
|
||||
|
||||
const mockNotify = vi.hoisted(() => vi.fn())
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
add: mockNotify,
|
||||
},
|
||||
}))
|
||||
const mockToastSuccess = vi.hoisted(() => vi.fn())
|
||||
const mockToastError = vi.hoisted(() => vi.fn())
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/app/components/base/ui/toast')>()
|
||||
return {
|
||||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
success: mockToastSuccess,
|
||||
error: mockToastError,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
// Mock modal context
|
||||
vi.mock('@/context/modal-context', () => ({
|
||||
|
|
@ -162,10 +169,7 @@ describe('ExternalKnowledgeBaseConnector', () => {
|
|||
})
|
||||
|
||||
// Verify success notification
|
||||
expect(mockNotify).toHaveBeenCalledWith({
|
||||
type: 'success',
|
||||
title: 'dataset.externalKnowledgeForm.connectedSuccess',
|
||||
})
|
||||
expect(mockToastSuccess).toHaveBeenCalledWith('dataset.externalKnowledgeForm.connectedSuccess')
|
||||
|
||||
// Verify navigation back
|
||||
expect(mockRouterBack).toHaveBeenCalledTimes(1)
|
||||
|
|
@ -204,10 +208,7 @@ describe('ExternalKnowledgeBaseConnector', () => {
|
|||
|
||||
// Verify error notification
|
||||
await waitFor(() => {
|
||||
expect(mockNotify).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: 'dataset.externalKnowledgeForm.connectedFailed',
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith('dataset.externalKnowledgeForm.connectedFailed')
|
||||
})
|
||||
|
||||
// Verify no navigation
|
||||
|
|
@ -226,10 +227,7 @@ describe('ExternalKnowledgeBaseConnector', () => {
|
|||
await fillFormAndSubmit(user)
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockNotify).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: 'dataset.externalKnowledgeForm.connectedFailed',
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith('dataset.externalKnowledgeForm.connectedFailed')
|
||||
})
|
||||
|
||||
expect(mockRouterBack).not.toHaveBeenCalled()
|
||||
|
|
@ -272,10 +270,7 @@ describe('ExternalKnowledgeBaseConnector', () => {
|
|||
resolvePromise({ id: 'new-id' })
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockNotify).toHaveBeenCalledWith({
|
||||
type: 'success',
|
||||
title: 'dataset.externalKnowledgeForm.connectedSuccess',
|
||||
})
|
||||
expect(mockToastSuccess).toHaveBeenCalledWith('dataset.externalKnowledgeForm.connectedSuccess')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ const ExternalKnowledgeBaseConnector = () => {
|
|||
setLoading(true)
|
||||
const result = await createExternalKnowledgeBase({ body: formValue })
|
||||
if (result && result.id) {
|
||||
toast.add({ type: 'success', title: t('externalKnowledgeForm.connectedSuccess', { ns: 'dataset' }) })
|
||||
toast.success(t('externalKnowledgeForm.connectedSuccess', { ns: 'dataset' }))
|
||||
trackEvent('create_external_knowledge_base', {
|
||||
provider: formValue.provider,
|
||||
name: formValue.name,
|
||||
|
|
@ -31,7 +31,7 @@ const ExternalKnowledgeBaseConnector = () => {
|
|||
}
|
||||
catch (error) {
|
||||
console.error('Error creating external knowledge base:', error)
|
||||
toast.add({ type: 'error', title: t('externalKnowledgeForm.connectedFailed', { ns: 'dataset' }) })
|
||||
toast.error(t('externalKnowledgeForm.connectedFailed', { ns: 'dataset' }))
|
||||
}
|
||||
setLoading(false)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import { MediaType } from '@/hooks/use-breakpoints'
|
|||
import { AppModeEnum } from '@/types/app'
|
||||
import SideBar from '../index'
|
||||
|
||||
const { mockToastAdd } = vi.hoisted(() => ({
|
||||
mockToastAdd: vi.fn(),
|
||||
const { mockToastSuccess } = vi.hoisted(() => ({
|
||||
mockToastSuccess: vi.fn(),
|
||||
}))
|
||||
|
||||
const mockSegments = ['apps']
|
||||
|
|
@ -47,14 +47,16 @@ vi.mock('@/service/use-explore', () => ({
|
|||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
add: mockToastAdd,
|
||||
close: vi.fn(),
|
||||
update: vi.fn(),
|
||||
promise: vi.fn(),
|
||||
},
|
||||
}))
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/app/components/base/ui/toast')>()
|
||||
return {
|
||||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
success: mockToastSuccess,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
const createInstalledApp = (overrides: Partial<InstalledApp> = {}): InstalledApp => ({
|
||||
id: overrides.id ?? 'app-123',
|
||||
|
|
@ -166,10 +168,7 @@ describe('SideBar', () => {
|
|||
|
||||
await waitFor(() => {
|
||||
expect(mockUninstall).toHaveBeenCalledWith('app-123')
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(expect.objectContaining({
|
||||
type: 'success',
|
||||
title: 'common.api.remove',
|
||||
}))
|
||||
expect(mockToastSuccess).toHaveBeenCalledWith('common.api.remove')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
@ -183,10 +182,7 @@ describe('SideBar', () => {
|
|||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdatePinStatus).toHaveBeenCalledWith({ appId: 'app-123', isPinned: true })
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(expect.objectContaining({
|
||||
type: 'success',
|
||||
title: 'common.api.success',
|
||||
}))
|
||||
expect(mockToastSuccess).toHaveBeenCalledWith('common.api.success')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -51,18 +51,12 @@ const SideBar = () => {
|
|||
const id = currId
|
||||
await uninstallApp(id)
|
||||
setShowConfirm(false)
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('api.remove', { ns: 'common' }),
|
||||
})
|
||||
toast.success(t('api.remove', { ns: 'common' }))
|
||||
}
|
||||
|
||||
const handleUpdatePinStatus = async (id: string, isPinned: boolean) => {
|
||||
await updatePinStatus({ appId: id, isPinned })
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('api.success', { ns: 'common' }),
|
||||
})
|
||||
toast.success(t('api.success', { ns: 'common' }))
|
||||
}
|
||||
|
||||
const pinnedAppsCount = installedApps.filter(({ is_pinned }) => is_pinned).length
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ vi.mock('react-i18next', async () => {
|
|||
})
|
||||
})
|
||||
|
||||
const mockNotify = vi.hoisted(() => vi.fn())
|
||||
const mockToastSuccess = vi.hoisted(() => vi.fn())
|
||||
const mockUpdateModelList = vi.hoisted(() => vi.fn())
|
||||
const mockInvalidateDefaultModel = vi.hoisted(() => vi.fn())
|
||||
const mockUpdateDefaultModel = vi.hoisted(() => vi.fn(() => Promise.resolve({ result: 'success' })))
|
||||
|
|
@ -43,11 +43,16 @@ vi.mock('@/context/provider-context', () => ({
|
|||
}),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
add: mockNotify,
|
||||
},
|
||||
}))
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/app/components/base/ui/toast')>()
|
||||
return {
|
||||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
success: mockToastSuccess,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
vi.mock('../../hooks', () => ({
|
||||
useModelList: () => ({
|
||||
|
|
@ -148,10 +153,7 @@ describe('SystemModel', () => {
|
|||
|
||||
await waitFor(() => {
|
||||
expect(mockUpdateDefaultModel).toHaveBeenCalledTimes(1)
|
||||
expect(mockNotify).toHaveBeenCalledWith({
|
||||
type: 'success',
|
||||
title: 'Modified successfully',
|
||||
})
|
||||
expect(mockToastSuccess).toHaveBeenCalledWith('Modified successfully')
|
||||
expect(mockInvalidateDefaultModel).toHaveBeenCalledTimes(5)
|
||||
expect(mockUpdateModelList).toHaveBeenCalledTimes(5)
|
||||
})
|
||||
|
|
@ -173,7 +175,7 @@ describe('SystemModel', () => {
|
|||
expect(mockUpdateDefaultModel).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
expect(screen.getByRole('button', { name: /save/i })).toBeInTheDocument()
|
||||
expect(mockNotify).not.toHaveBeenCalled()
|
||||
expect(mockToastSuccess).not.toHaveBeenCalled()
|
||||
expect(mockInvalidateDefaultModel).not.toHaveBeenCalled()
|
||||
expect(mockUpdateModelList).not.toHaveBeenCalled()
|
||||
})
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ const SystemModel: FC<SystemModelSelectorProps> = ({
|
|||
},
|
||||
})
|
||||
if (res.result === 'success') {
|
||||
toast.add({ type: 'success', title: t('actionMsg.modifiedSuccessfully', { ns: 'common' }) })
|
||||
toast.success(t('actionMsg.modifiedSuccessfully', { ns: 'common' }))
|
||||
setOpen(false)
|
||||
|
||||
const allModelTypes = [ModelTypeEnum.textGeneration, ModelTypeEnum.textEmbedding, ModelTypeEnum.rerank, ModelTypeEnum.speech2text, ModelTypeEnum.tts]
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import { DeleteConfirm } from '../delete-confirm'
|
|||
|
||||
const mockRefetch = vi.fn()
|
||||
const mockDelete = vi.fn()
|
||||
const mockToastAdd = vi.hoisted(() => vi.fn())
|
||||
const mockToastSuccess = vi.hoisted(() => vi.fn())
|
||||
const mockToastError = vi.hoisted(() => vi.fn())
|
||||
|
||||
vi.mock('../use-subscription-list', () => ({
|
||||
useSubscriptionList: () => ({ refetch: mockRefetch }),
|
||||
|
|
@ -14,11 +15,17 @@ vi.mock('@/service/use-triggers', () => ({
|
|||
useDeleteTriggerSubscription: () => ({ mutate: mockDelete, isPending: false }),
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
add: mockToastAdd,
|
||||
},
|
||||
}))
|
||||
vi.mock('@/app/components/base/ui/toast', async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import('@/app/components/base/ui/toast')>()
|
||||
return {
|
||||
...actual,
|
||||
toast: {
|
||||
...actual.toast,
|
||||
success: mockToastSuccess,
|
||||
error: mockToastError,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
|
|
@ -42,7 +49,7 @@ describe('DeleteConfirm', () => {
|
|||
fireEvent.click(screen.getByRole('button', { name: /pluginTrigger\.subscription\.list\.item\.actions\.deleteConfirm\.confirm/ }))
|
||||
|
||||
expect(mockDelete).not.toHaveBeenCalled()
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(expect.objectContaining({ type: 'error' }))
|
||||
expect(mockToastError).toHaveBeenCalledWith('pluginTrigger.subscription.list.item.actions.deleteConfirm.confirmInputWarning')
|
||||
})
|
||||
|
||||
it('should allow deletion after matching input name', () => {
|
||||
|
|
@ -87,6 +94,6 @@ describe('DeleteConfirm', () => {
|
|||
|
||||
fireEvent.click(screen.getByRole('button', { name: /pluginTrigger\.subscription\.list\.item\.actions\.deleteConfirm\.confirm/ }))
|
||||
|
||||
expect(mockToastAdd).toHaveBeenCalledWith(expect.objectContaining({ type: 'error', title: 'network error' }))
|
||||
expect(mockToastError).toHaveBeenCalledWith('network error')
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -41,26 +41,17 @@ export const DeleteConfirm = (props: Props) => {
|
|||
|
||||
const onConfirm = () => {
|
||||
if (workflowsInUse > 0 && inputName !== currentName) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t(`${tPrefix}.confirmInputWarning`, { ns: 'pluginTrigger' }),
|
||||
})
|
||||
toast.error(t(`${tPrefix}.confirmInputWarning`, { ns: 'pluginTrigger' }))
|
||||
return
|
||||
}
|
||||
deleteSubscription(currentId, {
|
||||
onSuccess: () => {
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t(`${tPrefix}.success`, { ns: 'pluginTrigger', name: currentName }),
|
||||
})
|
||||
toast.success(t(`${tPrefix}.success`, { ns: 'pluginTrigger', name: currentName }))
|
||||
refetch?.()
|
||||
onClose(true)
|
||||
},
|
||||
onError: (error: unknown) => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: error instanceof Error ? error.message : t(`${tPrefix}.error`, { ns: 'pluginTrigger', name: currentName }),
|
||||
})
|
||||
toast.error(error instanceof Error ? error.message : t(`${tPrefix}.error`, { ns: 'pluginTrigger', name: currentName }))
|
||||
},
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import GetSchema from '../get-schema'
|
|||
vi.mock('@/service/tools', () => ({
|
||||
importSchemaFromURL: vi.fn(),
|
||||
}))
|
||||
const mockToastAdd = vi.hoisted(() => vi.fn())
|
||||
const mockToastError = vi.hoisted(() => vi.fn())
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
add: mockToastAdd,
|
||||
error: mockToastError,
|
||||
},
|
||||
}))
|
||||
const importSchemaFromURLMock = vi.mocked(importSchemaFromURL)
|
||||
|
|
@ -30,10 +30,7 @@ describe('GetSchema', () => {
|
|||
fireEvent.change(input, { target: { value: 'ftp://invalid' } })
|
||||
fireEvent.click(screen.getByText('common.operation.ok'))
|
||||
|
||||
expect(mockToastAdd).toHaveBeenCalledWith({
|
||||
type: 'error',
|
||||
title: 'tools.createTool.urlError',
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith('tools.createTool.urlError')
|
||||
})
|
||||
|
||||
it('imports schema from url when valid', async () => {
|
||||
|
|
|
|||
|
|
@ -27,10 +27,7 @@ const GetSchema: FC<Props> = ({
|
|||
const [isParsing, setIsParsing] = useState(false)
|
||||
const handleImportFromUrl = async () => {
|
||||
if (!importUrl.startsWith('http://') && !importUrl.startsWith('https://')) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('createTool.urlError', { ns: 'tools' }),
|
||||
})
|
||||
toast.error(t('createTool.urlError', { ns: 'tools' }))
|
||||
return
|
||||
}
|
||||
setIsParsing(true)
|
||||
|
|
|
|||
|
|
@ -48,10 +48,10 @@ vi.mock('@/service/use-plugins', () => ({
|
|||
}),
|
||||
}))
|
||||
|
||||
const mockToastAdd = vi.hoisted(() => vi.fn())
|
||||
const mockToastError = vi.hoisted(() => vi.fn())
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
add: mockToastAdd,
|
||||
error: mockToastError,
|
||||
},
|
||||
}))
|
||||
|
||||
|
|
@ -310,10 +310,7 @@ 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',
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith('tools.mcp.modal.invalidServerUrl')
|
||||
})
|
||||
|
||||
it('should not call onConfirm with invalid server identifier', async () => {
|
||||
|
|
@ -335,10 +332,7 @@ 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',
|
||||
})
|
||||
expect(mockToastError).toHaveBeenCalledWith('tools.mcp.modal.invalidServerIdentifier')
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -82,11 +82,11 @@ const MCPModalContent: FC<MCPModalContentProps> = ({
|
|||
|
||||
const submit = async () => {
|
||||
if (!isValidUrl(state.url)) {
|
||||
toast.add({ type: 'error', title: t('mcp.modal.invalidServerUrl', { ns: 'tools' }) })
|
||||
toast.error(t('mcp.modal.invalidServerUrl', { ns: 'tools' }))
|
||||
return
|
||||
}
|
||||
if (!isValidServerID(state.serverIdentifier.trim())) {
|
||||
toast.add({ type: 'error', title: t('mcp.modal.invalidServerIdentifier', { ns: 'tools' }) })
|
||||
toast.error(t('mcp.modal.invalidServerIdentifier', { ns: 'tools' }))
|
||||
return
|
||||
}
|
||||
const formattedHeaders = state.headers.reduce((acc, item) => {
|
||||
|
|
|
|||
|
|
@ -71,10 +71,10 @@ vi.mock('@/app/components/tools/edit-custom-collection-modal', () => ({
|
|||
}))
|
||||
|
||||
// Mock toast
|
||||
const mockToastNotify = vi.fn()
|
||||
const mockToastSuccess = vi.fn()
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: {
|
||||
add: (options: { type: string, title: string }) => mockToastNotify(options),
|
||||
success: (title: string) => mockToastSuccess(title),
|
||||
},
|
||||
}))
|
||||
|
||||
|
|
@ -198,10 +198,7 @@ describe('CustomCreateCard', () => {
|
|||
fireEvent.click(screen.getByTestId('submit-modal'))
|
||||
|
||||
await waitFor(() => {
|
||||
expect(mockToastNotify).toHaveBeenCalledWith({
|
||||
type: 'success',
|
||||
title: expect.any(String),
|
||||
})
|
||||
expect(mockToastSuccess).toHaveBeenCalledWith(expect.any(String))
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
|||
|
|
@ -92,9 +92,13 @@ vi.mock('@/app/components/base/confirm', () => ({
|
|||
: null,
|
||||
}))
|
||||
|
||||
const mockToastAdd = vi.hoisted(() => vi.fn())
|
||||
const mockToastSuccess = vi.hoisted(() => vi.fn())
|
||||
const mockToastError = vi.hoisted(() => vi.fn())
|
||||
vi.mock('@/app/components/base/ui/toast', () => ({
|
||||
toast: { add: mockToastAdd },
|
||||
toast: {
|
||||
success: mockToastSuccess,
|
||||
error: mockToastError,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock('@/app/components/header/indicator', () => ({
|
||||
|
|
|
|||
|
|
@ -21,10 +21,7 @@ const Contribute = ({ onRefreshData }: Props) => {
|
|||
const [isShowEditCollectionToolModal, setIsShowEditCustomCollectionModal] = useState(false)
|
||||
const doCreateCustomToolCollection = async (data: CustomCollectionBackend) => {
|
||||
await createCustomCollection(data)
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
toast.success(t('api.actionSuccess', { ns: 'common' }))
|
||||
setIsShowEditCustomCollectionModal(false)
|
||||
onRefreshData()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,19 +122,13 @@ 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.add({
|
||||
type: 'success',
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
toast.success(t('api.actionSuccess', { ns: 'common' }))
|
||||
setIsShowEditCustomCollectionModal(false)
|
||||
}
|
||||
const doRemoveCustomToolCollection = async () => {
|
||||
await removeCustomCollection(collection?.name as string)
|
||||
onRefreshData()
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
toast.success(t('api.actionSuccess', { ns: 'common' }))
|
||||
setIsShowEditCustomCollectionModal(false)
|
||||
}
|
||||
// workflow provider
|
||||
|
|
@ -161,10 +155,7 @@ const ProviderDetail = ({
|
|||
const removeWorkflowToolProvider = async () => {
|
||||
await deleteWorkflowTool(collection.id)
|
||||
onRefreshData()
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
toast.success(t('api.actionSuccess', { ns: 'common' }))
|
||||
setIsShowEditWorkflowToolModal(false)
|
||||
}
|
||||
const updateWorkflowToolProvider = async (data: WorkflowToolProviderRequest & Partial<{
|
||||
|
|
@ -175,10 +166,7 @@ const ProviderDetail = ({
|
|||
invalidateAllWorkflowTools()
|
||||
onRefreshData()
|
||||
getWorkflowToolProvider()
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
toast.success(t('api.actionSuccess', { ns: 'common' }))
|
||||
setIsShowEditWorkflowToolModal(false)
|
||||
}
|
||||
const onClickCustomToolDelete = () => {
|
||||
|
|
@ -385,19 +373,13 @@ const ProviderDetail = ({
|
|||
onCancel={() => setShowSettingAuth(false)}
|
||||
onSaved={async (value) => {
|
||||
await updateBuiltInToolCredential(collection.name, value)
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
toast.success(t('api.actionSuccess', { ns: 'common' }))
|
||||
await onRefreshData()
|
||||
setShowSettingAuth(false)
|
||||
}}
|
||||
onRemove={async () => {
|
||||
await removeBuiltInToolCredential(collection.name)
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
toast.success(t('api.actionSuccess', { ns: 'common' }))
|
||||
await onRefreshData()
|
||||
setShowSettingAuth(false)
|
||||
}}
|
||||
|
|
|
|||
|
|
@ -40,17 +40,11 @@ const OutputVarList: FC<Props> = ({
|
|||
const { run: validateVarInput } = useDebounceFn((existingVariables: typeof list, newKey: string) => {
|
||||
const result = checkKeys([newKey], true)
|
||||
if (!result.isValid) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t(`varKeyError.${result.errorMessageKey}`, { ns: 'appDebug', key: result.errorKey }),
|
||||
})
|
||||
toast.error(t(`varKeyError.${result.errorMessageKey}`, { ns: 'appDebug', key: result.errorKey }))
|
||||
return
|
||||
}
|
||||
if (existingVariables.some(key => key.variable?.trim() === newKey.trim())) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: newKey }),
|
||||
})
|
||||
toast.error(t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: newKey }))
|
||||
}
|
||||
}, { wait: 500 })
|
||||
|
||||
|
|
|
|||
|
|
@ -53,17 +53,11 @@ const VarList: FC<Props> = ({
|
|||
const { run: validateVarInput } = useDebounceFn((list: Variable[], newKey: string) => {
|
||||
const result = checkKeys([newKey], true)
|
||||
if (!result.isValid) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t(`varKeyError.${result.errorMessageKey}`, { ns: 'appDebug', key: result.errorKey }),
|
||||
})
|
||||
toast.error(t(`varKeyError.${result.errorMessageKey}`, { ns: 'appDebug', key: result.errorKey }))
|
||||
return
|
||||
}
|
||||
if (list.some(item => item.variable?.trim() === newKey.trim())) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: newKey }),
|
||||
})
|
||||
toast.error(t('varKeyError.keyAlreadyExists', { ns: 'appDebug', key: newKey }))
|
||||
}
|
||||
}, { wait: 500 })
|
||||
|
||||
|
|
|
|||
|
|
@ -120,10 +120,7 @@ export const VersionHistoryPanel = ({
|
|||
break
|
||||
case VersionHistoryContextMenuOptions.copyId:
|
||||
copy(item.id)
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('versionHistory.action.copyIdSuccess', { ns: 'workflow' }),
|
||||
})
|
||||
toast.success(t('versionHistory.action.copyIdSuccess', { ns: 'workflow' }))
|
||||
break
|
||||
case VersionHistoryContextMenuOptions.exportDSL:
|
||||
handleExportDSL?.(false, item.id)
|
||||
|
|
@ -156,18 +153,12 @@ export const VersionHistoryPanel = ({
|
|||
workflowStore.setState({ isRestoring: false })
|
||||
workflowStore.setState({ backupDraft: undefined })
|
||||
handleRefreshWorkflowDraft()
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('versionHistory.action.restoreSuccess', { ns: 'workflow' }),
|
||||
})
|
||||
toast.success(t('versionHistory.action.restoreSuccess', { ns: 'workflow' }))
|
||||
deleteAllInspectVars()
|
||||
invalidAllLastRun()
|
||||
}
|
||||
catch {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('versionHistory.action.restoreFailure', { ns: 'workflow' }),
|
||||
})
|
||||
toast.error(t('versionHistory.action.restoreFailure', { ns: 'workflow' }))
|
||||
}
|
||||
finally {
|
||||
resetWorkflowVersionHistory()
|
||||
|
|
@ -180,19 +171,13 @@ export const VersionHistoryPanel = ({
|
|||
await deleteWorkflow(deleteVersionUrl?.(id) || '', {
|
||||
onSuccess: () => {
|
||||
setDeleteConfirmOpen(false)
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('versionHistory.action.deleteSuccess', { ns: 'workflow' }),
|
||||
})
|
||||
toast.success(t('versionHistory.action.deleteSuccess', { ns: 'workflow' }))
|
||||
resetWorkflowVersionHistory()
|
||||
deleteAllInspectVars()
|
||||
invalidAllLastRun()
|
||||
},
|
||||
onError: () => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('versionHistory.action.deleteFailure', { ns: 'workflow' }),
|
||||
})
|
||||
toast.error(t('versionHistory.action.deleteFailure', { ns: 'workflow' }))
|
||||
},
|
||||
onSettled: () => {
|
||||
setDeleteConfirmOpen(false)
|
||||
|
|
@ -210,17 +195,11 @@ export const VersionHistoryPanel = ({
|
|||
}, {
|
||||
onSuccess: () => {
|
||||
setEditModalOpen(false)
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('versionHistory.action.updateSuccess', { ns: 'workflow' }),
|
||||
})
|
||||
toast.success(t('versionHistory.action.updateSuccess', { ns: 'workflow' }))
|
||||
resetWorkflowVersionHistory()
|
||||
},
|
||||
onError: () => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('versionHistory.action.updateFailure', { ns: 'workflow' }),
|
||||
})
|
||||
toast.error(t('versionHistory.action.updateFailure', { ns: 'workflow' }))
|
||||
},
|
||||
onSettled: () => {
|
||||
setEditModalOpen(false)
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ const WorkflowPreview = () => {
|
|||
copy(content)
|
||||
else
|
||||
copy(JSON.stringify(content))
|
||||
toast.add({ type: 'success', title: t('actionMsg.copySuccessfully', { ns: 'common' }) })
|
||||
toast.success(t('actionMsg.copySuccessfully', { ns: 'common' }))
|
||||
}}
|
||||
>
|
||||
<span className="i-ri-clipboard-line h-3.5 w-3.5" />
|
||||
|
|
|
|||
|
|
@ -29,10 +29,7 @@ const ChangePasswordForm = () => {
|
|||
const [showSuccess, setShowSuccess] = useState(false)
|
||||
|
||||
const showErrorMessage = useCallback((message: string) => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: message,
|
||||
})
|
||||
toast.error(message)
|
||||
}, [])
|
||||
|
||||
const valid = useCallback(() => {
|
||||
|
|
|
|||
|
|
@ -23,17 +23,11 @@ export default function CheckCode() {
|
|||
const verify = async () => {
|
||||
try {
|
||||
if (!code.trim()) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('checkCode.emptyCode', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('checkCode.emptyCode', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
if (!/\d{6}/.test(code)) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('checkCode.invalidCode', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('checkCode.invalidCode', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
|
|
|||
|
|
@ -26,15 +26,12 @@ export default function CheckCode() {
|
|||
const handleGetEMailVerificationCode = async () => {
|
||||
try {
|
||||
if (!email) {
|
||||
toast.add({ type: 'error', title: t('error.emailEmpty', { ns: 'login' }) })
|
||||
toast.error(t('error.emailEmpty', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
|
||||
if (!emailRegex.test(email)) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.emailInValid', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.emailInValid', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
|
@ -47,10 +44,7 @@ export default function CheckCode() {
|
|||
router.push(`/reset-password/check-code?${params.toString()}`)
|
||||
}
|
||||
else {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: res.data,
|
||||
})
|
||||
toast.error(res.data)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
|
|
|
|||
|
|
@ -24,10 +24,7 @@ const ChangePasswordForm = () => {
|
|||
const [showConfirmPassword, setShowConfirmPassword] = useState(false)
|
||||
|
||||
const showErrorMessage = useCallback((message: string) => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: message,
|
||||
})
|
||||
toast.error(message)
|
||||
}, [])
|
||||
|
||||
const getSignInUrl = () => {
|
||||
|
|
|
|||
|
|
@ -31,17 +31,11 @@ export default function CheckCode() {
|
|||
const verify = async () => {
|
||||
try {
|
||||
if (!code.trim()) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('checkCode.emptyCode', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('checkCode.emptyCode', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
if (!/\d{6}/.test(code)) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('checkCode.invalidCode', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('checkCode.invalidCode', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
|
|
|||
|
|
@ -26,15 +26,12 @@ export default function MailAndCodeAuth({ isInvite }: MailAndCodeAuthProps) {
|
|||
const handleGetEMailVerificationCode = async () => {
|
||||
try {
|
||||
if (!email) {
|
||||
toast.add({ type: 'error', title: t('error.emailEmpty', { ns: 'login' }) })
|
||||
toast.error(t('error.emailEmpty', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
|
||||
if (!emailRegex.test(email)) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.emailInValid', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.emailInValid', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
|
|
|||
|
|
@ -35,18 +35,15 @@ export default function MailAndPasswordAuth({ isInvite, isEmailSetup, allowRegis
|
|||
|
||||
const handleEmailPasswordLogin = async () => {
|
||||
if (!email) {
|
||||
toast.add({ type: 'error', title: t('error.emailEmpty', { ns: 'login' }) })
|
||||
toast.error(t('error.emailEmpty', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
if (!emailRegex.test(email)) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.emailInValid', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.emailInValid', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
if (!password?.trim()) {
|
||||
toast.add({ type: 'error', title: t('error.passwordEmpty', { ns: 'login' }) })
|
||||
toast.error(t('error.passwordEmpty', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -83,18 +80,12 @@ export default function MailAndPasswordAuth({ isInvite, isEmailSetup, allowRegis
|
|||
}
|
||||
}
|
||||
else {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: res.data,
|
||||
})
|
||||
toast.error(res.data)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
if ((error as ResponseError).code === 'authentication_failed') {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.invalidEmailOrPassword', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.invalidEmailOrPassword', { ns: 'login' }))
|
||||
}
|
||||
}
|
||||
finally {
|
||||
|
|
|
|||
|
|
@ -49,10 +49,7 @@ const SSOAuth: FC<SSOAuthProps> = ({
|
|||
})
|
||||
}
|
||||
else {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.invalidSSOProtocol', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.invalidSSOProtocol', { ns: 'login' }))
|
||||
setIsLoading(false)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,10 +48,7 @@ const NormalForm = () => {
|
|||
}
|
||||
|
||||
if (message) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: message,
|
||||
})
|
||||
toast.error(message)
|
||||
}
|
||||
setAllMethodsAreDisabled(!systemFeatures.enable_social_oauth_login && !systemFeatures.enable_email_code_login && !systemFeatures.enable_email_password_login && !systemFeatures.sso_enforced_for_signin)
|
||||
setShowORLine((systemFeatures.enable_social_oauth_login || systemFeatures.sso_enforced_for_signin) && (systemFeatures.enable_email_code_login || systemFeatures.enable_email_password_login))
|
||||
|
|
|
|||
|
|
@ -26,17 +26,11 @@ export default function CheckCode() {
|
|||
const verify = async () => {
|
||||
try {
|
||||
if (!code.trim()) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('checkCode.emptyCode', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('checkCode.emptyCode', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
if (!/\d{6}/.test(code)) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('checkCode.invalidCode', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('checkCode.invalidCode', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
setIsLoading(true)
|
||||
|
|
@ -47,10 +41,7 @@ export default function CheckCode() {
|
|||
router.push(`/signup/set-password?${params.toString()}`)
|
||||
}
|
||||
else {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('checkCode.invalidCode', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('checkCode.invalidCode', { ns: 'login' }))
|
||||
}
|
||||
}
|
||||
catch (error) { console.error(error) }
|
||||
|
|
|
|||
|
|
@ -30,14 +30,11 @@ export default function Form({
|
|||
return
|
||||
|
||||
if (!email) {
|
||||
toast.add({ type: 'error', title: t('error.emailEmpty', { ns: 'login' }) })
|
||||
toast.error(t('error.emailEmpty', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
if (!emailRegex.test(email)) {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: t('error.emailInValid', { ns: 'login' }),
|
||||
})
|
||||
toast.error(t('error.emailInValid', { ns: 'login' }))
|
||||
return
|
||||
}
|
||||
const res = await submitMail({ email, language: locale })
|
||||
|
|
|
|||
|
|
@ -37,10 +37,7 @@ const ChangePasswordForm = () => {
|
|||
const { mutateAsync: register, isPending } = useMailRegister()
|
||||
|
||||
const showErrorMessage = useCallback((message: string) => {
|
||||
toast.add({
|
||||
type: 'error',
|
||||
title: message,
|
||||
})
|
||||
toast.error(message)
|
||||
}, [])
|
||||
|
||||
const valid = useCallback(() => {
|
||||
|
|
@ -82,10 +79,7 @@ const ChangePasswordForm = () => {
|
|||
})
|
||||
Cookies.remove('utm_info') // Clean up: remove utm_info cookie
|
||||
|
||||
toast.add({
|
||||
type: 'success',
|
||||
title: t('api.actionSuccess', { ns: 'common' }),
|
||||
})
|
||||
toast.success(t('api.actionSuccess', { ns: 'common' }))
|
||||
router.replace('/apps')
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,9 +133,7 @@ export const ProviderContextProvider = ({
|
|||
const quota = anthropic.system_configuration.quota_configurations.find(item => item.quota_type === anthropic.system_configuration.current_quota_type)
|
||||
if (quota && quota.is_valid && quota.quota_used < quota.quota_limit) {
|
||||
localStorage.setItem('anthropic_quota_notice', 'true')
|
||||
toast.add({
|
||||
type: 'info',
|
||||
title: t('provider.anthropicHosted.trialQuotaTip', { ns: 'common' }),
|
||||
toast.info(t('provider.anthropicHosted.trialQuotaTip', { ns: 'common' }), {
|
||||
timeout: 60000,
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ const afterResponseErrorCode = (otherOptions: IOtherOptions): AfterResponseHook
|
|||
const shouldNotifyError = response.status !== 401 && errorData && !otherOptions.silent
|
||||
|
||||
if (shouldNotifyError)
|
||||
toast.add({ type: 'error', title: errorData.message })
|
||||
toast.error(errorData.message)
|
||||
|
||||
if (response.status === 403 && errorData?.code === 'already_setup')
|
||||
globalThis.location.href = `${globalThis.location.origin}/signin`
|
||||
|
|
|
|||
Loading…
Reference in New Issue