File size: 4,953 Bytes
bcd59c6 c2c654e bcd59c6 9bf5220 bcd59c6 c2bc4c7 bcd59c6 9bf5220 bcd59c6 c2bc4c7 bcd59c6 c2bc4c7 9bf5220 bcd59c6 c2bc4c7 bcd59c6 9bf5220 c2bc4c7 9bf5220 bcd59c6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 | "use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import {
LayoutDashboard,
ClipboardList,
CheckSquare,
HardHat,
Building2,
Map,
ListTodo,
Settings,
LogOut,
Menu,
} from "lucide-react";
import { clsx, type ClassValue } from "clsx";
import { twMerge } from "tailwind-merge";
function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
interface SidebarProps {
role: "admin" | "worker";
mobileOpen: boolean;
setMobileOpen: (open: boolean) => void;
desktopOpen: boolean;
onLogout: () => void;
}
export default function DashboardSidebar({
role,
mobileOpen,
setMobileOpen,
desktopOpen,
onLogout,
}: SidebarProps) {
const pathname = usePathname();
const adminLinks = [
{ href: "/admin", label: "Overview", icon: LayoutDashboard },
{ href: "/admin/issues", label: "Issues", icon: ClipboardList },
{ href: "/admin/review", label: "Review Queue", icon: CheckSquare },
{ href: "/admin/workers", label: "Workforce", icon: HardHat },
{ href: "/admin/departments", label: "Departments", icon: Building2 },
{ href: "/admin/heatmap", label: "Heatmap", icon: Map },
];
const workerLinks = [{ href: "/worker", label: "My Tasks", icon: ListTodo }];
const links = role === "admin" ? adminLinks : workerLinks;
return (
<>
{mobileOpen && (
<div
className="fixed inset-0 z-40 bg-slate-900/20 backdrop-blur-sm lg:hidden"
onClick={() => setMobileOpen(false)}
/>
)}
<aside
className={cn(
"fixed inset-y-0 left-0 z-50 bg-white/80 backdrop-blur-xl border-r border-slate-200/60 transition-all duration-300 ease-in-out shadow-urban-lg",
"lg:relative lg:translate-x-0 lg:shadow-none",
mobileOpen ? "translate-x-0" : "-translate-x-full",
desktopOpen ? "lg:w-72" : "lg:w-0 lg:overflow-hidden lg:border-r-0",
)}
>
<div className="flex h-16 items-center px-6 border-b border-slate-100/50">
<div className="flex items-center gap-2">
<div className="h-8 w-8 rounded-lg bg-gradient-to-br from-urban-primary to-emerald-600 flex items-center justify-center shadow-lg shadow-emerald-500/20">
<span className="text-white font-bold font-mono">C</span>
</div>
<span className="text-xl font-bold tracking-tight text-slate-900">
CityTracker
</span>
</div>
<span className="ml-auto rounded-full bg-urban-primary/10 px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider text-urban-primary ring-1 ring-urban-primary/20">
{role}
</span>
</div>
<div className="flex flex-col justify-between h-[calc(100vh-4rem)] p-4">
<nav className="space-y-1">
{links.map((link) => {
const Icon = link.icon;
const isActive = pathname === link.href;
return (
<Link
key={link.href}
href={link.href}
onClick={() => setMobileOpen(false)}
className={cn(
"flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium transition-all duration-200 group relative overflow-hidden",
isActive
? "bg-urban-primary/10 text-urban-primary shadow-sm ring-1 ring-urban-primary/20"
: "text-slate-500 hover:bg-slate-50 hover:text-slate-900",
)}
>
{isActive && (
<div className="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-6 bg-urban-primary rounded-r-full" />
)}
<Icon
className={cn(
"h-5 w-5 transition-transform group-hover:scale-110",
isActive
? "text-urban-primary"
: "text-slate-400 group-hover:text-slate-600",
)}
/>
<span className={isActive ? "ml-1.5" : ""}>{link.label}</span>
</Link>
);
})}
</nav>
<div className="border-t border-slate-100 pt-4 space-y-1">
<button className="flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium text-slate-500 hover:bg-slate-50 hover:text-slate-900 transition-colors">
<Settings className="h-5 w-5 text-slate-400" />
Settings
</button>
<button
onClick={onLogout}
className="flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium text-red-600 hover:bg-red-50 hover:text-red-700 transition-colors"
>
<LogOut className="h-5 w-5" />
Sign Out
</button>
</div>
</div>
</aside>
</>
);
}
|