diff --git a/apps/web/app/(dashboard)/loading.tsx b/apps/web/app/(dashboard)/loading.tsx new file mode 100644 index 00000000..8e96cdd5 --- /dev/null +++ b/apps/web/app/(dashboard)/loading.tsx @@ -0,0 +1,28 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +export default function DashboardLoading() { + return ( +
+ {/* Header skeleton */} +
+ + +
+ {/* Toolbar skeleton */} +
+ + +
+ {/* Content skeleton */} +
+ {Array.from({ length: 6 }).map((_, i) => ( +
+ + + +
+ ))} +
+
+ ); +} diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx index c1df5730..26c41c34 100644 --- a/apps/web/app/layout.tsx +++ b/apps/web/app/layout.tsx @@ -1,5 +1,4 @@ import type { Metadata, Viewport } from "next"; -import { cookies } from "next/headers"; import { Geist, Geist_Mono } from "next/font/google"; import { ThemeProvider } from "@/components/theme-provider"; import { Toaster } from "@/components/ui/sonner"; @@ -8,6 +7,7 @@ import { QueryProvider } from "@core/provider"; import { AuthInitializer } from "@/features/auth"; import { WSProvider } from "@/features/realtime"; import { ModalRegistry } from "@/features/modals"; +import { LocaleSync } from "@/components/locale-sync"; import "./globals.css"; const geist = Geist({ subsets: ["latin"], variable: "--font-sans" }); @@ -51,22 +51,19 @@ export const metadata: Metadata = { }, }; -export default async function RootLayout({ +export default function RootLayout({ children, }: { children: React.ReactNode; }) { - const cookieStore = await cookies(); - const locale = cookieStore.get("multica-locale")?.value; - const lang = locale === "zh" ? "zh" : "en"; - return ( + diff --git a/apps/web/components/locale-sync.tsx b/apps/web/components/locale-sync.tsx new file mode 100644 index 00000000..e223adb3 --- /dev/null +++ b/apps/web/components/locale-sync.tsx @@ -0,0 +1,20 @@ +"use client"; + +import { useEffect } from "react"; + +/** + * Reads the locale cookie on the client and updates . + * This avoids calling cookies() in the root Server Component layout, + * which would mark the entire app as dynamic and disable the Router Cache. + */ +export function LocaleSync() { + useEffect(() => { + const match = document.cookie.match(/(?:^|;\s*)multica-locale=(\w+)/); + const locale = match?.[1]; + if (locale === "zh") { + document.documentElement.lang = "zh"; + } + }, []); + + return null; +}