| import React, { useState } from 'react'; |
| import { Link, useNavigate, useLocation } from 'react-router-dom'; |
| import { |
| Zap, |
| Menu, |
| X, |
| LogOut, |
| User, |
| LayoutDashboard, |
| BookOpen, |
| Award, |
| ChevronDown |
| } from 'lucide-react'; |
| import { useAuth } from '../AuthContext'; |
| import { cn } from '../lib/utils'; |
| import { motion, AnimatePresence } from 'motion/react'; |
|
|
| export function Navbar() { |
| const { profile, logout } = useAuth(); |
| const [isOpen, setIsOpen] = useState(false); |
| const [showProfileMenu, setShowProfileMenu] = useState(false); |
| const navigate = useNavigate(); |
| const location = useLocation(); |
|
|
| const handleLogout = async () => { |
| await logout(); |
| navigate('/'); |
| }; |
|
|
| const navLinks = profile?.role === 'teacher' ? [ |
| { label: 'Dashboard', path: '/teacher/dashboard', icon: LayoutDashboard }, |
| { label: 'Question Bank', path: '/teacher/questions', icon: BookOpen }, |
| { label: 'Students', path: '/teacher/groups', icon: User }, |
| ] : [ |
| { label: 'Dashboard', path: '/student/dashboard', icon: LayoutDashboard }, |
| { label: 'My Results', path: '/student/results', icon: Award }, |
| ]; |
|
|
| return ( |
| <nav className="bg-white/45 backdrop-blur-2xl border-b border-white/60 sticky top-0 z-50"> |
| <div className="max-w-[1200px] mx-auto px-6 h-20 flex items-center justify-between"> |
| <Link to="/" className="flex items-center gap-2"> |
| <div className="w-9 h-9 bg-primary rounded-xl flex items-center justify-center shadow-lg shadow-primary/20"> |
| <Zap className="w-5 h-5 text-white fill-white" /> |
| </div> |
| <span className="text-xl font-black text-dark-navy tracking-tight">ExamForge</span> |
| </Link> |
| |
| {/* Desktop Links */} |
| <div className="hidden md:flex items-center gap-8"> |
| {navLinks.map((link) => ( |
| <Link |
| key={link.path} |
| to={link.path} |
| className={cn( |
| "text-sm font-bold transition-all flex items-center gap-2", |
| location.pathname === link.path |
| ? "text-primary" |
| : "text-text-muted hover:text-text-primary" |
| )} |
| > |
| <link.icon className="w-4 h-4" /> |
| {link.label} |
| </Link> |
| ))} |
| </div> |
| |
| {/* Profile / Actions */} |
| <div className="hidden md:flex items-center gap-6"> |
| <div className="relative"> |
| <button |
| onClick={() => setShowProfileMenu(!showProfileMenu)} |
| className="flex items-center gap-3 p-1.5 rounded-xl hover:bg-light-bg transition-colors" |
| > |
| <div className="w-8 h-8 bg-primary/10 rounded-lg flex items-center justify-center text-primary font-bold text-xs"> |
| {profile?.fullName?.charAt(0) || 'U'} |
| </div> |
| <div className="text-left"> |
| <p className="text-xs font-bold text-dark-navy leading-none mb-1">{profile?.fullName?.split(' ')[0]}</p> |
| <p className="text-[10px] text-text-muted font-bold uppercase tracking-widest leading-none">{profile?.role}</p> |
| </div> |
| <ChevronDown className="w-3 h-3 text-text-muted" /> |
| </button> |
| |
| <AnimatePresence> |
| {showProfileMenu && ( |
| <> |
| <div className="fixed inset-0" onClick={() => setShowProfileMenu(false)} /> |
| <motion.div |
| initial={{ opacity: 0, y: 10, scale: 0.95 }} |
| animate={{ opacity: 1, y: 0, scale: 1 }} |
| exit={{ opacity: 0, y: 10, scale: 0.95 }} |
| className="absolute right-0 mt-2 w-48 bg-white rounded-xl shadow-[0_10px_30px_rgba(0,0,0,0.1)] border border-border-gray p-2 z-50" |
| > |
| <button |
| onClick={handleLogout} |
| className="w-full flex items-center gap-3 px-3 py-2 text-sm font-bold text-red-600 hover:bg-red-50 rounded-lg transition-colors" |
| > |
| <LogOut className="w-4 h-4" /> Sign Out |
| </button> |
| </motion.div> |
| </> |
| )} |
| </AnimatePresence> |
| </div> |
| </div> |
| |
| {/* Mobile Toggle */} |
| <button onClick={() => setIsOpen(!isOpen)} className="md:hidden p-2 text-dark-navy"> |
| {isOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />} |
| </button> |
| </div> |
|
|
| {} |
| <AnimatePresence> |
| {isOpen && ( |
| <motion.div |
| initial={{ opacity: 0, height: 0 }} |
| animate={{ opacity: 1, height: 'auto' }} |
| exit={{ opacity: 0, height: 0 }} |
| className="md:hidden bg-white border-t border-border-gray px-6 py-8" |
| > |
| <div className="flex flex-col gap-6"> |
| {navLinks.map((link) => ( |
| <Link |
| key={link.path} |
| to={link.path} |
| onClick={() => setIsOpen(false)} |
| className="flex items-center gap-4 text-lg font-bold text-dark-navy" |
| > |
| <div className="w-10 h-10 bg-light-bg rounded-xl flex items-center justify-center"> |
| <link.icon className="w-5 h-5 text-text-muted" /> |
| </div> |
| {link.label} |
| </Link> |
| ))} |
| <hr className="border-border-gray" /> |
| <button |
| onClick={handleLogout} |
| className="flex items-center gap-4 text-lg font-bold text-red-600" |
| > |
| <div className="w-10 h-10 bg-red-50 rounded-xl flex items-center justify-center"> |
| <LogOut className="w-5 h-5" /> |
| </div> |
| Sign Out |
| </button> |
| </div> |
| </motion.div> |
| )} |
| </AnimatePresence> |
| </nav> |
| ); |
| } |
|
|