mkcart / frontend /src /components /Header.jsx
Kumar
updated
c2efbe6
import { useState, useEffect, useRef, useCallback } from "react"
import { Link, NavLink, useNavigate, useLocation } from "react-router-dom"
import Search from "./Search"
import NotificationCenter from "./NotificationCenter"
import { useSelector, useDispatch } from "react-redux"
import { logout, setUser } from "../store/slices/authSlice"
import { useGetWishlistsQuery } from "../store/slices/userApiSlice";
import { useGetCartsQuery } from "../store/slices/userApiSlice";
import { toast } from "react-toastify"
import { motion, AnimatePresence } from "framer-motion"
import { ShoppingCart, User, LogOut, Package, Menu, X, Heart, Gift, CreditCard } from "lucide-react"
import "./Header.css"
import { useLogoutAPIMutation } from "../store/slices/userApiSlice";
import { apiSlice } from "../store/slices/apiSlice";
import { performLogout } from "../utils/authUtils";
export default function Header({ onOpenLiveChat, onOpenAIAssistant }) {
const { isLoggedIn, userData } = useSelector((state) => state.auth)
const [isScrolled, setIsScrolled] = useState(false)
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
const [showUserDropdown, setShowUserDropdown] = useState(false)
const shouldSkipQueries = !isLoggedIn || !userData || !localStorage.getItem('token');
const { data: wishlistData, refetch: refetchWishlist, error: wishlistError } = useGetWishlistsQuery(undefined, {
skip: shouldSkipQueries,
refetchOnMountOrArgChange: false,
pollingInterval: 0,
});
const { data: cartsData, refetch: refetchCarts, error: cartError } = useGetCartsQuery(undefined, {
skip: shouldSkipQueries,
refetchOnMountOrArgChange: false,
pollingInterval: 0,
});
const [logoutAPI] = useLogoutAPIMutation();
const dispatch = useDispatch()
const navigate = useNavigate()
const location = useLocation()
useEffect(() => {
const initializeAuth = () => {
try {
const storedUserData = localStorage.getItem('userData');
const storedToken = localStorage.getItem('token');
if (storedUserData && storedToken) {
try {
const userData = JSON.parse(storedUserData);
if (userData._id && userData.email) {
dispatch(setUser(userData));
} else {
localStorage.removeItem('userData');
localStorage.removeItem('token');
}
} catch (error) {
console.error('Error parsing stored user data:', error);
localStorage.removeItem('userData');
localStorage.removeItem('token');
}
}
} catch (error) {
console.error('Error initializing auth:', error);
localStorage.removeItem('userData');
localStorage.removeItem('token');
}
};
initializeAuth();
}, [dispatch]);
const wishlistCount = Array.isArray(wishlistData?.wishlist)
? new Set(wishlistData.wishlist.map(item => item.productId || item._id)).size
: 0;
const cartItems = cartsData?.carts?.[0]?.cartItems || [];
useEffect(() => {
const handleScroll = () => {
setIsScrolled(window.scrollY > 50)
}
window.addEventListener("scroll", handleScroll)
return () => window.removeEventListener("scroll", handleScroll)
}, [])
useEffect(() => {
const handleClick = (e) => {
if (!e.target.closest('.user-menu')) setShowUserDropdown(false);
};
if (showUserDropdown) document.addEventListener('mousedown', handleClick);
return () => document.removeEventListener('mousedown', handleClick);
}, [showUserDropdown]);
useEffect(() => {
const handleCartUpdated = () => {
if (isLoggedIn && userData) {
refetchCarts();
}
};
window.addEventListener('cart-updated', handleCartUpdated);
return () => window.removeEventListener('cart-updated', handleCartUpdated);
}, [isLoggedIn, userData, refetchCarts]);
useEffect(() => {
const handleWishlistUpdated = () => {
if (isLoggedIn && userData) {
refetchWishlist();
}
};
window.addEventListener('wishlist-updated', handleWishlistUpdated);
return () => window.removeEventListener('wishlist-updated', handleWishlistUpdated);
}, [isLoggedIn, userData, refetchWishlist]);
const logoutHandler = useCallback(async () => {
await performLogout(dispatch, logoutAPI, logout, apiSlice.util.resetApiState);
}, [logoutAPI, dispatch]);
return (
<motion.header
initial={{ y: -100 }}
animate={{ y: 0 }}
transition={{ duration: 0.8, type: "spring", stiffness: 100 }}
className={`Premium-header ${isScrolled ? "scrolled" : ""}`}
>
<nav className="Premium-navbar">
<div className="navbar-container">
{}
<motion.div className="brand-container" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
<Link
className="brand-link"
to="/"
onClick={(e) => {
if (location.pathname === '/') {
e.preventDefault();
window.scrollTo({ top: 0, behavior: 'smooth' });
} else {
setTimeout(() => {
window.scrollTo({ top: 0, behavior: 'smooth' });
}, 100);
}
}}
>
<div className="logo-container">
<img src="MKCart-head.png" alt="MKCart Logo" className="logo-img" />
<div className="logo-glow"></div>
</div>
</Link>
</motion.div>
{}
<div className="desktop-nav">
{}
<div className="search-wrapper">
<Search />
</div>
{}
<div className="nav-items">
{}
<motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
<NavLink className="nav-item-Premium" to="/productlist">
<Package size={20} />
<span>Products</span>
<div className="nav-glow"></div>
</NavLink>
</motion.div>
{}
<NotificationCenter />
{}
<div className="wishlit-container">
<motion.button
className={`wishlit-btn${wishlistCount > 0 ? ' has-wishlist' : ''}`}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
onClick={() => {
if (!isLoggedIn) {
toast.error("You must be logged in to view wishlist!", { autoClose: 2000 });
setTimeout(() => navigate("/login"), 1500);
return;
}
navigate('/wishlist');
}}
>
<Heart size={20} color={wishlistCount > 0 ? '#ff4757' : undefined} fill={wishlistCount > 0 ? '#ff4757' : 'none'} />
{wishlistCount > 0 && (
<span className="wishlit-badge">{wishlistCount}</span>
)}
</motion.button>
</div>
{}
{isLoggedIn && userData ? (
<div className="user-menu" style={{ position: 'relative' }}>
<span className="user-name-gradient" style={{ marginRight: 16 }}>{userData.name}</span>
<button
className="user-avatar-btn"
style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer' }}
onClick={() => setShowUserDropdown((prev) => !prev)}
>
<div className="user-avagtar" style={{
width: 48,
height: 48,
borderRadius: '50%',
background: 'rgba(255,255,255,0.18)',
boxShadow: '0 4px 18px rgba(31,38,135,0.10)',
backdropFilter: 'blur(12px) saturate(180%)',
WebkitBackdropFilter: 'blur(12px) saturate(180%)',
border: '1.5px solid rgba(255,255,255,0.18)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<User size={28} />
</div>
<div className="user-glow"></div>
</button>
{showUserDropdown && (
<div className="custom-user-dropdown" style={{
position: 'absolute',
top: 'calc(100% + 8px)',
right: 0,
minWidth: 200,
background: 'rgba(130, 162, 183, 0.29)',
borderRadius: 16,
boxShadow: '0 8px 32px 0 rgba(31, 38, 135, 0.12)',
backdropFilter: 'blur(18px) saturate(180%)',
WebkitBackdropFilter: 'blur(18px) saturate(180%)',
border: '1.5px solid rgba(255, 255, 255, 0.18)',
zIndex: 100
}}>
<Link to="/profile" className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', textDecoration: 'none' }} onClick={() => setShowUserDropdown(false)}>
<User size={16} style={{ marginRight: 8 }} />
<span>Profile</span>
</Link>
<Link to="/wishlist" className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', textDecoration: 'none' }} onClick={() => setShowUserDropdown(false)}>
<Heart size={16} style={{ marginRight: 8 }} />
<span>Wishlist</span>
</Link>
<Link to="/orders" className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', textDecoration: 'none' }} onClick={() => setShowUserDropdown(false)}>
<Package size={16} style={{ marginRight: 8 }} />
<span>Orders</span>
</Link>
<Link to="/cart" className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', textDecoration: 'none' }} onClick={() => setShowUserDropdown(false)}>
<ShoppingCart size={16} style={{ marginRight: 8 }} />
<span>Cart</span>
</Link>
<Link to="/checkout" className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', textDecoration: 'none' }} onClick={() => setShowUserDropdown(false)}>
<CreditCard size={16} style={{ marginRight: 8 }} />
<span>Checkout</span>
</Link>
<div className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', cursor: 'pointer' }} onClick={() => { onOpenLiveChat && onOpenLiveChat(); setShowUserDropdown(false); }}>
<Gift size={16} style={{ marginRight: 8 }} />
<span>Chat With Us</span>
</div>
<div className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', cursor: 'pointer' }} onClick={() => { onOpenAIAssistant && onOpenAIAssistant(); setShowUserDropdown(false); }}>
<User size={16} style={{ marginRight: 8 }} />
<span>Assistant</span>
</div>
<div style={{ borderTop: '1px solid #444', margin: '4px 0' }} />
<div className="dropdown-item-Premium logout" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#ff4757', cursor: 'pointer' }} onClick={() => { logoutHandler(); setShowUserDropdown(false); }}>
<LogOut size={16} style={{ marginRight: 8 }} />
<span>Logout</span>
</div>
</div>
)}
</div>
) : (
<motion.div whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}>
<NavLink className="login-btn-Premium" to="/login">
<User size={18} />
<span>Login</span>
<div className="btn-shimmer"></div>
</NavLink>
</motion.div>
)}
{}
<motion.div className="cart-wrapper" whileHover={{ scale: 1.1 }} whileTap={{ scale: 0.9 }}>
<div
className="cart-link-Premium"
style={{ cursor: 'pointer' }}
onClick={() => {
if (!isLoggedIn) {
toast.error("You must be logged in to view cart!", { autoClose: 2000 });
setTimeout(() => navigate("/login"), 1500);
return;
}
navigate('/cart');
}}
>
<div className="cart-icon-container">
<ShoppingCart size={24} className="cart-icon" />
<AnimatePresence>
{cartItems.length > 0 && (
<motion.span
className="cart-badge-Premium"
key={cartItems.length}
initial={{ scale: 0, rotate: 180 }}
animate={{ scale: 1, rotate: 0 }}
exit={{ scale: 0, rotate: -180 }}
transition={{ type: "spring", stiffness: 500 }}
>
{cartItems.length}
</motion.span>
)}
</AnimatePresence>
<div className="cart-pulse"></div>
</div>
</div>
</motion.div>
</div>
</div>
{}
{}
</div>
{}
<AnimatePresence>
{isMobileMenuOpen && (
<motion.div
className="mobile-menu"
initial={{ opacity: 0, height: 0 }}
animate={{ opacity: 1, height: "auto" }}
exit={{ opacity: 0, height: 0 }}
transition={{ duration: 0.3 }}
>
<div className="mobile-menu-content">
<div className="mobile-search">
<Search />
</div>
<div className="mobile-nav-items">
<Link to="/productlist" className="mobile-nav-item" onClick={() => setIsMobileMenuOpen(false)}>
<Package size={20} />
<span>All Products</span>
</Link>
{isLoggedIn ? (
<>
<Link to="/profile" className="mobile-nav-item" onClick={() => setIsMobileMenuOpen(false)}>
<User size={20} />
<span>Profile</span>
</Link>
<button
className="mobile-nav-item"
onClick={() => {
logoutHandler()
setIsMobileMenuOpen(false)
}}
>
<LogOut size={20} />
<span>Logout</span>
</button>
</>
) : (
<Link to="/login" className="mobile-nav-item" onClick={() => setIsMobileMenuOpen(false)}>
<User size={20} />
<span>Login</span>
</Link>
)}
<div
className="mobile-nav-item"
style={{ cursor: 'pointer' }}
onClick={() => {
if (!isLoggedIn) {
toast.error("You must be logged in to view cart!", { autoClose: 2000 });
setTimeout(() => navigate("/login"), 1500);
setIsMobileMenuOpen(false);
return;
}
navigate('/cart');
setIsMobileMenuOpen(false);
}}
>
<ShoppingCart size={20} />
<span>Cart ({cartItems.length})</span>
</div>
</div>
</div>
</motion.div>
)}
</AnimatePresence>
{}
<div className="mobile-header-controls" style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
{isLoggedIn && userData && isMobileMenuOpen && (
<div className="user-menu">
<button
className="user-avatar-btn"
style={{ background: 'none', border: 'none', padding: 0, cursor: 'pointer' }}
onClick={() => setShowUserDropdown((prev) => !prev)}
>
<div className="user-avagtar" style={{
width: 40,
height: 40,
borderRadius: '50%',
background: 'rgba(90, 139, 158, 0.27)',
boxShadow: '0 4px 18px rgba(31,38,135,0.10)',
backdropFilter: 'blur(12px) saturate(180%)',
WebkitBackdropFilter: 'blur(12px) saturate(180%)',
border: '1.5px solid rgba(255,255,255,0.18)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}>
<User size={26} />
</div>
<div className="user-glow"></div>
</button>
{showUserDropdown && (
<div className="custom-user-dropdown" style={{
position: 'absolute',
top: 'calc(100% + 8px)',
right: 0,
minWidth: 200,
background: 'rgba(130, 162, 183, 0.29)',
borderRadius: 16,
boxShadow: '0 8px 32px 0 rgba(31, 38, 135, 0.12)',
backdropFilter: 'blur(18px) saturate(180%)',
WebkitBackdropFilter: 'blur(18px) saturate(180%)',
border: '1.5px solid rgba(255, 255, 255, 0.18)',
zIndex: 100
}}>
<Link to="/profile" className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', textDecoration: 'none' }} onClick={() => { setShowUserDropdown(false); setIsMobileMenuOpen(false); }}>
<User size={16} style={{ marginRight: 8 }} />
<span>Profile</span>
</Link>
<Link to="/wishlist" className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', textDecoration: 'none' }} onClick={() => { setShowUserDropdown(false); setIsMobileMenuOpen(false); }}>
<Heart size={16} style={{ marginRight: 8 }} />
<span>Wishlist</span>
</Link>
<Link to="/orders" className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', textDecoration: 'none' }} onClick={() => { setShowUserDropdown(false); setIsMobileMenuOpen(false); }}>
<Package size={16} style={{ marginRight: 8 }} />
<span>Orders</span>
</Link>
<Link to="/cart" className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', textDecoration: 'none' }} onClick={() => { setShowUserDropdown(false); setIsMobileMenuOpen(false); }}>
<ShoppingCart size={16} style={{ marginRight: 8 }} />
<span>Cart</span>
</Link>
<div className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', cursor: 'pointer' }} onClick={() => { onOpenLiveChat && onOpenLiveChat(); setShowUserDropdown(false); setIsMobileMenuOpen(false); }}>
<Gift size={16} style={{ marginRight: 8 }} />
<span>Chat With Us</span>
</div>
<div className="dropdown-item-Premium" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#fff', cursor: 'pointer' }} onClick={() => { onOpenAIAssistant && onOpenAIAssistant(); setShowUserDropdown(false); setIsMobileMenuOpen(false); }}>
<User size={16} style={{ marginRight: 8 }} />
<span>Assistant</span>
</div>
<div style={{ borderTop: '1px solid #444', margin: '4px 0' }} />
<div className="dropdown-item-Premium logout" style={{ display: 'flex', alignItems: 'center', padding: '10px 18px', color: '#ff4757', cursor: 'pointer' }} onClick={() => { logoutHandler(); setShowUserDropdown(false); setIsMobileMenuOpen(false); }}>
<LogOut size={16} style={{ marginRight: 8 }} />
<span>Logout</span>
</div>
</div>
)}
</div>
)}
<motion.button
className="mobile-menu-toggle"
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
whileHover={{ scale: 1.1 }}
whileTap={{ scale: 0.9 }}
>
<AnimatePresence mode="wait">
{isMobileMenuOpen ? (
<motion.div
key="close"
initial={{ rotate: -90, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }}
exit={{ rotate: 90, opacity: 0 }}
transition={{ duration: 0.2 }}
>
<X size={24} />
</motion.div>
) : (
<motion.div
key="menu"
initial={{ rotate: 90, opacity: 0 }}
animate={{ rotate: 0, opacity: 1 }}
exit={{ rotate: -90, opacity: 0 }}
transition={{ duration: 0.2 }}
>
<Menu size={24} />
</motion.div>
)}
</AnimatePresence>
</motion.button>
</div>
</nav>
</motion.header>
)
}