Spaces:
Runtime error
Runtime error
| "use client"; | |
| import { useEffect, useState } from "react"; | |
| import { Menu01Icon } from "@hugeicons/core-free-icons"; | |
| import { DashboardSidebar } from "@/components/dashboard/dashboard-sidebar"; | |
| import { AppLogo } from "@/components/shared/app-logo"; | |
| import { Icon } from "@/components/shared/icon"; | |
| import { Button } from "@/components/ui/button"; | |
| import { cn } from "@/lib/utils"; | |
| const SIDEBAR_STORAGE_KEY = "courtrix_sidebar_collapsed"; | |
| type DashboardShellProps = { | |
| user: { | |
| firstName: string; | |
| lastName: string; | |
| email: string; | |
| }; | |
| children: React.ReactNode; | |
| }; | |
| export function DashboardShell({ user, children }: DashboardShellProps) { | |
| const [isDesktopCollapsed, setIsDesktopCollapsed] = useState(() => { | |
| if (typeof window === "undefined") { | |
| return false; | |
| } | |
| return window.localStorage.getItem(SIDEBAR_STORAGE_KEY) === "true"; | |
| }); | |
| const [isMobile, setIsMobile] = useState(() => { | |
| if (typeof window === "undefined") { | |
| return false; | |
| } | |
| return window.matchMedia("(max-width: 1023px)").matches; | |
| }); | |
| const [isMobileOpen, setIsMobileOpen] = useState(false); | |
| useEffect(() => { | |
| const mediaQuery = window.matchMedia("(max-width: 1023px)"); | |
| const handleViewportChange = () => { | |
| const mobile = mediaQuery.matches; | |
| setIsMobile(mobile); | |
| if (mobile) { | |
| setIsMobileOpen(false); | |
| } | |
| }; | |
| mediaQuery.addEventListener("change", handleViewportChange); | |
| return () => mediaQuery.removeEventListener("change", handleViewportChange); | |
| }, []); | |
| function toggleDesktopCollapse() { | |
| setIsDesktopCollapsed((current) => { | |
| const next = !current; | |
| window.localStorage.setItem(SIDEBAR_STORAGE_KEY, String(next)); | |
| return next; | |
| }); | |
| } | |
| return ( | |
| <div className="flex min-h-screen bg-[var(--background)]" dir="rtl"> | |
| {/* Sidebar - placed first in DOM means it will be on the right in RTL */} | |
| <aside | |
| className={cn( | |
| "hidden lg:block sticky top-0 h-screen shrink-0 border-l border-[var(--border)] transition-[width] duration-300", | |
| isDesktopCollapsed ? "w-[92px]" : "w-[290px]" | |
| )} | |
| > | |
| <DashboardSidebar | |
| collapsed={isDesktopCollapsed} | |
| onToggleCollapse={toggleDesktopCollapse} | |
| user={user} | |
| /> | |
| </aside> | |
| <div className="flex-1 min-w-0 flex flex-col h-screen overflow-y-auto"> | |
| <div className="p-4 lg:p-8"> | |
| <div className="mb-6 flex items-center justify-between gap-3 lg:hidden"> | |
| <Button size="icon" variant="outline" onClick={() => setIsMobileOpen(true)}> | |
| <Icon icon={Menu01Icon} size={18} /> | |
| </Button> | |
| <AppLogo /> | |
| </div> | |
| <main className="mx-auto max-w-[1200px] space-y-5">{children}</main> | |
| </div> | |
| </div> | |
| {isMobile ? ( | |
| <div | |
| className={cn( | |
| "fixed inset-0 z-40 bg-[rgba(20,13,7,0.5)] backdrop-blur-[2px] transition-opacity lg:hidden", | |
| isMobileOpen | |
| ? "pointer-events-auto opacity-100" | |
| : "pointer-events-none opacity-0", | |
| )} | |
| onClick={() => setIsMobileOpen(false)} | |
| > | |
| <div | |
| className={cn( | |
| "absolute inset-y-0 right-0 w-[290px] max-w-full transform p-3 transition-transform", | |
| isMobileOpen ? "translate-x-0" : "translate-x-full", | |
| )} | |
| onClick={(event) => event.stopPropagation()} | |
| > | |
| <DashboardSidebar | |
| mobile | |
| onCloseMobile={() => setIsMobileOpen(false)} | |
| user={user} | |
| /> | |
| </div> | |
| </div> | |
| ) : null} | |
| </div> | |
| ); | |
| } | |