import { useAuth } from "@/hooks/useAuth"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarInset, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider, SidebarTrigger, useSidebar, } from "@/components/ui/sidebar"; import { useIsMobile } from "@/hooks/useMobile"; import { LayoutDashboard, LogOut, PanelLeft, Users, Heart, ShieldCheck, Loader2, Settings, Home } from "lucide-react"; import { CSSProperties, useCallback, useEffect, useRef, useState } from "react"; import { useLocation } from "wouter"; import { DashboardLayoutSkeleton } from './DashboardLayoutSkeleton'; import { Button } from "./ui/button"; import { toast } from "sonner"; import { getErrorCopy } from "@/lib/i18n/errorMessages"; const menuItems = [ { icon: Home, label: "返回首页", path: "/" }, { icon: LayoutDashboard, label: "个人中心", path: "/dashboard" }, { icon: Heart, label: "我的收藏", path: "/favorites" }, { icon: Settings, label: "账号设置", path: "/settings" }, ]; const adminMenuItems = [ { icon: ShieldCheck, label: "管理后台", path: "/admin" }, ]; const SIDEBAR_WIDTH_KEY = "sidebar-width"; const DEFAULT_WIDTH = 280; const MIN_WIDTH = 200; const MAX_WIDTH = 480; export default function DashboardLayout({ children, }: { children: React.ReactNode; }) { const [, navigate] = useLocation(); const [isLoggingOut, setIsLoggingOut] = useState(false); const [sidebarWidth, setSidebarWidth] = useState(() => { const saved = localStorage.getItem(SIDEBAR_WIDTH_KEY); return saved ? parseInt(saved, 10) : DEFAULT_WIDTH; }); const { loading, user, logout } = useAuth(); const handleLogout = useCallback(async () => { setIsLoggingOut(true); try { await logout(); toast.success("已退出登录"); navigate("/"); } catch (error: unknown) { const { title, description } = getErrorCopy(error, { context: "auth.logout" }); toast.error(title, { description }); } finally { setIsLoggingOut(false); } }, [logout, navigate]); useEffect(() => { localStorage.setItem(SIDEBAR_WIDTH_KEY, sidebarWidth.toString()); }, [sidebarWidth]); if (loading) { return } if (!user) { return (

Sign in to continue

Access to this dashboard requires authentication. Continue to launch the login flow.

); } return ( {children} ); } type DashboardLayoutContentProps = { children: React.ReactNode; setSidebarWidth: (width: number) => void; handleLogout: () => void; isLoggingOut: boolean; }; function DashboardLayoutContent({ children, setSidebarWidth, handleLogout, isLoggingOut, }: DashboardLayoutContentProps) { const { user } = useAuth(); const [location, setLocation] = useLocation(); const { state, toggleSidebar } = useSidebar(); const isCollapsed = state === "collapsed"; const [isResizing, setIsResizing] = useState(false); const sidebarRef = useRef(null); const activeMenuItem = menuItems.find(item => item.path === location); const isMobile = useIsMobile(); useEffect(() => { if (isCollapsed) { setIsResizing(false); } }, [isCollapsed]); useEffect(() => { const handleMouseMove = (e: MouseEvent) => { if (!isResizing) return; const sidebarLeft = sidebarRef.current?.getBoundingClientRect().left ?? 0; const newWidth = e.clientX - sidebarLeft; if (newWidth >= MIN_WIDTH && newWidth <= MAX_WIDTH) { setSidebarWidth(newWidth); } }; const handleMouseUp = () => { setIsResizing(false); }; if (isResizing) { document.addEventListener("mousemove", handleMouseMove); document.addEventListener("mouseup", handleMouseUp); document.body.style.cursor = "col-resize"; document.body.style.userSelect = "none"; } return () => { document.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseup", handleMouseUp); document.body.style.cursor = ""; document.body.style.userSelect = ""; }; }, [isResizing, setSidebarWidth]); return ( <>
{!isCollapsed ? (
Navigation
) : null}
{menuItems.map(item => { const isActive = location === item.path; return ( setLocation(item.path)} tooltip={item.label} className={`h-10 transition-all font-normal`} > {item.label} ); })} {user?.role === "admin" && ( <>
管理
{adminMenuItems.map(item => { const isActive = location === item.path; return ( setLocation(item.path)} tooltip={item.label} className={`h-10 transition-all font-normal`} > {item.label} ); })} )}
{isLoggingOut ? ( ) : ( )} {isLoggingOut ? "退出中..." : "退出登录"}
{ if (isCollapsed) return; setIsResizing(true); }} style={{ zIndex: 50 }} />
{isMobile && (
{activeMenuItem?.label ?? "Menu"}
)}
{children}
); }