"use client"; import type { ReactNode } from "react"; import * as React from "react"; import { AppSidebar } from "@/app/(main)/_components/sidebar/app-sidebar"; import { SidebarInset, SidebarProvider } from "@/components/ui/sidebar"; import { auth } from "@/lib/auth"; import { getClientCookie } from "@/lib/cookie.client"; import { SIDEBAR_COLLAPSIBLE_VALUES, SIDEBAR_VARIANT_VALUES, type SidebarCollapsible, type SidebarVariant, } from "@/lib/preferences/layout"; import { PREFERENCE_DEFAULTS } from "@/lib/preferences/preferences-config"; import { cn } from "@/lib/utils"; import { MainContent } from "./_components/main-content"; // Reads a layout-critical preference from the browser cookie (falls back to the // default during static-export prerender where document is unavailable). Kept // fully client-side so the app can be statically exported — no Server Actions / // next/headers. function readPref(key: string, allowed: readonly T[], fallback: T): T { if (typeof document === "undefined") return fallback; const value = getClientCookie(key); return value && (allowed as readonly string[]).includes(value) ? (value as T) : fallback; } export default function Layout({ children }: Readonly<{ children: ReactNode }>) { // Client-side auth gate — replaces the Next proxy/middleware that static export // disables. No token → bounce to /login; render nothing until confirmed so no // protected UI (or its API calls) flashes for a logged-out visitor. const [authed, setAuthed] = React.useState(false); React.useEffect(() => { if (auth.getToken()) { setAuthed(true); } else { // 客户端守卫认为未登录时,必须同时清掉 cookie:否则 proxy.ts 仅凭 // “cookie 存在”就把我们从 /login 又重定向回主界面,与本守卫来回弹跳 // 形成无限重定向 → 白屏(cookie 与 localStorage 不一致时触发)。 auth.clearToken(); window.location.href = "/login"; } }, []); const defaultOpen = typeof document === "undefined" ? true : getClientCookie("sidebar_state") !== "false"; const variant = readPref( "sidebar_variant", SIDEBAR_VARIANT_VALUES, PREFERENCE_DEFAULTS.sidebar_variant, ); const collapsible = readPref( "sidebar_collapsible", SIDEBAR_COLLAPSIBLE_VALUES, PREFERENCE_DEFAULTS.sidebar_collapsible, ); if (!authed) return null; return ( *]:mx-auto", "[html[data-content-layout=centered]_&>*]:w-full", "[html[data-content-layout=centered]_&>*]:max-w-screen-2xl", "peer-data-[variant=inset]:border", "[--dashboard-header-height:--spacing(12)]", "min-w-0 overflow-x-hidden", )} > {children} ); }