Spaces:
Sleeping
Sleeping
| import { useState, useEffect } from 'react'; | |
| import { Link, useLocation, useNavigate } from 'react-router-dom'; | |
| import { motion, AnimatePresence } from 'framer-motion'; | |
| import { HiMenu, HiX, HiChevronDown, HiHeart, HiUserCircle } from 'react-icons/hi'; | |
| import { useAuth } from '../../contexts/AuthContext'; | |
| import { NAV_LINKS, FOUNDATION_INFO } from '../../utils/constants'; | |
| import './Header.css'; | |
| export default function Header() { | |
| const [isScrolled, setIsScrolled] = useState(false); | |
| const [mobileOpen, setMobileOpen] = useState(false); | |
| const [dropdownOpen, setDropdownOpen] = useState(null); | |
| const { currentUser, userProfile, logout } = useAuth(); | |
| const location = useLocation(); | |
| const navigate = useNavigate(); | |
| useEffect(() => { | |
| const handleScroll = () => setIsScrolled(window.scrollY > 20); | |
| window.addEventListener('scroll', handleScroll); | |
| return () => window.removeEventListener('scroll', handleScroll); | |
| }, []); | |
| // Close mobile menu on route change | |
| useEffect(() => { | |
| setMobileOpen(false); | |
| setDropdownOpen(null); | |
| }, [location]); | |
| const handleLogout = async () => { | |
| try { | |
| await logout(); | |
| navigate('/'); | |
| } catch (error) { | |
| console.error('Logout error:', error); | |
| } | |
| }; | |
| const isActive = (path) => location.pathname === path; | |
| return ( | |
| <header className={`header ${isScrolled ? 'header--scrolled' : ''}`}> | |
| <div className="header__container container"> | |
| {/* Logo */} | |
| <Link to="/" className="header__logo"> | |
| <img src="/logo.png" alt={FOUNDATION_INFO.name} className="header__logo-img" /> | |
| <div className="header__logo-text"> | |
| <span className="header__logo-name">Social Share</span> | |
| <span className="header__logo-sub">& Care Foundation</span> | |
| </div> | |
| </Link> | |
| {/* Desktop Navigation */} | |
| <nav className="header__nav"> | |
| {NAV_LINKS.map((link) => ( | |
| <div | |
| key={link.path} | |
| className="header__nav-item" | |
| onMouseEnter={() => link.children && setDropdownOpen(link.path)} | |
| onMouseLeave={() => setDropdownOpen(null)} | |
| > | |
| <Link | |
| to={link.path} | |
| className={`header__nav-link ${isActive(link.path) ? 'header__nav-link--active' : ''}`} | |
| > | |
| {link.label} | |
| {link.children && <HiChevronDown className="header__chevron" />} | |
| </Link> | |
| {/* Dropdown */} | |
| <AnimatePresence> | |
| {link.children && dropdownOpen === link.path && ( | |
| <motion.div | |
| className="header__dropdown" | |
| initial={{ opacity: 0, y: 10 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| exit={{ opacity: 0, y: 10 }} | |
| transition={{ duration: 0.2 }} | |
| > | |
| {link.children.map((child) => ( | |
| <Link | |
| key={child.path} | |
| to={child.path} | |
| className="header__dropdown-link" | |
| > | |
| {child.label} | |
| </Link> | |
| ))} | |
| </motion.div> | |
| )} | |
| </AnimatePresence> | |
| </div> | |
| ))} | |
| </nav> | |
| {/* Right Actions */} | |
| <div className="header__actions"> | |
| <Link to="/donate" className="btn btn--primary btn--sm header__donate-btn"> | |
| <HiHeart /> Donate | |
| </Link> | |
| {currentUser ? ( | |
| <div | |
| className="header__user" | |
| onMouseEnter={() => setDropdownOpen('user')} | |
| onMouseLeave={() => setDropdownOpen(null)} | |
| > | |
| <button className="header__user-btn"> | |
| {userProfile?.profileImage ? ( | |
| <img src={userProfile.profileImage} alt="" className="header__avatar" referrerPolicy="no-referrer" /> | |
| ) : ( | |
| <HiUserCircle size={40} /> | |
| )} | |
| </button> | |
| <AnimatePresence> | |
| {dropdownOpen === 'user' && ( | |
| <motion.div | |
| className="header__dropdown header__dropdown--right" | |
| initial={{ opacity: 0, y: 10 }} | |
| animate={{ opacity: 1, y: 0 }} | |
| exit={{ opacity: 0, y: 10 }} | |
| > | |
| <div className="header__dropdown-user"> | |
| <strong>{userProfile?.displayName}</strong> | |
| <span>{userProfile?.email}</span> | |
| </div> | |
| <Link to="/dashboard" className="header__dropdown-link">Dashboard</Link> | |
| <Link to="/dashboard/profile" className="header__dropdown-link">Profile</Link> | |
| <Link to="/donations/history" className="header__dropdown-link">My Donations</Link> | |
| <button onClick={handleLogout} className="header__dropdown-link header__dropdown-link--danger"> | |
| Sign Out | |
| </button> | |
| </motion.div> | |
| )} | |
| </AnimatePresence> | |
| </div> | |
| ) : ( | |
| <Link to="/login" className="btn btn--outline btn--sm"> | |
| Sign In | |
| </Link> | |
| )} | |
| {/* Mobile Toggle */} | |
| <button | |
| className="header__mobile-toggle" | |
| onClick={() => setMobileOpen(!mobileOpen)} | |
| aria-label="Toggle menu" | |
| > | |
| {mobileOpen ? <HiX size={24} /> : <HiMenu size={24} />} | |
| </button> | |
| </div> | |
| </div> | |
| {/* Mobile Menu */} | |
| <AnimatePresence> | |
| {mobileOpen && ( | |
| <motion.div | |
| className="header__mobile" | |
| initial={{ height: 0, opacity: 0 }} | |
| animate={{ height: 'auto', opacity: 1 }} | |
| exit={{ height: 0, opacity: 0 }} | |
| transition={{ duration: 0.3 }} | |
| > | |
| <nav className="header__mobile-nav"> | |
| {NAV_LINKS.map((link) => ( | |
| <div key={link.path}> | |
| <Link | |
| to={link.path} | |
| className={`header__mobile-link ${isActive(link.path) ? 'header__mobile-link--active' : ''}`} | |
| > | |
| {link.label} | |
| </Link> | |
| {link.children && ( | |
| <div className="header__mobile-sub"> | |
| {link.children.map((child) => ( | |
| <Link key={child.path} to={child.path} className="header__mobile-sub-link"> | |
| {child.label} | |
| </Link> | |
| ))} | |
| </div> | |
| )} | |
| </div> | |
| ))} | |
| {!currentUser && ( | |
| <Link to="/login" className="btn btn--primary" style={{ marginTop: '1rem' }}> | |
| Sign In | |
| </Link> | |
| )} | |
| {currentUser && ( | |
| <> | |
| <Link to="/dashboard" className="header__mobile-link">Dashboard</Link> | |
| <Link to="/donations/history" className="header__mobile-link">My Donations</Link> | |
| <button onClick={handleLogout} className="header__mobile-link" style={{ color: '#EF4444' }}> | |
| Sign Out | |
| </button> | |
| </> | |
| )} | |
| </nav> | |
| </motion.div> | |
| )} | |
| </AnimatePresence> | |
| </header> | |
| ); | |
| } | |