import { useState, useEffect, useRef, useCallback } from "react"; import { useNavigate, useLocation } from "react-router-dom"; import { useCart } from "../context/useCart"; import { useTheme } from "../context/ThemeContext"; import "./Navbar.css"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { faHome, faBox, faShoppingCart, faTruck, faClockRotateLeft, faSignOutAlt, faBars, faTimes, faBell, faGift, faUserCircle, faSun, faMoon, } from "@fortawesome/free-solid-svg-icons"; import { faTelegram } from "@fortawesome/free-brands-svg-icons"; import type { IconDefinition } from "@fortawesome/fontawesome-svg-core"; import { getClientNotifications, markNotificationsRead, getPublicSettings } from "../api/api"; import type { ClientNotification } from "../api/api"; interface MenuItem { id: string; label: string; icon: IconDefinition; path: string; } function Navbar() { const { theme, toggleTheme } = useTheme(); const [isMenuOpen, setIsMenuOpen] = useState(false); const [isLoggingOut, setIsLoggingOut] = useState(false); const [notifications, setNotifications] = useState([]); const [unreadCount, setUnreadCount] = useState(0); const [showNotifPanel, setShowNotifPanel] = useState(false); const [referralEnabled, setReferralEnabled] = useState(true); const [shopName, setShopName] = useState("Milieu-Nantais"); const seenKeysRef = useRef>(new Set()); const isFirstLoadRef = useRef(true); const notifPanelRef = useRef(null); const { cartCount } = useCart(); const navigate = useNavigate(); const location = useLocation(); const fetchNotifications = useCallback(async () => { const res = await getClientNotifications(); if (!res.success) return; setNotifications(res.notifications); setUnreadCount(res.unread_count); if (!isFirstLoadRef.current) { for (const n of res.notifications) { if (n.read) continue; const key = `${n.command_id}-${n.type}-${n.created_at}`; if (!seenKeysRef.current.has(key)) { seenKeysRef.current.add(key); } } } else { for (const n of res.notifications) { seenKeysRef.current.add(`${n.command_id}-${n.type}-${n.created_at}`); } isFirstLoadRef.current = false; } }, []); useEffect(() => { fetchNotifications(); const interval = setInterval(fetchNotifications, 15000); return () => clearInterval(interval); }, [fetchNotifications]); useEffect(() => { getPublicSettings().then((s) => { setReferralEnabled(s.referral_enabled); if (s.shop_name) setShopName(s.shop_name); }); }, []); useEffect(() => { const handleClickOutside = (e: MouseEvent) => { if (notifPanelRef.current && !notifPanelRef.current.contains(e.target as Node)) { setShowNotifPanel(false); } }; document.addEventListener("mousedown", handleClickOutside); return () => document.removeEventListener("mousedown", handleClickOutside); }, []); const handleNotifBellClick = async () => { setShowNotifPanel((prev) => !prev); if (!showNotifPanel && unreadCount > 0) { await markNotificationsRead(); setUnreadCount(0); setNotifications((prev) => prev.map((n) => ({ ...n, read: true }))); } }; const menuItems: MenuItem[] = [ { id: "accueil", label: "Accueil", icon: faHome, path: "/user/accueil" }, { id: "produits", label: "Nos Produits", icon: faBox, path: "/user/nos-produits" }, { id: "panier", label: "Mon Panier", icon: faShoppingCart, path: "/user/panier" }, { id: "suivi", label: "Suivi Livraison", icon: faTruck, path: "/user/suivi-livraison" }, { id: "historique", label: "Historique", icon: faClockRotateLeft, path: "/user/consultation-historique" }, ...(referralEnabled ? [{ id: "parrainage", label: "Parrainage", icon: faGift, path: "/user/parrainage" } as MenuItem] : []), { id: "profil", label: "Mon Profil", icon: faUserCircle, path: "/user/profil" }, ]; const toggleMenu = () => setIsMenuOpen((v) => !v); const closeMenu = () => setIsMenuOpen(false); const handleNavigation = (path: string) => { navigate(path); closeMenu(); }; const handleLogout = async () => { if (isLoggingOut) return; setIsLoggingOut(true); try { const token = sessionStorage.getItem("admin_token"); if (token) { try { await fetch("/api/v2/admin/auth/logout", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, }); } catch { /* noop */ } } sessionStorage.removeItem("admin_token"); sessionStorage.removeItem("admin_username"); navigate("/login/client", { replace: true }); } catch { sessionStorage.removeItem("admin_token"); sessionStorage.removeItem("admin_username"); navigate("/login/client", { replace: true }); } finally { setIsLoggingOut(false); } }; const handleTelegram = () => window.open("https://t.me/milieu_nantais", "_blank"); return ( <> {/* ── Top Bar ─────────────────────────────────── */}
Milieu‑Nantais
{/* Theme toggle */} {/* Notifications */}
{showNotifPanel && (
Notifications
{notifications.length === 0 ? (
Aucune notification
) : (
    {notifications.map((n, i) => (
  • {n.message} {new Date(n.created_at).toLocaleTimeString("fr-FR", { hour: "2-digit", minute: "2-digit", })}
  • ))}
)}
)}
{/* Panier */}
{/* ── Overlay ─────────────────────────────────── */} {isMenuOpen &&
} {/* ── Sidebar ─────────────────────────────────── */} ); } export default Navbar;