Spaces:
Sleeping
Sleeping
| import { Link } from 'react-router-dom'; | |
| import { useAuth } from '../context/AuthContext'; | |
| import { useSocket } from '../context/SocketContext'; | |
| import { Camera, LogOut, User, Bell, MessageSquare } from 'lucide-react'; | |
| import { useState } from 'react'; | |
| const Header = () => { | |
| const { user, activeRole, switchRole, logout } = useAuth(); | |
| const { notifications, setNotifications } = useSocket() || { notifications: [] }; | |
| const [isNotifOpen, setIsNotifOpen] = useState(false); | |
| const unreadCount = notifications.filter(n => !n.isRead).length; | |
| return ( | |
| <header className="bg-white/80 backdrop-blur-md border-b sticky top-0 z-50"> | |
| <div className="container mx-auto px-4 py-4 flex justify-between items-center"> | |
| <Link to="/" className="flex items-center space-x-2 text-blue-600 font-bold text-2xl group"> | |
| <div className="bg-blue-600 text-white p-1.5 rounded-xl group-hover:rotate-12 transition-transform"> | |
| <Camera size={24} /> | |
| </div> | |
| <span>SnapLocal<span className="text-gray-900 ml-1">Pro</span></span> | |
| </Link> | |
| <nav className="flex items-center space-x-6"> | |
| <Link to="/search" className="text-gray-600 hover:text-blue-600 font-bold text-sm uppercase tracking-wider">Explore</Link> | |
| {user ? ( | |
| <div className="flex items-center space-x-5"> | |
| {/* Role Switcher — only shown when user has multiple roles */} | |
| {user?.roles?.length > 1 && ( | |
| <div className="flex items-center bg-gray-100 rounded-xl p-1 gap-1"> | |
| <button | |
| onClick={() => { switchRole('customer'); window.location.href = '/dashboard'; }} | |
| className={`px-3 py-1.5 rounded-lg text-xs font-black transition ${activeRole === 'customer' ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-500 hover:text-gray-700' | |
| }`} | |
| title="Switch to Customer view" | |
| > | |
| 👤 Customer | |
| </button> | |
| <button | |
| onClick={() => { switchRole('photographer'); window.location.href = '/dashboard'; }} | |
| className={`px-3 py-1.5 rounded-lg text-xs font-black transition ${activeRole === 'photographer' ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-500 hover:text-gray-700' | |
| }`} | |
| title="Switch to Photographer view" | |
| > | |
| 📷 Photographer | |
| </button> | |
| </div> | |
| )} | |
| <Link to="/chat" className="relative p-2 text-gray-400 hover:text-blue-600 transition"> | |
| <MessageSquare size={22} /> | |
| <span className="absolute top-1 right-1 w-2 h-2 bg-blue-600 rounded-full border-2 border-white"></span> | |
| </Link> | |
| <div className="relative"> | |
| <button | |
| onClick={() => setIsNotifOpen(!isNotifOpen)} | |
| className="relative p-2 text-gray-400 hover:text-blue-600 transition" | |
| > | |
| <Bell size={22} /> | |
| {unreadCount > 0 && ( | |
| <span className="absolute top-0 right-0 bg-red-500 text-white text-[10px] font-black px-1.5 py-0.5 rounded-full min-w-[18px] border-2 border-white"> | |
| {unreadCount} | |
| </span> | |
| )} | |
| </button> | |
| {isNotifOpen && ( | |
| <div className="absolute right-0 mt-4 w-80 bg-white rounded-3xl shadow-2xl border border-gray-100 overflow-hidden animate-in fade-in zoom-in-95 duration-200"> | |
| <div className="p-5 border-b border-gray-50 flex justify-between items-center"> | |
| <h3 className="font-black text-gray-900">Notifications</h3> | |
| {unreadCount > 0 && ( | |
| <button className="text-xs text-blue-600 font-bold hover:underline">Mark all read</button> | |
| )} | |
| </div> | |
| <div className="max-h-96 overflow-y-auto"> | |
| {notifications.length > 0 ? notifications.map((n, i) => ( | |
| <div key={i} className="p-4 border-b border-gray-50 hover:bg-gray-50 transition cursor-pointer"> | |
| <p className="text-sm font-medium text-gray-800">{n.content}</p> | |
| <p className="text-[10px] text-gray-400 font-bold uppercase mt-1">{new Date(n.createdAt || Date.now()).toLocaleDateString()}</p> | |
| </div> | |
| )) : ( | |
| <div className="p-8 text-center text-gray-400 italic text-sm">No notifications yet.</div> | |
| )} | |
| </div> | |
| <Link to="/dashboard" onClick={() => setIsNotifOpen(false)} className="block p-4 text-center text-xs font-black text-blue-600 bg-gray-50 hover:bg-gray-100 transition"> | |
| View All Bookings | |
| </Link> | |
| </div> | |
| )} | |
| </div> | |
| <Link to="/dashboard" className="flex items-center space-x-2 text-gray-700 font-black p-1 pr-4 bg-gray-50 rounded-2xl hover:ring-2 hover:ring-blue-100 transition"> | |
| <div className="w-9 h-9 bg-blue-600 text-white rounded-xl flex items-center justify-center shadow-lg shadow-blue-100"> | |
| <User size={18} /> | |
| </div> | |
| <span className="hidden sm:inline text-xs uppercase tracking-wider">{user?.firstName || user?.email?.split('@')[0]}</span> | |
| </Link> | |
| <button | |
| onClick={logout} | |
| className="p-2 text-gray-400 hover:text-red-600 transition" | |
| title="Logout" | |
| > | |
| <LogOut size={20} /> | |
| </button> | |
| </div> | |
| ) : ( | |
| <div className="flex items-center space-x-4"> | |
| <Link to="/login" className="text-gray-600 hover:text-blue-600 font-black text-xs uppercase tracking-widest">Login</Link> | |
| <Link to="/register" className="bg-gray-900 text-white px-6 py-3 rounded-2xl font-black text-xs uppercase tracking-widest hover:bg-gray-800 transition shadow-xl active:scale-95">Get Started</Link> | |
| </div> | |
| )} | |
| </nav> | |
| </div> | |
| </header> | |
| ); | |
| }; | |
| export default Header; | |