'use client' import { useState, useEffect, useCallback } from 'react' import { useRouter } from 'next/navigation'; import Image from 'next/image'; import { Dialog, DialogPanel, Disclosure, DisclosureButton, DisclosurePanel, Popover, PopoverButton, PopoverGroup, PopoverPanel, Transition } from '@headlessui/react' import { Bars3Icon, ShoppingBagIcon, UsersIcon, UserCircleIcon, XMarkIcon, ArrowRightOnRectangleIcon, MoonIcon, SunIcon } from '@heroicons/react/24/outline' import { ChevronDownIcon } from '@heroicons/react/20/solid' import useAuthStore from '../../store/authStore'; import { Avatar } from '../avatar'; import LanguageSwitcher from '../LanguageSwitcher'; // Replace current shopItems definition with detailed version (adds icon & description) const shopItems = [ { name: 'VIP', href: '/shop/vip', description: 'Exclusive VIP shop', icon: ShoppingBagIcon }, { name: 'Public', href: '/shop/public', description: 'Open catalog for everyone', icon: UsersIcon }, ]; const navLinks = [ { name: 'Affiliate-Links', href: '/affiliate-links' }, { name: 'Memberships', href: '/memberships' }, { name: 'About us', href: '/about-us' }, ]; export default function Header() { const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const [isDark, setIsDark] = useState(false) const [mounted, setMounted] = useState(false) // added const user = useAuthStore((s) => s.user); const logout = useAuthStore((s) => s.logout); const accessToken = useAuthStore((s) => s.accessToken); const refreshAuthToken = useAuthStore((s) => s.refreshAuthToken); const router = useRouter(); // NEW: permission flag const [hasReferralPerm, setHasReferralPerm] = useState(false) const handleLogout = async () => { try { await logout(); router.push('/login'); } catch (err) { console.error('Logout failed:', err); } }; // Helper to get user initials for profile icon const getUserInitials = () => { if (!user) return 'U'; if (user.firstName || user.lastName) { return ( (user.firstName?.[0] || '') + (user.lastName?.[0] || '') ).toUpperCase(); } if (user.email) { return user.email[0].toUpperCase(); } return 'U'; }; // Theme initialization & persistence useEffect(() => { const stored = localStorage.getItem('theme') if (stored === 'dark' || (!stored && window.matchMedia('(prefers-color-scheme: dark)').matches)) { document.documentElement.classList.add('dark') setIsDark(true) } else { document.documentElement.classList.remove('dark') setIsDark(false) } setMounted(true) // hydration complete }, []) const toggleTheme = useCallback(() => { setIsDark(prev => { const next = !prev if (next) { document.documentElement.classList.add('dark') localStorage.setItem('theme', 'dark') } else { document.documentElement.classList.remove('dark') localStorage.setItem('theme', 'light') } return next }) }, []) // Fetch user permissions and set hasReferralPerm useEffect(() => { let cancelled = false const fetchPermissions = async () => { if (!mounted) { console.log('⏸️ Header: not mounted yet, skipping permissions fetch') return } if (!user) { console.log('ℹ️ Header: no user, clearing permission flag') if (!cancelled) setHasReferralPerm(false) return } const uid = (user as any)?.id ?? (user as any)?._id ?? (user as any)?.userId if (!uid) { console.warn('⚠️ Header: user id missing, cannot fetch permissions', user) if (!cancelled) setHasReferralPerm(false) return } const base = process.env.NEXT_PUBLIC_API_BASE_URL || '' const url = `${base}/api/users/${uid}/permissions` console.log('🌐 Header: fetching permissions:', { url, uid }) // Ensure we have a token (try refresh if needed) let tokenToUse = accessToken try { if (!tokenToUse && refreshAuthToken) { const ok = await refreshAuthToken() if (ok) tokenToUse = useAuthStore.getState().accessToken } } catch (e) { console.error('❌ Header: refreshAuthToken error:', e) } try { const res = await fetch(url, { method: 'GET', credentials: 'include', headers: { 'Content-Type': 'application/json', ...(tokenToUse ? { Authorization: `Bearer ${tokenToUse}` } : {}) } }) console.log('📡 Header: permissions status:', res.status) const body = await res.json().catch(() => null) console.log('📦 Header: permissions body:', body) // Try common shapes const permsSrc = body?.data?.permissions ?? body?.permissions ?? body let can = false if (Array.isArray(permsSrc)) { // Could be array of strings or objects can = permsSrc.includes?.('can_create_referrals') || permsSrc.some?.((p: any) => p?.name === 'can_create_referrals' || p?.key === 'can_create_referrals') } else if (permsSrc && typeof permsSrc === 'object') { can = !!permsSrc.can_create_referrals } console.log('✅ Header: can_create_referrals =', can) if (!cancelled) setHasReferralPerm(!!can) } catch (e) { console.error('❌ Header: fetch permissions error:', e) if (!cancelled) setHasReferralPerm(false) } } fetchPermissions() return () => { cancelled = true } }, [mounted, user, accessToken, refreshAuthToken]) const isLoggedIn = !!user const userPresent = mounted && isLoggedIn // NEW: detect admin role across common shapes const isAdmin = !!user && ( (user as any)?.role === 'admin' || (user as any)?.userType === 'admin' || (user as any)?.isAdmin === true || ((user as any)?.roles?.includes?.('admin')) ) return (
{/* Admin subheader (gold) - centered */} {userPresent && isAdmin && (
Admin Navigation {/* NEW: Matrix Management link */}
)} {/* Mobile dialog and rest of header */}
{/* Sliding panel */}
{!mounted ? (
) : user ? ( <> {/* User info now FIRST under logo */}
{user?.firstName && user?.lastName ? `${user.firstName} ${user.lastName}` : (user?.email || 'User')}
{user?.email || 'user@example.com'}
{/* Theme + Language now AFTER user info */}
{/* Navigation / Shop after that */}
Shop {shopItems.map(item => ( { router.push(item.href); setMobileMenuOpen(false); }} className="block rounded-lg py-2 pl-6 pr-3 text-sm/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5 w-full text-left" > {item.name} ))} {/* Affiliate Links */} {/* Memberships */} {/* Referral Management - match others (no highlight) */} {hasReferralPerm && ( )} {/* About us */}
) : (
Shop {shopItems.map(item => ( { router.push(item.href); setMobileMenuOpen(false); }} className="block rounded-lg py-2 pl-6 pr-3 text-sm/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5 w-full text-left" > {item.name} ))}
)}
) }