spb/autoscholar
Public
1"use client";2import Link from "next/link";3import { usePathname } from "next/navigation";4import { useEffect, useState } from "react";5import { IconClose, IconMenu } from "@/components/Icons";67/** Admin navigation: vertical list on desktop; on mobile a compact button opening a drawer. */8export function AdminNav({9 items,10 mobileOnly = false,11}: {12 items: Array<[string, string]>;13 mobileOnly?: boolean;14}) {15 const pathname = usePathname();16 const [open, setOpen] = useState(false);17 // biome-ignore lint/correctness/useExhaustiveDependencies: close on navigation18 useEffect(() => setOpen(false), [pathname]);19 useEffect(() => {20 if (!open) return;21 const onKey = (e: KeyboardEvent) => e.key === "Escape" && setOpen(false);22 document.addEventListener("keydown", onKey);23 const prev = document.body.style.overflow;24 document.body.style.overflow = "hidden";25 return () => {26 document.removeEventListener("keydown", onKey);27 document.body.style.overflow = prev;28 };29 }, [open]);30 const isCurrent = (href: string) => (href === "/admin" ? pathname === "/admin" : pathname.startsWith(href));31 const list = (32 <nav aria-label="Admin" className="flex flex-col gap-0.5 px-2 pb-2">33 {items.map(([href, label]) => (34 <Link35 key={href}36 href={href}37 aria-current={isCurrent(href) ? "page" : undefined}38 className="nav-link flex min-h-10 items-center text-sm md:min-h-9"39 >40 {label}41 </Link>42 ))}43 </nav>44 );45 if (!mobileOnly) return list;46 return (47 <div className="md:hidden">48 <button49 type="button"50 className="btn-icon"51 aria-label={open ? "Close admin menu" : "Open admin menu"}52 aria-expanded={open}53 aria-controls="admin-drawer"54 onClick={() => setOpen((v) => !v)}55 >56 {open ? <IconClose /> : <IconMenu />}57 </button>58 {open && (59 <>60 <button61 type="button"62 className="drawer-backdrop"63 aria-label="Close admin menu"64 onClick={() => setOpen(false)}65 />66 <div67 id="admin-drawer"68 role="dialog"69 aria-modal="true"70 aria-label="Admin navigation"71 className="drawer-panel"72 >73 <div className="flex items-center justify-between px-2">74 <span className="eyebrow">Admin</span>75 <button76 type="button"77 className="btn-icon"78 aria-label="Close admin menu"79 onClick={() => setOpen(false)}80 >81 <IconClose />82 </button>83 </div>84 <div className="mt-3">{list}</div>85 <div className="mt-4 px-2">86 <Link href="/" className="text-xs">87 ← Public site88 </Link>89 </div>90 </div>91 </>92 )}93 </div>94 );95}96