"use client"; import { useState, useEffect } from "react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { ChevronDown, Menu, X } from "lucide-react"; import { cn } from "@/lib/utils"; export type NavLeaf = { href: string; label: string }; export type NavItem = | { kind: "link"; href: string; label: string; icon: React.ReactNode } | { kind: "group"; label: string; icon: React.ReactNode; children: NavLeaf[] }; // A group is active when the current path is one of its children, or sits under one // of them. A child of "/" only matches exactly — otherwise every route would count as // "under" the dashboard. function isGroupActive(item: Extract, pathname: string): boolean { return item.children.some((c) => c.href === "/" ? pathname === "/" : pathname === c.href || pathname.startsWith(`${c.href}/`), ); } export function Sidebar({ items }: { items: NavItem[] }) { const pathname = usePathname(); const [mobileOpen, setMobileOpen] = useState(false); // Close drawer on navigation useEffect(() => { setMobileOpen(false); }, [pathname]); // Lock body scroll when drawer is open useEffect(() => { document.body.style.overflow = mobileOpen ? "hidden" : ""; return () => { document.body.style.overflow = ""; }; }, [mobileOpen]); const renderNav = (onNavigate?: () => void) => ( ); return ( <> {/* Desktop: persistent sidebar (hidden on mobile) */} {/* Mobile: hamburger in the top bar triggers a drawer */} {/* The drawer must out-rank Leaflet. Leaflet's own `.leaflet-top` / `.leaflet-bottom` control panes are `z-index: 1000` and its map panes run 400–700, all in the root stacking context — so the old z-40/z-50 pair lost and the Plant Map painted straight over the open menu. Keep these values above 1000, and above the header's z-[1100]. */} {mobileOpen && (
setMobileOpen(false)} /> )} ); } function LeafLink({ href, label, icon, active, onClick, }: { href: string; label: string; icon: React.ReactNode; active: boolean; onClick?: () => void; }) { return ( {icon} {label} ); } function Group({ item, active }: { item: Extract; active: boolean }) { const [open, setOpen] = useState(active); const pathname = usePathname(); return (
{open && (
{item.children.map((child) => { const childActive = pathname === child.href; return ( {child.label} ); })}
)}
); }