125 lines
No EOL
3.7 KiB
TypeScript
125 lines
No EOL
3.7 KiB
TypeScript
import { Link, useLocation } from 'react-router-dom';
|
|
import { useAuthStore } from '../../lib/auth';
|
|
|
|
interface SidebarItem {
|
|
label: string;
|
|
href: string;
|
|
icon: string;
|
|
roles?: string[];
|
|
badge?: number;
|
|
}
|
|
|
|
interface SidebarProps {
|
|
onMobileClose?: () => void;
|
|
}
|
|
|
|
export function Sidebar({ onMobileClose }: SidebarProps) {
|
|
const { user } = useAuthStore();
|
|
const location = useLocation();
|
|
|
|
const sidebarItems: SidebarItem[] = [
|
|
{
|
|
label: 'Dashboard',
|
|
href: '/',
|
|
icon: '🏠',
|
|
},
|
|
{
|
|
label: 'All Jobs',
|
|
href: '/jobs',
|
|
icon: '📋',
|
|
},
|
|
{
|
|
label: 'Upload Video',
|
|
href: '/jobs/new',
|
|
icon: '📤',
|
|
roles: ['client', 'production', 'admin'],
|
|
},
|
|
{
|
|
label: 'QC Review',
|
|
href: '/admin/qc',
|
|
icon: '🔍',
|
|
roles: ['reviewer', 'production', 'admin'],
|
|
},
|
|
{
|
|
label: 'Final Review',
|
|
href: '/admin/final',
|
|
icon: '✅',
|
|
roles: ['reviewer', 'production', 'admin'],
|
|
},
|
|
];
|
|
|
|
const filteredItems = sidebarItems.filter(item =>
|
|
!item.roles || item.roles.includes(user?.role || '')
|
|
);
|
|
|
|
const isActive = (href: string) => {
|
|
if (href === '/') {
|
|
return location.pathname === '/';
|
|
}
|
|
return location.pathname.startsWith(href);
|
|
};
|
|
|
|
return (
|
|
<div className="h-full w-64 bg-white shadow-lg border-r border-gray-200 flex flex-col">
|
|
{/* Logo/Brand */}
|
|
<div className="flex items-center px-6 py-4 border-b border-gray-200">
|
|
<div className="flex items-center space-x-3">
|
|
<div className="w-8 h-8 bg-gradient-to-br from-blue-500 to-purple-600 rounded-lg flex items-center justify-center">
|
|
<span className="text-white text-sm font-bold">VA</span>
|
|
</div>
|
|
<div>
|
|
<h1 className="text-lg font-bold text-gray-900">Video Access</h1>
|
|
<p className="text-xs text-gray-500">Accessibility Platform</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Navigation */}
|
|
<nav className="flex-1 px-4 py-6 space-y-2">
|
|
{filteredItems.map((item) => (
|
|
<Link
|
|
key={item.href}
|
|
to={item.href}
|
|
onClick={onMobileClose}
|
|
className={`flex items-center px-3 py-2.5 rounded-lg text-sm font-medium transition-all duration-200 group ${
|
|
isActive(item.href)
|
|
? 'bg-blue-50 text-blue-700 border border-blue-200'
|
|
: 'text-gray-600 hover:text-gray-900 hover:bg-gray-50'
|
|
}`}
|
|
>
|
|
<span className="text-lg mr-3">{item.icon}</span>
|
|
<span className="flex-1">{item.label}</span>
|
|
{item.badge && (
|
|
<span className={`px-2 py-0.5 text-xs rounded-full font-medium ${
|
|
isActive(item.href)
|
|
? 'bg-blue-100 text-blue-700'
|
|
: 'bg-gray-100 text-gray-600'
|
|
}`}>
|
|
{item.badge}
|
|
</span>
|
|
)}
|
|
</Link>
|
|
))}
|
|
</nav>
|
|
|
|
{/* User Info */}
|
|
<div className="px-4 py-4 border-t border-gray-200">
|
|
<div className="flex items-center space-x-3">
|
|
<div className="w-8 h-8 bg-gradient-to-br from-green-400 to-blue-500 rounded-full flex items-center justify-center">
|
|
<span className="text-white text-sm font-medium">
|
|
{user?.email?.charAt(0).toUpperCase()}
|
|
</span>
|
|
</div>
|
|
<div className="flex-1 min-w-0">
|
|
<p className="text-sm font-medium text-gray-900 truncate">
|
|
{user?.email}
|
|
</p>
|
|
<p className="text-xs text-gray-500 capitalize">
|
|
{user?.role}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |