163 lines
5.3 KiB
TypeScript
163 lines
5.3 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import { usePathname, useRouter } from 'next/navigation'
|
|
import Link from 'next/link'
|
|
import { createClient } from '@/lib/supabase/client'
|
|
import {
|
|
LayoutDashboard,
|
|
BookOpen,
|
|
Plus,
|
|
BarChart3,
|
|
Settings,
|
|
LogOut,
|
|
Shield,
|
|
Menu,
|
|
X,
|
|
} from 'lucide-react'
|
|
import { cn } from '@/lib/utils'
|
|
|
|
interface SidebarProps {
|
|
username: string
|
|
email: string
|
|
role: string
|
|
}
|
|
|
|
const navLinks = [
|
|
{ href: '/dashboard', label: 'Dashboard', icon: LayoutDashboard },
|
|
{ href: '/dashboard/quizzes', label: 'Mes Quizzes', icon: BookOpen },
|
|
{ href: '/dashboard/sessions/create', label: 'Créer Session', icon: Plus },
|
|
{ href: '/dashboard/reports', label: 'Rapports', icon: BarChart3 },
|
|
{ href: '/dashboard/settings', label: 'Paramètres', icon: Settings },
|
|
]
|
|
|
|
export default function Sidebar({ username, email, role }: SidebarProps) {
|
|
const pathname = usePathname()
|
|
const router = useRouter()
|
|
const [mobileOpen, setMobileOpen] = useState(false)
|
|
|
|
const handleLogout = async () => {
|
|
const supabase = createClient()
|
|
await supabase.auth.signOut()
|
|
router.push('/login')
|
|
router.refresh()
|
|
}
|
|
|
|
const closeMobile = () => setMobileOpen(false)
|
|
|
|
const sidebarContent = (
|
|
<>
|
|
{/* Brand */}
|
|
<div className="p-5 border-b border-border">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-10 h-10 bg-primary rounded-xl flex items-center justify-center shadow-lg shadow-primary/30 flex-shrink-0">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none">
|
|
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 14H9V8h2v8zm4 0h-2V8h2v8z" fill="white"/>
|
|
</svg>
|
|
</div>
|
|
<div>
|
|
<p className="font-bold text-text-primary leading-tight">SolyQuiz</p>
|
|
<p className="text-xs text-text-muted capitalize">{role === 'admin' ? 'Admin Portal' : 'Trainer Portal'}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Navigation */}
|
|
<nav className="flex-1 p-4 space-y-1 overflow-y-auto">
|
|
{navLinks.map(({ href, label, icon: Icon }) => {
|
|
const isActive = pathname === href || (href !== '/dashboard' && pathname.startsWith(href))
|
|
return (
|
|
<Link
|
|
key={href}
|
|
href={href}
|
|
onClick={closeMobile}
|
|
className={cn('sidebar-link', isActive && 'sidebar-link-active')}
|
|
>
|
|
<Icon size={18} />
|
|
<span>{label}</span>
|
|
</Link>
|
|
)
|
|
})}
|
|
</nav>
|
|
|
|
{/* Section admin */}
|
|
{role === 'admin' && (
|
|
<div className="px-4 pb-2 border-t border-border pt-3 mt-1">
|
|
<p className="text-xs font-medium text-text-muted uppercase tracking-wider px-3 mb-1">Administration</p>
|
|
<Link
|
|
href="/dashboard/admin/users"
|
|
onClick={closeMobile}
|
|
className={cn('sidebar-link', pathname.startsWith('/dashboard/admin') && 'sidebar-link-active')}
|
|
>
|
|
<Shield size={18} />
|
|
<span>Utilisateurs</span>
|
|
</Link>
|
|
</div>
|
|
)}
|
|
|
|
{/* User profile + logout */}
|
|
<div className="border-t border-border p-4">
|
|
<div className="flex items-center gap-3 mb-3">
|
|
<div className="w-9 h-9 bg-primary/20 rounded-full flex items-center justify-center text-primary font-semibold text-sm flex-shrink-0">
|
|
{username.slice(0, 2).toUpperCase()}
|
|
</div>
|
|
<div className="min-w-0">
|
|
<p className="text-sm font-medium text-text-primary truncate">{username}</p>
|
|
<p className="text-xs text-text-muted truncate">{email}</p>
|
|
</div>
|
|
</div>
|
|
<button
|
|
onClick={handleLogout}
|
|
className="sidebar-link w-full text-red-400 hover:text-red-300 hover:bg-red-500/10"
|
|
>
|
|
<LogOut size={18} />
|
|
<span>Déconnexion</span>
|
|
</button>
|
|
</div>
|
|
</>
|
|
)
|
|
|
|
return (
|
|
<>
|
|
{/* Bouton hamburger — mobile uniquement */}
|
|
<button
|
|
onClick={() => setMobileOpen(true)}
|
|
className="lg:hidden fixed top-4 left-4 z-40 p-2 bg-background-secondary border border-border rounded-lg shadow-md"
|
|
aria-label="Ouvrir le menu"
|
|
>
|
|
<Menu size={20} className="text-text-primary" />
|
|
</button>
|
|
|
|
{/* Overlay mobile */}
|
|
{mobileOpen && (
|
|
<div
|
|
className="lg:hidden fixed inset-0 z-40 bg-black/60 backdrop-blur-sm"
|
|
onClick={closeMobile}
|
|
/>
|
|
)}
|
|
|
|
{/* Sidebar desktop (sticky) */}
|
|
<aside className="hidden lg:flex w-64 flex-shrink-0 bg-background-secondary border-r border-border flex-col h-screen sticky top-0">
|
|
{sidebarContent}
|
|
</aside>
|
|
|
|
{/* Sidebar mobile (drawer) */}
|
|
<aside
|
|
className={cn(
|
|
'lg:hidden fixed top-0 left-0 z-50 w-72 bg-background-secondary border-r border-border flex flex-col h-screen transition-transform duration-300',
|
|
mobileOpen ? 'translate-x-0' : '-translate-x-full'
|
|
)}
|
|
>
|
|
{/* Bouton fermer */}
|
|
<button
|
|
onClick={closeMobile}
|
|
className="absolute top-4 right-4 p-1.5 hover:bg-background-elevated rounded-lg text-text-muted hover:text-text-primary transition-colors"
|
|
>
|
|
<X size={18} />
|
|
</button>
|
|
{sidebarContent}
|
|
</aside>
|
|
</>
|
|
)
|
|
}
|