use base ui toast

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

View File

@ -4,7 +4,6 @@ import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createMockProviderContextValue } from '@/__mocks__/provider-context'
import { useToastContext } from '@/app/components/base/toast/context'
import { contactSalesUrl, defaultPlan } from '@/app/components/billing/config'
import { Plan } from '@/app/components/billing/type'
import {
@ -19,6 +18,19 @@ import { useProviderContext } from '@/context/provider-context'
import { defaultSystemFeatures } from '@/types/feature'
import CustomPage from '../index'
const { mockToast } = vi.hoisted(() => {
const mockToast = Object.assign(vi.fn(), {
success: vi.fn(),
error: vi.fn(),
warning: vi.fn(),
info: vi.fn(),
dismiss: vi.fn(),
update: vi.fn(),
promise: vi.fn(),
})
return { mockToast }
})
vi.mock('@/context/provider-context', () => ({
useProviderContext: vi.fn(),
}))
@ -35,15 +47,14 @@ vi.mock('@/context/app-context', async (importOriginal) => {
vi.mock('@/context/global-public-context', () => ({
useGlobalPublicStore: vi.fn(),
}))
vi.mock('@/app/components/base/toast/context', () => ({
useToastContext: vi.fn(),
vi.mock('@/app/components/base/ui/toast', () => ({
toast: mockToast,
}))
const mockUseProviderContext = vi.mocked(useProviderContext)
const mockUseModalContext = vi.mocked(useModalContext)
const mockUseAppContext = vi.mocked(useAppContext)
const mockUseGlobalPublicStore = vi.mocked(useGlobalPublicStore)
const mockUseToastContext = vi.mocked(useToastContext)
const createProviderContext = ({
enableBilling = false,
@ -106,9 +117,6 @@ describe('CustomPage', () => {
systemFeatures: createSystemFeatures(),
setSystemFeatures: vi.fn(),
}))
mockUseToastContext.mockReturnValue({
notify: vi.fn(),
} as unknown as ReturnType<typeof useToastContext>)
})
// Integration coverage for the page and its child custom brand section.

View File

@ -5,7 +5,6 @@ import { act, renderHook } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createMockProviderContextValue } from '@/__mocks__/provider-context'
import { getImageUploadErrorMessage, imageUpload } from '@/app/components/base/image-uploader/utils'
import { useToastContext } from '@/app/components/base/toast/context'
import { defaultPlan } from '@/app/components/billing/config'
import { Plan } from '@/app/components/billing/type'
import {
@ -20,8 +19,22 @@ import { updateCurrentWorkspace } from '@/service/common'
import { defaultSystemFeatures } from '@/types/feature'
import useWebAppBrand from '../use-web-app-brand'
vi.mock('@/app/components/base/toast/context', () => ({
useToastContext: vi.fn(),
const { mockNotify, mockToast } = vi.hoisted(() => {
const mockNotify = vi.fn()
const mockToast = 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(),
})
return { mockNotify, mockToast }
})
vi.mock('@/app/components/base/ui/toast', () => ({
toast: mockToast,
}))
vi.mock('@/service/common', () => ({
updateCurrentWorkspace: vi.fn(),
@ -44,8 +57,6 @@ vi.mock('@/app/components/base/image-uploader/utils', () => ({
getImageUploadErrorMessage: vi.fn(),
}))
const mockNotify = vi.fn()
const mockUseToastContext = vi.mocked(useToastContext)
const mockUpdateCurrentWorkspace = vi.mocked(updateCurrentWorkspace)
const mockUseAppContext = vi.mocked(useAppContext)
const mockUseProviderContext = vi.mocked(useProviderContext)
@ -119,7 +130,6 @@ describe('useWebAppBrand', () => {
appContextValue = createAppContextValue()
systemFeatures = createSystemFeatures()
mockUseToastContext.mockReturnValue({ notify: mockNotify } as unknown as ReturnType<typeof useToastContext>)
mockUpdateCurrentWorkspace.mockResolvedValue(appContextValue.currentWorkspace)
mockUseAppContext.mockImplementation(() => appContextValue)
mockUseProviderContext.mockReturnValue(createProviderContext())

View File

@ -2,7 +2,7 @@ import type { ChangeEvent } from 'react'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { getImageUploadErrorMessage, imageUpload } from '@/app/components/base/image-uploader/utils'
import { useToastContext } from '@/app/components/base/toast/context'
import { toast } from '@/app/components/base/ui/toast'
import { Plan } from '@/app/components/billing/type'
import { useAppContext } from '@/context/app-context'
import { useGlobalPublicStore } from '@/context/global-public-context'
@ -12,28 +12,20 @@ import { updateCurrentWorkspace } from '@/service/common'
const MAX_LOGO_FILE_SIZE = 5 * 1024 * 1024
const CUSTOM_CONFIG_URL = '/workspaces/custom-config'
const WEB_APP_LOGO_UPLOAD_URL = '/workspaces/custom-config/webapp-logo/upload'
const useWebAppBrand = () => {
const { t } = useTranslation()
const { notify } = useToastContext()
const { plan, enableBilling } = useProviderContext()
const {
currentWorkspace,
mutateCurrentWorkspace,
isCurrentWorkspaceManager,
} = useAppContext()
const { currentWorkspace, mutateCurrentWorkspace, isCurrentWorkspaceManager } = useAppContext()
const [fileId, setFileId] = useState('')
const [imgKey, setImgKey] = useState(() => Date.now())
const [uploadProgress, setUploadProgress] = useState(0)
const systemFeatures = useGlobalPublicStore(s => s.systemFeatures)
const isSandbox = enableBilling && plan.type === Plan.sandbox
const uploading = uploadProgress > 0 && uploadProgress < 100
const webappLogo = currentWorkspace.custom_config?.replace_webapp_logo || ''
const webappBrandRemoved = currentWorkspace.custom_config?.remove_webapp_brand
const uploadDisabled = isSandbox || webappBrandRemoved || !isCurrentWorkspaceManager
const workspaceLogo = systemFeatures.branding.enabled ? systemFeatures.branding.workspace_logo : ''
const persistWorkspaceBrand = async (body: Record<string, unknown>) => {
await updateCurrentWorkspace({
url: CUSTOM_CONFIG_URL,
@ -41,18 +33,14 @@ const useWebAppBrand = () => {
})
mutateCurrentWorkspace()
}
const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (!file)
return
if (file.size > MAX_LOGO_FILE_SIZE) {
notify({ type: 'error', message: t('imageUploader.uploadFromComputerLimit', { ns: 'common', size: 5 }) })
toast.error(t('imageUploader.uploadFromComputerLimit', { ns: 'common', size: 5 }))
return
}
imageUpload({
file,
onProgressCallback: setUploadProgress,
@ -61,17 +49,12 @@ const useWebAppBrand = () => {
setFileId(res.id)
},
onErrorCallback: (error) => {
const errorMessage = getImageUploadErrorMessage(
error,
t('imageUploader.uploadFromComputerUploadError', { ns: 'common' }),
t,
)
notify({ type: 'error', message: errorMessage })
const errorMessage = getImageUploadErrorMessage(error, t('imageUploader.uploadFromComputerUploadError', { ns: 'common' }), t)
toast.error(errorMessage)
setUploadProgress(-1)
},
}, false, WEB_APP_LOGO_UPLOAD_URL)
}
const handleApply = async () => {
await persistWorkspaceBrand({
remove_webapp_brand: webappBrandRemoved,
@ -80,25 +63,21 @@ const useWebAppBrand = () => {
setFileId('')
setImgKey(Date.now())
}
const handleRestore = async () => {
await persistWorkspaceBrand({
remove_webapp_brand: false,
replace_webapp_logo: '',
})
}
const handleSwitch = async (checked: boolean) => {
await persistWorkspaceBrand({
remove_webapp_brand: checked,
})
}
const handleCancel = () => {
setFileId('')
setUploadProgress(0)
}
return {
fileId,
imgKey,
@ -117,5 +96,4 @@ const useWebAppBrand = () => {
handleSwitch,
}
}
export default useWebAppBrand