"use client"; import { useEffect, useState } from "react"; import Link from "next/link"; import { usePathname, useRouter } from "next/navigation"; import { Menu } from "lucide-react"; import { api, staff, type Me, type StaffMe } from "@/lib/api"; import { RequirePermission, StaffProvider } from "@/components/staff-context"; import { Button } from "@/components/ui/button"; import { ConfirmProvider } from "@/components/confirm"; import { NAV, Sidebar } from "./sidebar"; export default function DashboardLayout({ children, }: { children: React.ReactNode; }) { const router = useRouter(); const pathname = usePathname(); const [me, setMe] = useState(null); const [staffMe, setStaffMe] = useState(null); const [checked, setChecked] = useState(false); // Navigation drawer, below lg only. const [navOpen, setNavOpen] = useState(false); // Staff gate: only an account holding an admin role, and not blocked from // the panel, may see the dashboard. /admin/me answers both at once — it 403s // otherwise. The BFF enforces this on every /admin/* call regardless, so // this is UX, not the security boundary. useEffect(() => { let cancelled = false; Promise.all([api.me(), staff.me()]) .then(([u, s]) => { if (cancelled) return; if (!u || !s) { router.replace("/login"); return; } setMe(u); setStaffMe(s); setChecked(true); }) .catch(() => { if (!cancelled) router.replace("/login"); }); return () => { cancelled = true; }; }, [router]); // A role without stats:read has no Overview — send them to the first // section they can use instead of a page of permission errors. useEffect(() => { if ( !staffMe || pathname !== "/" || staffMe.permissions.includes("stats:read") ) return; const first = NAV.find((n) => staffMe.permissions.includes(n.perm)); if (first) router.replace(first.href); }, [staffMe, pathname, router]); async function signOut() { await api.logout().catch(() => {}); router.replace("/login"); } if (!checked || !me || !staffMe) { return (
Loading…
); } const section = NAV.find( (n) => n.href !== "/" && (pathname === n.href || pathname.startsWith(n.href + "/")), ) ?? (pathname === "/" ? NAV[0] : undefined); return (
setNavOpen(false)} onSignOut={signOut} />
{/* Below lg: a top bar with the menu button and where you are. */}

{section?.label ?? "Admin"}

{/* The section's own permission, so a URL typed straight in explains itself instead of failing call by call. */} {section ? ( {children} ) : ( children )}
); }