refactor: remove LocaleSwitcher component and update locale handling
- Removed LocaleSwitcher from NavUser, SiteHeader, and WorkbenchHeader components. - Updated tests to reflect the removal of LocaleSwitcher. - Changed default locale from "en-US" to "zh-CN" in i18n configuration. - Refactored locale resolution logic to read configured locale without relying on browser language detection. - Updated AgentDesk SDK to support language configuration from public API. - Cleaned up unused auth options fetching in the auth API.
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
import Image from "next/image"
|
||||
import Link from "next/link"
|
||||
|
||||
import { LocaleSwitcher } from "@/components/locale-switcher"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { useAppLocale, useI18n } from "@/i18n/provider"
|
||||
import enUSMessages from "@/messages/en-US.json"
|
||||
@@ -39,7 +38,7 @@ export function LegalDocumentPage({ type }: { type: LegalPageType }) {
|
||||
return (
|
||||
<main className="min-h-svh bg-muted px-6 py-8 md:px-10">
|
||||
<div className="mx-auto flex w-full max-w-4xl flex-col gap-6">
|
||||
<header className="flex items-center justify-between gap-4">
|
||||
<header className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2 font-medium">
|
||||
<Image
|
||||
src="/images/logo.svg"
|
||||
@@ -51,7 +50,6 @@ export function LegalDocumentPage({ type }: { type: LegalPageType }) {
|
||||
/>
|
||||
<span>{t("app.brand")}</span>
|
||||
</div>
|
||||
<LocaleSwitcher />
|
||||
</header>
|
||||
|
||||
<Card className="bg-card/95">
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
"use client"
|
||||
|
||||
import { LanguagesIcon } from "lucide-react"
|
||||
|
||||
import { useAppLocale, useI18n } from "@/i18n/provider"
|
||||
import { SUPPORTED_LOCALES, type AppLocale } from "@/i18n/config"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuRadioGroup,
|
||||
DropdownMenuRadioItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu"
|
||||
|
||||
export function LocaleSwitcher() {
|
||||
const t = useI18n()
|
||||
const { locale, setLocale } = useAppLocale()
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={<Button variant="outline" size="sm" />}
|
||||
aria-label={t("common.language")}
|
||||
>
|
||||
<LanguagesIcon />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-40 min-w-40">
|
||||
<DropdownMenuRadioGroup
|
||||
value={locale}
|
||||
onValueChange={(value) => setLocale(value as AppLocale)}
|
||||
>
|
||||
{SUPPORTED_LOCALES.map((option) => (
|
||||
<DropdownMenuRadioItem key={option} value={option}>
|
||||
{t(`locale.${option}`)}
|
||||
</DropdownMenuRadioItem>
|
||||
))}
|
||||
</DropdownMenuRadioGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
)
|
||||
}
|
||||
@@ -7,7 +7,8 @@ import { startTransition, useEffect, useState } from "react"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import { useAuth } from "@/components/auth-provider"
|
||||
import { fetchAuthOptions, loginWithPassword, type AuthOptions } from "@/lib/api/auth"
|
||||
import { loginWithPassword } from "@/lib/api/auth"
|
||||
import { fetchPublicConfig, type PublicConfig } from "@/lib/api/config"
|
||||
import { useI18n } from "@/i18n/provider"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { Button } from "@/components/ui/button"
|
||||
@@ -40,15 +41,15 @@ export function LoginForm({
|
||||
const { session } = useAuth()
|
||||
const [isPending, setIsPending] = useState(false)
|
||||
const [isWxWorkEnv, setIsWxWorkEnv] = useState(false)
|
||||
const [authOptions, setAuthOptions] = useState<AuthOptions | null>(null)
|
||||
const [authOptionsError, setAuthOptionsError] = useState<string | null>(null)
|
||||
const [publicConfig, setPublicConfig] = useState<PublicConfig | null>(null)
|
||||
const [publicConfigError, setPublicConfigError] = useState<string | null>(null)
|
||||
const nextPath = searchParams.get("next")
|
||||
const wxworkError = searchParams.get("wxworkError")
|
||||
const oidcError = searchParams.get("oidcError")
|
||||
const redirectPath =
|
||||
nextPath && nextPath.startsWith("/") ? nextPath : "/dashboard"
|
||||
const enabledProviderCount =
|
||||
Number(authOptions?.wxworkEnabled) + Number(authOptions?.oidcEnabled)
|
||||
Number(publicConfig?.wxworkEnabled) + Number(publicConfig?.oidcEnabled)
|
||||
|
||||
useEffect(() => {
|
||||
if (session) {
|
||||
@@ -75,17 +76,17 @@ export function LoginForm({
|
||||
useEffect(() => {
|
||||
let cancelled = false
|
||||
|
||||
void fetchAuthOptions()
|
||||
void fetchPublicConfig()
|
||||
.then((options) => {
|
||||
if (!cancelled) {
|
||||
setAuthOptions(options)
|
||||
setAuthOptionsError(null)
|
||||
setPublicConfig(options)
|
||||
setPublicConfigError(null)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
if (!cancelled) {
|
||||
setAuthOptions(null)
|
||||
setAuthOptionsError(error instanceof Error ? error.message : "")
|
||||
setPublicConfig(null)
|
||||
setPublicConfigError(error instanceof Error ? error.message : "")
|
||||
}
|
||||
})
|
||||
|
||||
@@ -115,7 +116,7 @@ export function LoginForm({
|
||||
}
|
||||
}
|
||||
|
||||
if (authOptionsError) {
|
||||
if (publicConfigError) {
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<Card className="overflow-hidden p-0">
|
||||
@@ -124,7 +125,7 @@ export function LoginForm({
|
||||
<div className="space-y-1">
|
||||
<h1 className="text-lg font-semibold">{t("auth.optionsLoadFailed")}</h1>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{authOptionsError || t("api.requestFailed")}
|
||||
{publicConfigError || t("api.requestFailed")}
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -133,7 +134,7 @@ export function LoginForm({
|
||||
)
|
||||
}
|
||||
|
||||
if (!authOptions) {
|
||||
if (!publicConfig) {
|
||||
return (
|
||||
<div className={cn("flex flex-col gap-6", className)} {...props}>
|
||||
<Card className="overflow-hidden p-0">
|
||||
@@ -206,7 +207,7 @@ export function LoginForm({
|
||||
enabledProviderCount === 1 ? "grid-cols-1" : "grid-cols-2"
|
||||
)}
|
||||
>
|
||||
{authOptions.wxworkEnabled ? (
|
||||
{publicConfig.wxworkEnabled ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
@@ -228,7 +229,7 @@ export function LoginForm({
|
||||
<span>{t("auth.wxworkSignIn")}</span>
|
||||
</Button>
|
||||
) : null}
|
||||
{authOptions.oidcEnabled ? (
|
||||
{publicConfig.oidcEnabled ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
|
||||
@@ -6,7 +6,6 @@ import { useState } from "react"
|
||||
import { useAuth } from "@/components/auth-provider"
|
||||
import { useI18n } from "@/i18n/provider"
|
||||
import { ChangePasswordDialog } from "@/components/change-password-dialog"
|
||||
import { LocaleSwitcher } from "@/components/locale-switcher"
|
||||
import { useNotifications } from "@/components/notification-provider"
|
||||
import { PaletteToggle } from "@/components/palette-toggle"
|
||||
import { ThemeToggle } from "@/components/theme-toggle"
|
||||
@@ -104,7 +103,6 @@ export function NavUser({
|
||||
{t("nav.preferences")}
|
||||
</DropdownMenuLabel>
|
||||
<div className="flex items-center gap-2 px-2 pb-2">
|
||||
<LocaleSwitcher />
|
||||
<PaletteToggle />
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
|
||||
@@ -5,10 +5,10 @@ import { describe, it } from "node:test";
|
||||
const source = await readFile(new URL("./site-header.tsx", import.meta.url), "utf8");
|
||||
|
||||
describe("site header preferences", () => {
|
||||
it("renders locale, palette, and theme controls in the dashboard header", () => {
|
||||
assert.match(source, /import \{ LocaleSwitcher \} from "@\/components\/locale-switcher"/);
|
||||
it("renders palette and theme controls without a locale switcher", () => {
|
||||
assert.doesNotMatch(source, /LocaleSwitcher/);
|
||||
assert.match(source, /import \{ PaletteToggle \} from "@\/components\/palette-toggle"/);
|
||||
assert.match(source, /import \{ ThemeToggle \} from "@\/components\/theme-toggle"/);
|
||||
assert.match(source, /<LocaleSwitcher \/>[\s\S]*<PaletteToggle \/>[\s\S]*<ThemeToggle \/>/);
|
||||
assert.match(source, /<PaletteToggle \/>[\s\S]*<ThemeToggle \/>/);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import { useEffect, useRef } from "react"
|
||||
import { usePathname } from "next/navigation"
|
||||
|
||||
import { LocaleSwitcher } from "@/components/locale-switcher"
|
||||
import { PaletteToggle } from "@/components/palette-toggle"
|
||||
import { RealtimeConnectionStatus } from "@/components/realtime-connection-status"
|
||||
import { ThemeToggle } from "@/components/theme-toggle"
|
||||
@@ -78,7 +77,6 @@ export function SiteHeader() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center justify-end gap-2">
|
||||
<LocaleSwitcher />
|
||||
<PaletteToggle />
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,6 @@ import { useRouter } from "next/navigation"
|
||||
import { useState } from "react"
|
||||
|
||||
import { ChangePasswordDialog } from "@/components/change-password-dialog"
|
||||
import { LocaleSwitcher } from "@/components/locale-switcher"
|
||||
import { PaletteToggle } from "@/components/palette-toggle"
|
||||
import { RealtimeConnectionStatus } from "@/components/realtime-connection-status"
|
||||
import { ThemeToggle } from "@/components/theme-toggle"
|
||||
@@ -36,7 +35,6 @@ export function WorkbenchHeader() {
|
||||
<div className="hidden sm:block">
|
||||
<RealtimeConnectionStatus status={realtimeStatus} compact />
|
||||
</div>
|
||||
<LocaleSwitcher />
|
||||
<PaletteToggle />
|
||||
<ThemeToggle />
|
||||
<WorkbenchUserMenu />
|
||||
|
||||
Reference in New Issue
Block a user