"use client"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { useAuth } from "./auth-provider"; import { useTheme } from "next-themes"; import type { LucideIcon } from "lucide-react"; import { LayoutDashboard, Users, Building2, Truck, FileText, ShoppingCart, LogOut, Sun, Moon, Menu, CheckCircle, Archive, Package, ClipboardList, Wrench, ChevronDown, MapPinned, Settings as SettingsIcon, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Separator } from "@/components/ui/separator"; import { ScrollArea } from "@/components/ui/scroll-area"; import { Sheet, SheetContent, SheetTrigger, SheetTitle } from "@/components/ui/sheet"; import { useEffect, useState, type ReactNode } from "react"; /* ============================================================================ * CATEGORY SYSTEM * * Sidebar nav is grouped into 4 categories, each with a fixed brand color * so users can scan by color. Colors are intentionally hard-coded — they * stay the same regardless of which theme is active, so the sidebar reads * consistently. * * dashboard → blue (brand) * management → blue (brand) * quotes → gold (brand) * operations → red (brand) * ========================================================================== */ type CategoryId = "dashboard" | "management" | "quotes" | "operations"; const CATEGORY_COLORS: Record = { dashboard: "#4C96D7", management: "#4C96D7", quotes: "#F2AF58", operations: "#E94E49", }; // Helper — produce 8-digit hex with alpha (browser-portable, no color-mix needed) function withAlpha(hex: string, alphaPercent: number): string { const a = Math.round((alphaPercent / 100) * 255) .toString(16) .padStart(2, "0"); return `${hex}${a}`; } type NavEntry = { href: string; label: string; icon: LucideIcon; category: CategoryId; }; const dashboardItems: NavEntry[] = [ { href: "/dashboard", label: "Dashboard", icon: LayoutDashboard, category: "dashboard" }, ]; const managementItems: NavEntry[] = [ { href: "/customers", label: "Customers", icon: Users, category: "management" }, { href: "/companies", label: "Companies", icon: Building2, category: "management" }, { href: "/suppliers", label: "Suppliers", icon: Truck, category: "management" }, { href: "/supplier-map", label: "Supplier Map", icon: MapPinned, category: "management" }, ]; const quoteItems: NavEntry[] = [ { href: "/quotes", label: "All Quotes", icon: FileText, category: "quotes" }, { href: "/quotes/approvals", label: "Pending Approvals", icon: CheckCircle, category: "quotes" }, { href: "/quotes/archived", label: "Archived", icon: Archive, category: "quotes" }, ]; const operationsItems: NavEntry[] = [ { href: "/orders", label: "Orders", icon: ShoppingCart, category: "operations" }, { href: "/collection-pool", label: "Collection Pool", icon: Package, category: "operations" }, { href: "/delivery-notes", label: "Delivery Notes", icon: ClipboardList, category: "operations" }, { href: "/repairs", label: "Repairs", icon: Wrench, category: "operations" }, ]; const CATEGORY_LABELS: Record = { dashboard: "Dashboard", management: "Management", quotes: "Quotes", operations: "Operations", }; /* ============================================================================ * NavItem — single nav link with category-colored icon * ========================================================================== */ function NavItem({ item, active, onClick, indent = false, }: { item: NavEntry; active: boolean; onClick?: () => void; indent?: boolean; }) { const color = CATEGORY_COLORS[item.category]; // Inactive: subtle category tint. Active: white-on-primary from theme. const inactiveStyle = active ? undefined : { color: withAlpha(color, 78) }; return ( {item.label} ); } /* ============================================================================ * NavGroup — accordion wrapper tinted with category color * ========================================================================== */ function NavGroup({ category, open, onToggle, headerActive, headerIcon, items, pathname, onLinkClick, }: { category: CategoryId; open: boolean; onToggle: () => void; headerActive: boolean; headerIcon: LucideIcon; items: NavEntry[]; pathname: string; onLinkClick?: () => void; }) { const color = CATEGORY_COLORS[category]; const HeaderIcon = headerIcon; return (
{open && (
{items.map((item) => { const isActive = item.href === "/quotes" ? pathname === "/quotes" : pathname === item.href || pathname.startsWith(item.href + "/"); return ( ); })}
)}
); } /* ============================================================================ * NavContent — the actual sidebar body * ========================================================================== */ function NavContent({ onLinkClick }: { onLinkClick?: () => void }) { const pathname = usePathname(); const { user, logout } = useAuth(); const { theme, setTheme } = useTheme(); const isManagementActive = managementItems.some( (item) => pathname === item.href || pathname.startsWith(item.href + "/") ); const isQuotesActive = pathname === "/quotes" || pathname.startsWith("/quotes/"); const isOperationsActive = operationsItems.some( (item) => pathname === item.href || pathname.startsWith(item.href + "/") ); const [managementOpen, setManagementOpen] = useState(isManagementActive); const [quotesOpen, setQuotesOpen] = useState(isQuotesActive); // Auto-open accordion when route inside it becomes active useEffect(() => { if (isManagementActive) setManagementOpen(true); if (isQuotesActive) setQuotesOpen(true); }, [isManagementActive, isQuotesActive]); const isDashboardActive = pathname === "/dashboard" || pathname.startsWith("/dashboard/"); return (
{/* Brand block */}

WFDASH

Westcott Fasteners

{/* Brand color stripe */}
{/* Main nav */} {/* Bottom block: settings (icon-only) + theme + avatar + logout */}
{/* Settings — small icon-only button bottom-left */} {/* Theme toggle */} {/* User */}
{user?.name?.charAt(0)?.toUpperCase() || user?.email?.charAt(0)?.toUpperCase() || "U"}

{user?.name || "User"}

{user?.email}

{/* Logout */}
); } /* ============================================================================ * OperationsList — flat items with a 4px red left gutter * * Operations is the only flat category. To make it visually consistent with * the accordion groups above, we wrap it in a thin red gutter that hints * at the category color without painting a full background. * ========================================================================== */ function OperationsList({ active, pathname, onLinkClick, }: { active: boolean; pathname: string; onLinkClick?: () => void; }): ReactNode { const color = CATEGORY_COLORS.operations; return (
{/* Tiny header label so the category is identifiable */}
Operations
{operationsItems.map((item) => { const isActive = pathname === item.href || pathname.startsWith(item.href + "/"); return ( ); })}
); } /* ============================================================================ * Sidebar — desktop + mobile (Sheet) wrappers * ========================================================================== */ export function Sidebar() { const [open, setOpen] = useState(false); return ( <>
} > Navigation setOpen(false)} />

WFDASH

); }