File size: 5,393 Bytes
e1ef9fc | 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 | 'use client';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { cn } from '@/lib/utils';
import {
LayoutDashboard,
UtensilsCrossed,
QrCode,
ShoppingBag,
BarChart3,
CreditCard,
Settings,
Store,
ChevronLeft,
Menu,
LogOut,
Sparkles,
} from 'lucide-react';
import { useState } from 'react';
const navigation = [
{ name: 'Overview', href: '/dashboard/overview', icon: LayoutDashboard },
{ name: 'Menu Builder', href: '/dashboard/menu-builder', icon: UtensilsCrossed },
{ name: 'QR Codes', href: '/dashboard/qr-manager', icon: QrCode },
{ name: 'Orders', href: '/dashboard/orders', icon: ShoppingBag },
{ name: 'Analytics', href: '/dashboard/analytics', icon: BarChart3 },
{ name: 'Billing', href: '/dashboard/billing', icon: CreditCard },
{ name: 'Restaurant', href: '/dashboard/restaurant-setup', icon: Store },
{ name: 'Settings', href: '/dashboard/settings', icon: Settings },
];
export function DashboardSidebar() {
const pathname = usePathname();
const [collapsed, setCollapsed] = useState(false);
const [mobileOpen, setMobileOpen] = useState(false);
return (
<>
{/* Mobile menu button */}
<button
onClick={() => setMobileOpen(true)}
className="fixed left-4 top-4 z-50 rounded-xl border border-zinc-200 bg-white p-2.5 shadow-sm lg:hidden dark:border-zinc-700 dark:bg-zinc-900"
>
<Menu className="h-5 w-5 text-zinc-600" />
</button>
{/* Mobile overlay */}
{mobileOpen && (
<div
className="fixed inset-0 z-40 bg-black/30 backdrop-blur-sm lg:hidden"
onClick={() => setMobileOpen(false)}
/>
)}
{/* Sidebar */}
<aside
className={cn(
'fixed inset-y-0 left-0 z-50 flex flex-col border-r border-zinc-200/60 bg-white transition-all duration-300 dark:border-zinc-800 dark:bg-zinc-950',
collapsed ? 'w-[72px]' : 'w-[260px]',
mobileOpen ? 'translate-x-0' : '-translate-x-full lg:translate-x-0'
)}
>
{/* Logo */}
<div className={cn('flex h-16 items-center border-b border-zinc-200/60 px-4 dark:border-zinc-800', collapsed && 'justify-center')}>
<Link href="/dashboard/overview" className="flex items-center gap-2.5">
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-emerald-500 to-cyan-500 shadow-sm">
<Sparkles className="h-4 w-4 text-white" />
</div>
{!collapsed && (
<span className="text-lg font-bold tracking-tight text-zinc-900 dark:text-white">
Scan<span className="text-emerald-600">Menu</span>
</span>
)}
</Link>
</div>
{/* Navigation */}
<nav className="flex-1 space-y-1 overflow-y-auto px-3 py-4">
{navigation.map((item) => {
const isActive = pathname === item.href || pathname?.startsWith(item.href + '/');
return (
<Link
key={item.name}
href={item.href}
onClick={() => setMobileOpen(false)}
className={cn(
'group flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium transition-all duration-150',
isActive
? 'bg-zinc-900 text-white shadow-sm dark:bg-white dark:text-zinc-900'
: 'text-zinc-600 hover:bg-zinc-100 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-white',
collapsed && 'justify-center px-2'
)}
>
<item.icon className={cn('h-[18px] w-[18px] shrink-0', isActive ? 'text-current' : 'text-zinc-400 group-hover:text-zinc-600 dark:group-hover:text-zinc-300')} />
{!collapsed && <span>{item.name}</span>}
</Link>
);
})}
</nav>
{/* Footer */}
<div className="border-t border-zinc-200/60 p-3 dark:border-zinc-800">
{!collapsed && (
<div className="mb-3 rounded-xl bg-gradient-to-br from-emerald-50 to-cyan-50 p-3 dark:from-emerald-950/30 dark:to-cyan-950/30">
<p className="text-xs font-semibold text-emerald-700 dark:text-emerald-400">Pro Plan</p>
<p className="mt-0.5 text-[11px] text-emerald-600/70 dark:text-emerald-500/70">Unlimited menus & orders</p>
</div>
)}
<button
className={cn(
'flex w-full items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium text-zinc-500 transition-all hover:bg-zinc-100 hover:text-zinc-700 dark:hover:bg-zinc-800',
collapsed && 'justify-center px-2'
)}
>
<LogOut className="h-[18px] w-[18px]" />
{!collapsed && <span>Sign Out</span>}
</button>
{/* Collapse toggle — desktop only */}
<button
onClick={() => setCollapsed(!collapsed)}
className="mt-2 hidden w-full items-center justify-center rounded-xl p-2 text-zinc-400 transition-all hover:bg-zinc-100 hover:text-zinc-600 lg:flex dark:hover:bg-zinc-800"
>
<ChevronLeft className={cn('h-4 w-4 transition-transform', collapsed && 'rotate-180')} />
</button>
</div>
</aside>
</>
);
}
|