feat: initial
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useAuth } from "@/lib/auth";
|
||||
import { Sidebar } from "@/components/Sidebar";
|
||||
import { Spinner, Button } from "@/components/ui";
|
||||
import { LogoutIcon, MenuIcon, CloseIcon } from "@/components/icons";
|
||||
|
||||
export default function DashboardLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const { token, ready, logout } = useAuth();
|
||||
const router = useRouter();
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
|
||||
// Client-side route protection (static export has no server to gate on).
|
||||
useEffect(() => {
|
||||
if (ready && !token) router.replace("/login");
|
||||
}, [ready, token, router]);
|
||||
|
||||
if (!ready || !token) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center">
|
||||
<Spinner className="h-10 w-10" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function onLogout() {
|
||||
logout();
|
||||
router.replace("/login");
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex min-h-screen bg-background">
|
||||
{/* Desktop sidebar */}
|
||||
<aside className="sticky top-0 hidden h-screen w-64 shrink-0 border-l border-border bg-surface lg:block">
|
||||
<Sidebar />
|
||||
</aside>
|
||||
|
||||
{/* Mobile drawer */}
|
||||
{mobileOpen && (
|
||||
<div className="fixed inset-0 z-40 lg:hidden">
|
||||
<div
|
||||
className="absolute inset-0 bg-black/40"
|
||||
onClick={() => setMobileOpen(false)}
|
||||
/>
|
||||
<aside className="absolute right-0 top-0 h-full w-64 bg-surface shadow-xl">
|
||||
<Sidebar onNavigate={() => setMobileOpen(false)} />
|
||||
</aside>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="flex min-w-0 flex-1 flex-col">
|
||||
<header className="sticky top-0 z-30 flex items-center justify-between border-b border-border bg-surface/80 px-4 py-3 backdrop-blur">
|
||||
<button
|
||||
className="rounded-lg p-2 text-muted hover:bg-surface-muted lg:hidden"
|
||||
onClick={() => setMobileOpen((v) => !v)}
|
||||
aria-label="منو"
|
||||
>
|
||||
{mobileOpen ? <CloseIcon /> : <MenuIcon />}
|
||||
</button>
|
||||
<div className="flex-1" />
|
||||
<Button variant="ghost" icon={<LogoutIcon className="h-4 w-4" />} onClick={onLogout}>
|
||||
خروج
|
||||
</Button>
|
||||
</header>
|
||||
|
||||
<main className="flex-1 p-4 sm:p-6">{children}</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user