fix: default invite language to current locale

This commit is contained in:
yyh
2026-05-14 17:50:53 +08:00
parent 454637060d
commit 0836071203
2 changed files with 123 additions and 1 deletions

View File

@ -0,0 +1,113 @@
import type { MockedFunction } from 'vitest'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { useLocale } from '@/context/i18n'
import { useRouter, useSearchParams } from '@/next/navigation'
import { activateMember } from '@/service/common'
import { useInvitationCheck } from '@/service/use-common'
import { getBrowserTimezone } from '@/utils/timezone'
import InviteSettingsPage from '../page'
vi.mock('@tanstack/react-query', async () => {
const actual = await vi.importActual<typeof import('@tanstack/react-query')>('@tanstack/react-query')
return {
...actual,
useSuspenseQuery: vi.fn(() => ({
data: {
branding: {
enabled: true,
},
},
})),
}
})
vi.mock('@/context/i18n', () => ({
useLocale: vi.fn(),
}))
vi.mock('@/i18n-config', () => ({
setLocaleOnClient: vi.fn(() => Promise.resolve()),
}))
vi.mock('@/next/navigation', () => ({
useRouter: vi.fn(),
useSearchParams: vi.fn(),
}))
vi.mock('@/service/common', () => ({
activateMember: vi.fn(),
}))
vi.mock('@/service/use-common', () => ({
useInvitationCheck: vi.fn(),
}))
vi.mock('@/utils/timezone', () => ({
getBrowserTimezone: vi.fn(),
timezones: [
{ value: 'Asia/Shanghai', name: 'Asia/Shanghai' },
{ value: 'America/Los_Angeles', name: 'America/Los_Angeles' },
],
}))
vi.mock('../utils/post-login-redirect', () => ({
resolvePostLoginRedirect: vi.fn(() => null),
}))
const mockReplace = vi.fn()
const mockRefetch = vi.fn()
const mockUseLocale = useLocale as unknown as MockedFunction<typeof useLocale>
const mockUseRouter = useRouter as unknown as MockedFunction<typeof useRouter>
const mockUseSearchParams = useSearchParams as unknown as MockedFunction<typeof useSearchParams>
const mockActivateMember = activateMember as unknown as MockedFunction<typeof activateMember>
const mockUseInvitationCheck = useInvitationCheck as unknown as MockedFunction<typeof useInvitationCheck>
const mockGetBrowserTimezone = getBrowserTimezone as unknown as MockedFunction<typeof getBrowserTimezone>
describe('InviteSettingsPage', () => {
beforeEach(() => {
vi.clearAllMocks()
mockUseLocale.mockReturnValue('zh-Hans')
mockUseRouter.mockReturnValue({ replace: mockReplace } as unknown as ReturnType<typeof useRouter>)
mockUseSearchParams.mockReturnValue(
new URLSearchParams('invite_token=invite-token') as unknown as ReturnType<typeof useSearchParams>,
)
mockUseInvitationCheck.mockReturnValue({
data: {
is_valid: true,
data: {
workspace_name: 'Acme',
workspace_id: 'workspace-id',
email: 'invitee@example.com',
},
},
refetch: mockRefetch,
} as unknown as ReturnType<typeof useInvitationCheck>)
mockGetBrowserTimezone.mockReturnValue('Asia/Shanghai')
mockActivateMember.mockResolvedValue({ result: 'success' })
})
describe('Activation payload', () => {
it('should default language to the current UI locale', async () => {
render(<InviteSettingsPage />)
fireEvent.change(screen.getByLabelText('login.name'), {
target: { value: 'Invitee' },
})
fireEvent.click(screen.getByRole('button', { name: 'login.join Acme' }))
await waitFor(() => {
expect(mockActivateMember).toHaveBeenCalledWith({
url: '/activate',
body: {
token: 'invite-token',
name: 'Invitee',
interface_language: 'zh-Hans',
timezone: 'Asia/Shanghai',
},
})
})
})
})
})

View File

@ -11,6 +11,7 @@ import { useTranslation } from 'react-i18next'
import Input from '@/app/components/base/input'
import Loading from '@/app/components/base/loading'
import { LICENSE_LINK } from '@/constants/link'
import { useLocale } from '@/context/i18n'
import { setLocaleOnClient } from '@/i18n-config'
import { languages, LanguagesSupported } from '@/i18n-config/language'
import Link from '@/next/link'
@ -43,14 +44,22 @@ const TIMEZONE_OPTIONS: TimezoneSelectOption[] = timezones.map(item => ({
name: item.name,
}))
const getInitialLanguage = (locale: Locale): Locale => {
if (LANGUAGE_OPTIONS.some(item => item.value === locale))
return locale
return LanguagesSupported[0]!
}
export default function InviteSettingsPage() {
const { t } = useTranslation()
const { data: systemFeatures } = useSuspenseQuery(systemFeaturesQueryOptions())
const router = useRouter()
const searchParams = useSearchParams()
const token = decodeURIComponent(searchParams.get('invite_token') as string)
const locale = useLocale()
const [name, setName] = useState('')
const [language, setLanguage] = useState(LanguagesSupported[0])
const [language, setLanguage] = useState(() => getInitialLanguage(locale))
const [timezone, setTimezone] = useState(() => getBrowserTimezone() || 'America/Los_Angeles')
const selectedLanguage = LANGUAGE_OPTIONS.find(item => item.value === language)
const selectedTimezone = TIMEZONE_OPTIONS.find(item => item.value === timezone)