Spaces:
Running
Running
| "use client"; | |
| import { useState } from "react"; | |
| import { motion, AnimatePresence } from "framer-motion"; | |
| import { usePathname } from "next/navigation"; | |
| import { Sidebar } from "./Sidebar"; | |
| import { Navbar } from "./Navbar"; | |
| import { Menu } from "lucide-react"; | |
| import { useThemeStore } from "@/lib/stores/themeStore"; | |
| // βββ Page transition wrapper ββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| function PageTransition({ children }: { children: React.ReactNode }) { | |
| const pathname = usePathname(); | |
| return ( | |
| <AnimatePresence mode="wait"> | |
| <motion.div | |
| key={pathname} | |
| initial={{ opacity: 0, y: 8 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| exit={{ opacity: 0, y: -8 }} | |
| transition={{ duration: 0.25, ease: "easeOut" }} | |
| className="h-full" | |
| > | |
| {children} | |
| </motion.div> | |
| </AnimatePresence> | |
| ); | |
| } | |
| export function DashboardLayout({ children }: { children: React.ReactNode }) { | |
| const [mobileOpen, setMobileOpen] = useState(false); | |
| const { theme } = useThemeStore(); | |
| const isLight = theme === "light"; | |
| return ( | |
| <div | |
| className="flex h-screen overflow-hidden transition-colors duration-300" | |
| style={{ background: "var(--bg)", color: "var(--fg)" }} | |
| > | |
| {/* Ambient background gradients */} | |
| <div className="fixed inset-0 z-0 overflow-hidden pointer-events-none"> | |
| <div | |
| className="absolute top-[-15%] left-[-5%] w-[45%] h-[45%] rounded-full blur-[140px] transition-opacity duration-300" | |
| style={{ background: "var(--ambient-1)" }} | |
| /> | |
| <div | |
| className="absolute bottom-[-15%] right-[-5%] w-[45%] h-[45%] rounded-full blur-[140px] transition-opacity duration-300" | |
| style={{ background: "var(--ambient-2)" }} | |
| /> | |
| <div | |
| className="absolute top-[40%] left-[40%] w-[30%] h-[30%] rounded-full blur-[120px] transition-opacity duration-300" | |
| style={{ background: "var(--ambient-3)" }} | |
| /> | |
| {/* Noise texture β only visible in dark mode */} | |
| {!isLight && ( | |
| <div | |
| className="absolute inset-0 opacity-[0.015]" | |
| style={{ | |
| backgroundImage: `url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E")`, | |
| backgroundRepeat: "repeat", | |
| backgroundSize: "128px 128px", | |
| }} | |
| /> | |
| )} | |
| </div> | |
| {/* ββ Desktop Sidebar βββββββββββββββββββββββββββββββββββββββββββββββββββ */} | |
| <div className="z-10 hidden lg:flex h-full flex-shrink-0"> | |
| <Sidebar /> | |
| </div> | |
| {/* ββ Mobile Sidebar Overlay βββββββββββββββββββββββββββββββββββββββββββββ */} | |
| <AnimatePresence> | |
| {mobileOpen && ( | |
| <> | |
| <motion.div | |
| initial={{ opacity: 0 }} | |
| animate={{ opacity: 1 }} | |
| exit={{ opacity: 0 }} | |
| onClick={() => setMobileOpen(false)} | |
| className={`fixed inset-0 z-30 backdrop-blur-sm lg:hidden ${isLight ? "bg-slate-900/30" : "bg-black/60"}`} | |
| /> | |
| <motion.div | |
| initial={{ x: "-100%" }} | |
| animate={{ x: 0 }} | |
| exit={{ x: "-100%" }} | |
| transition={{ type: "spring", damping: 30, stiffness: 300 }} | |
| className="fixed left-0 top-0 z-40 h-full lg:hidden" | |
| > | |
| <Sidebar /> | |
| </motion.div> | |
| </> | |
| )} | |
| </AnimatePresence> | |
| {/* ββ Main content βββββββββββββββββββββββββββββββββββββββββββββββββββββββ */} | |
| <div className="z-10 flex flex-1 flex-col overflow-hidden min-w-0"> | |
| {/* Mobile header with hamburger */} | |
| <div | |
| className="flex items-center lg:hidden border-b backdrop-blur-xl px-4 py-3" | |
| style={{ background: "var(--navbar-bg)", borderColor: "var(--border)" }} | |
| > | |
| <button | |
| onClick={() => setMobileOpen(true)} | |
| className={`rounded-xl p-2 mr-3 transition-colors ${ | |
| isLight | |
| ? "bg-slate-100 text-slate-500 hover:text-slate-800" | |
| : "bg-white/5 text-zinc-400 hover:text-white" | |
| }`} | |
| > | |
| <Menu className="h-5 w-5" /> | |
| </button> | |
| <span className="text-base font-bold gradient-text">BankBot</span> | |
| </div> | |
| <Navbar /> | |
| <main className="flex-1 overflow-y-auto p-4 sm:p-6 lg:p-8"> | |
| <PageTransition>{children}</PageTransition> | |
| </main> | |
| </div> | |
| </div> | |
| ); | |
| } | |