feat(nav): add responsive mobile dropdown menu with avatar and counters

- Hide desktop items on small screens, add Menu/X toggle
- Include Matches badge, History, Profile, and Logout
- Keep real-time pending matches counter
This commit is contained in:
Radosław Gierwiało
2025-11-15 23:09:45 +01:00
parent 38adf1e5a5
commit cbc970f60b

View File

@@ -1,6 +1,6 @@
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import { useAuth } from '../../contexts/AuthContext'; import { useAuth } from '../../contexts/AuthContext';
import { Video, LogOut, User, History, Users } from 'lucide-react'; import { Video, LogOut, User, History, Users, Menu, X } from 'lucide-react';
import Avatar from '../common/Avatar'; import Avatar from '../common/Avatar';
import { useState, useEffect } from 'react'; import { useState, useEffect } from 'react';
import { matchesAPI } from '../../services/api'; import { matchesAPI } from '../../services/api';
@@ -10,6 +10,7 @@ const Navbar = () => {
const { user, logout } = useAuth(); const { user, logout } = useAuth();
const navigate = useNavigate(); const navigate = useNavigate();
const [pendingMatchesCount, setPendingMatchesCount] = useState(0); const [pendingMatchesCount, setPendingMatchesCount] = useState(0);
const [menuOpen, setMenuOpen] = useState(false);
const handleLogout = () => { const handleLogout = () => {
logout(); logout();
@@ -68,8 +69,8 @@ const Navbar = () => {
<span className="text-xl font-bold text-gray-900">spotlight.cam</span> <span className="text-xl font-bold text-gray-900">spotlight.cam</span>
</Link> </Link>
</div> </div>
{/* Desktop menu */}
<div className="flex items-center space-x-4"> <div className="hidden md:flex items-center space-x-4">
<Link <Link
to="/matches" to="/matches"
className="flex items-center space-x-1 px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-primary-600 hover:bg-gray-100 relative" className="flex items-center space-x-1 px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:text-primary-600 hover:bg-gray-100 relative"
@@ -112,8 +113,70 @@ const Navbar = () => {
<span>Logout</span> <span>Logout</span>
</button> </button>
</div> </div>
{/* Mobile menu button */}
<div className="flex md:hidden items-center">
<button
onClick={() => setMenuOpen((o) => !o)}
aria-label="Open menu"
className="p-2 rounded-md text-gray-700 hover:bg-gray-100"
>
{menuOpen ? <X className="w-6 h-6" /> : <Menu className="w-6 h-6" />}
</button>
</div> </div>
</div> </div>
</div>
{/* Mobile dropdown */}
{menuOpen && (
<div className="md:hidden border-t bg-white shadow-sm">
<div className="px-4 py-3 flex items-center space-x-3">
<Avatar src={user?.avatar} username={user.username} size={32} />
<div className="flex-1">
<p className="text-sm font-medium text-gray-900">{user.username}</p>
</div>
</div>
<div className="px-2 pb-2 space-y-1">
<Link
to="/matches"
onClick={() => setMenuOpen(false)}
className="flex items-center justify-between px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-100"
>
<span className="flex items-center gap-2">
<Users className="w-4 h-4" /> Matches
</span>
{pendingMatchesCount > 0 && (
<span className="ml-2 bg-red-500 text-white text-xs rounded-full h-5 px-2 flex items-center justify-center font-semibold">
{pendingMatchesCount}
</span>
)}
</Link>
<Link
to="/history"
onClick={() => setMenuOpen(false)}
className="flex items-center gap-2 px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-100"
>
<History className="w-4 h-4" /> History
</Link>
<Link
to="/profile"
onClick={() => setMenuOpen(false)}
className="flex items-center gap-2 px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-100"
>
<User className="w-4 h-4" /> Profile
</Link>
<button
onClick={() => {
setMenuOpen(false);
handleLogout();
}}
className="w-full flex items-center gap-2 px-3 py-2 rounded-md text-sm font-medium text-red-600 hover:bg-red-50"
>
<LogOut className="w-4 h-4" /> Logout
</button>
</div>
</div>
)}
</nav> </nav>
); );
}; };