130 lines
4.1 KiB
TypeScript
130 lines
4.1 KiB
TypeScript
'use client';
|
|
|
|
import Link from 'next/link';
|
|
import { usePathname } from 'next/navigation';
|
|
import { cn } from '@/lib/utils';
|
|
import Image from 'next/image';
|
|
import { Josefin_Sans } from 'next/font/google';
|
|
import { Montserrat } from 'next/font/google';
|
|
import {
|
|
LayoutDashboard,
|
|
FolderOpen,
|
|
Users,
|
|
UserCog,
|
|
Settings,
|
|
ChevronLeft,
|
|
ChevronRight,
|
|
ListTodo,
|
|
CalendarRange,
|
|
} from 'lucide-react';
|
|
import { useState } from 'react';
|
|
import { useSession } from 'next-auth/react';
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
const navItems = [
|
|
{ href: '/dashboard', label: 'Dashboard', icon: LayoutDashboard },
|
|
{ href: '/projects', label: 'Projects', icon: FolderOpen },
|
|
{ href: '/tasks', label: 'My Tasks', icon: ListTodo, hideForClient: true },
|
|
{ href: '/schedule', label: 'Schedule', icon: CalendarRange, adminOnly: true },
|
|
{ href: '/clients', label: 'Clients', icon: Users, adminOnly: true },
|
|
{ href: '/users', label: 'Users', icon: UserCog, adminOnly: true, adminStrictOnly: true },
|
|
{ href: '/settings', label: 'Settings', icon: Settings },
|
|
];
|
|
|
|
const josefin = Josefin_Sans({
|
|
subsets: ['latin'],
|
|
weight: ['300', '400'],
|
|
});
|
|
const montserrat = Montserrat({
|
|
subsets: ['latin'],
|
|
weight: ['200', '500', '600'],
|
|
});
|
|
|
|
export function Sidebar() {
|
|
const pathname = usePathname();
|
|
const { data: session } = useSession();
|
|
const [collapsed, setCollapsed] = useState(false);
|
|
const isAdmin = ['ADMIN', 'PRODUCER'].includes(session?.user?.role ?? '');
|
|
|
|
return (
|
|
<aside
|
|
className={cn(
|
|
'flex flex-col border-r border-zinc-800 bg-zinc-900 transition-all duration-200',
|
|
collapsed ? 'w-16' : 'w-60',
|
|
)}
|
|
>
|
|
{/* Logo */}
|
|
<div
|
|
className={cn(
|
|
'flex items-center gap-3 px-4 py-5 border-b border-zinc-800',
|
|
collapsed && 'justify-center px-0',
|
|
)}
|
|
>
|
|
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-md bg-black">
|
|
<Image src="/logo.svg" alt="Logo" width={32} height={32} />
|
|
</div>
|
|
|
|
{!collapsed && (
|
|
<div className={montserrat.className}>
|
|
<span className="block text-2xl font-light text-white leading-none">
|
|
TWO TALES
|
|
</span>
|
|
|
|
<span className="block text-[11px] tracking-[0.18em] italic text-zinc-400 leading-none -mt-0.25">
|
|
vfx review
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Navigation */}
|
|
<nav className="flex-1 px-2 py-4 space-y-1">
|
|
{navItems.map((item) => {
|
|
if (item.adminOnly && !isAdmin) return null;
|
|
if ((item as any).adminStrictOnly && session?.user?.role !== 'ADMIN') return null;
|
|
if ((item as any).hideForClient && session?.user?.role === 'CLIENT')
|
|
return null;
|
|
const Icon = item.icon;
|
|
const isActive =
|
|
pathname === item.href || pathname.startsWith(item.href + '/');
|
|
|
|
return (
|
|
<Link
|
|
key={item.href}
|
|
href={item.href}
|
|
className={cn(
|
|
'flex items-center gap-3 rounded-lg px-3 py-2.5 text-sm font-medium transition-colors',
|
|
collapsed && 'justify-center px-0 w-full',
|
|
isActive
|
|
? 'bg-amber-500/10 text-amber-400'
|
|
: 'text-zinc-400 hover:text-white hover:bg-zinc-800',
|
|
)}
|
|
title={collapsed ? item.label : undefined}
|
|
>
|
|
<Icon className="h-4 w-4 shrink-0" />
|
|
{!collapsed && <span>{item.label}</span>}
|
|
</Link>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
{/* Collapse toggle */}
|
|
<div className="border-t border-zinc-800 p-2">
|
|
<Button
|
|
variant="ghost"
|
|
size="icon-sm"
|
|
className="w-full"
|
|
onClick={() => setCollapsed(!collapsed)}
|
|
title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
|
|
>
|
|
{collapsed ? (
|
|
<ChevronRight className="h-4 w-4" />
|
|
) : (
|
|
<ChevronLeft className="h-4 w-4" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|