ScanMenu / src /components /layout /dashboard-sidebar.tsx
HamzaAri's picture
πŸš€ Deploy ScanMenu - Production-ready SaaS web app for digital restaurant menus & QR ordering
e1ef9fc verified
'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>
</>
);
}