631 lines
29 KiB
TypeScript
631 lines
29 KiB
TypeScript
'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 (
|
||
<header
|
||
// Remove bottom border when admin subheader is present to avoid a blue line under the gold bar
|
||
className={`relative isolate z-10 shadow-lg shadow-black/30 after:pointer-events-none after:absolute after:inset-0 after:-z-10 after:bg-[radial-gradient(circle_at_20%_20%,rgba(56,124,255,0.18),transparent_55%),radial-gradient(circle_at_80%_35%,rgba(139,92,246,0.16),transparent_60%)] ${isAdmin ? '' : 'border-b border-white/10'}`}
|
||
style={{
|
||
background: 'linear-gradient(135deg, #0F1D37 0%, #0A162A 50%, #081224 100%)',
|
||
}}
|
||
>
|
||
<nav aria-label="Global" className="mx-auto flex max-w-7xl items-center justify-between p-6 lg:px-8">
|
||
<div className="flex lg:flex-1">
|
||
<button
|
||
onClick={() => router.push('/')}
|
||
className="p-2 flex items-center gap-3 max-w-full lg:-m-1.5 lg:gap-0"
|
||
>
|
||
<span className="sr-only">ProfitPlanet</span>
|
||
<Image
|
||
src="/images/logos/pp_logo_gold_transparent.png"
|
||
alt="ProfitPlanet Logo"
|
||
width={280}
|
||
height={84}
|
||
className="h-14 w-auto flex-shrink-0 sm:h-16 lg:h-[4.5rem]"
|
||
/>
|
||
{/* Removed flickering mobile heading (now only shown inside the sliding panel) */}
|
||
</button>
|
||
</div>
|
||
<div className="flex lg:hidden">
|
||
<button
|
||
type="button"
|
||
onClick={() => setMobileMenuOpen(true)}
|
||
aria-expanded={mobileMenuOpen}
|
||
className="-m-2.5 inline-flex items-center justify-center rounded-md p-2.5 text-gray-400 transition-transform duration-300 ease-out data-[open=true]:rotate-90"
|
||
data-open={mobileMenuOpen ? 'true' : 'false'}
|
||
>
|
||
<span className="sr-only">Open main menu</span>
|
||
<Bars3Icon aria-hidden="true" className="size-6" />
|
||
</button>
|
||
</div>
|
||
<PopoverGroup className="hidden lg:flex lg:gap-x-12">
|
||
{/* Shop dropdown stays first */}
|
||
<Popover>
|
||
<PopoverButton className="flex items-center gap-x-1 text-sm/6 font-semibold text-gray-900 dark:text-white">
|
||
Shop
|
||
<ChevronDownIcon aria-hidden="true" className="size-5 flex-none text-gray-500" />
|
||
</PopoverButton>
|
||
{/* ...existing Shop PopoverPanel... */}
|
||
<PopoverPanel
|
||
transition
|
||
className="absolute left-0 right-0 top-full z-50 rounded-b-2xl shadow-xl shadow-black/40 ring-1 ring-white/10 dark:ring-white/15 overflow-hidden data-closed:-translate-y-1 data-closed:opacity-0 data-enter:duration-200 data-enter:ease-out data-leave:duration-150 data-leave:ease-in"
|
||
style={{
|
||
background: 'linear-gradient(150deg, rgba(26,46,84,0.95) 0%, rgba(18,37,70,0.92) 45%, rgba(30,56,104,0.88) 100%)',
|
||
backdropFilter: 'blur(26px) saturate(175%)',
|
||
WebkitBackdropFilter: 'blur(26px) saturate(175%)'
|
||
}}
|
||
>
|
||
<div className="relative before:absolute before:inset-0 before:pointer-events-none before:bg-[radial-gradient(circle_at_18%_30%,rgba(56,124,255,0.30),transparent_62%),radial-gradient(circle_at_82%_40%,rgba(139,92,246,0.22),transparent_65%)]">
|
||
<div className="mx-auto grid max-w-7xl grid-cols-2 md:grid-cols-3 gap-x-4 px-6 py-10 lg:px-8 xl:gap-x-8">
|
||
{shopItems.map(item => (
|
||
<div
|
||
key={item.name}
|
||
className="group relative rounded-lg p-6 text-sm/6 hover:bg-white/5 transition-colors"
|
||
>
|
||
<div className="flex size-11 items-center justify-center rounded-lg bg-white/10 backdrop-blur-md group-hover:bg-white/20 transition-colors">
|
||
<item.icon aria-hidden="true" className="size-6 text-gray-300 group-hover:text-white" />
|
||
</div>
|
||
<button
|
||
onClick={() => router.push(item.href)}
|
||
className="mt-6 block font-semibold text-white"
|
||
>
|
||
{item.name}
|
||
<span className="absolute inset-0" />
|
||
</button>
|
||
<p className="mt-1 text-gray-300">{item.description}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</PopoverPanel>
|
||
</Popover>
|
||
|
||
{/* Affiliate Links */}
|
||
<button
|
||
onClick={() => router.push('/affiliate-links')}
|
||
className="text-sm/6 font-semibold text-gray-900 dark:text-white hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors"
|
||
>
|
||
Affiliate-Links
|
||
</button>
|
||
|
||
{/* Memberships */}
|
||
<button
|
||
onClick={() => router.push('/memberships')}
|
||
className="text-sm/6 font-semibold text-gray-900 dark:text-white hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors"
|
||
>
|
||
Memberships
|
||
</button>
|
||
|
||
{/* Referral Management - match others (no highlight) */}
|
||
{userPresent && hasReferralPerm && (
|
||
<button
|
||
onClick={() => { console.log('🧭 Header: navigate to /referral-management'); router.push('/referral-management') }}
|
||
className="text-sm/6 font-semibold text-gray-900 dark:text-white hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors"
|
||
>
|
||
Referral Management
|
||
</button>
|
||
)}
|
||
|
||
{/* About us */}
|
||
<button
|
||
onClick={() => router.push('/about-us')}
|
||
className="text-sm/6 font-semibold text-gray-900 dark:text-white hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors"
|
||
>
|
||
About us
|
||
</button>
|
||
</PopoverGroup>
|
||
<div className="hidden lg:flex lg:flex-1 lg:justify-end lg:items-center lg:gap-x-4">
|
||
{/* Stable auth slot to avoid SSR/CSR structural drift */}
|
||
<div className="flex items-center">
|
||
{userPresent ? (
|
||
<Popover className="relative">
|
||
<PopoverButton className="flex items-center gap-x-1 text-sm font-semibold text-gray-900 dark:text-white">
|
||
<Avatar
|
||
src=""
|
||
initials={(() => {
|
||
if (!user) return 'U'
|
||
if (user.firstName || user.lastName) {
|
||
return ((user.firstName?.[0] || '') + (user.lastName?.[0] || '')).toUpperCase()
|
||
}
|
||
return user.email ? user.email[0].toUpperCase() : 'U'
|
||
})()}
|
||
className="size-8 bg-gradient-to-br from-indigo-500/40 to-indigo-600/60 text-white"
|
||
/>
|
||
<ChevronDownIcon aria-hidden="true" className="size-5 flex-none text-gray-500" />
|
||
</PopoverButton>
|
||
<PopoverPanel
|
||
transition
|
||
className="absolute left-0 top-full mt-2 w-64 rounded-md bg-white dark:bg-gray-900 ring-1 ring-black/10 dark:ring-white/10 shadow-lg data-closed:-translate-y-1 data-closed:opacity-0 data-enter:duration-200 data-leave:duration-150"
|
||
>
|
||
<div className="p-4">
|
||
<div className="flex flex-col border-b border-gray-200 dark:border-white/10 pb-4 mb-4">
|
||
<div className="font-medium text-gray-900 dark:text-white">
|
||
{user?.firstName && user?.lastName ? `${user.firstName} ${user.lastName}` : (user?.email || 'User')}
|
||
</div>
|
||
<div className="text-sm text-gray-500 dark:text-gray-400">
|
||
{user?.email || 'user@example.com'}
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={() => router.push('/profile')}
|
||
className="flex items-center gap-x-2 w-full text-left p-2 text-sm text-gray-800 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-white/5 rounded-md"
|
||
>
|
||
<UserCircleIcon className="size-5 text-gray-400" />
|
||
Profile
|
||
</button>
|
||
<button
|
||
onClick={handleLogout}
|
||
className="flex items-center gap-x-2 w-full text-left p-2 text-sm text-gray-800 dark:text-gray-200 hover:bg-gray-100 dark:hover:bg-white/5 rounded-md"
|
||
>
|
||
<ArrowRightOnRectangleIcon className="size-5 text-gray-400" />
|
||
Logout
|
||
</button>
|
||
</div>
|
||
</PopoverPanel>
|
||
</Popover>
|
||
) : mounted ? (
|
||
<button
|
||
onClick={() => router.push('/login')}
|
||
className="text-sm/6 font-semibold text-gray-900 dark:text-white hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors"
|
||
>
|
||
Log in <span aria-hidden="true">→</span>
|
||
</button>
|
||
) : (
|
||
<div aria-hidden="true" className="w-20 h-8 rounded-md bg-gray-200 dark:bg-gray-700/70 animate-pulse" />
|
||
)}
|
||
</div>
|
||
{/* Language & theme remain after auth slot */}
|
||
<LanguageSwitcher variant={isDark ? 'dark' : 'light'} />
|
||
<button
|
||
onClick={toggleTheme}
|
||
aria-label="Toggle theme"
|
||
className="p-2 rounded-md border border-gray-300 dark:border-white/10 bg-gray-100 hover:bg-gray-200 dark:bg-gray-800 dark:hover:bg-gray-700 text-gray-700 dark:text-gray-200 transition-colors"
|
||
>
|
||
{isDark ? <SunIcon className="h-5 w-5" /> : <MoonIcon className="h-5 w-5" />}
|
||
</button>
|
||
</div>
|
||
</nav>
|
||
|
||
{/* Admin subheader (gold) - centered */}
|
||
{userPresent && isAdmin && (
|
||
<div
|
||
className="w-full border-t border-amber-700/40"
|
||
style={{ background: 'linear-gradient(90deg, #D4AF37 0%, #C99A2E 100%)' }}
|
||
>
|
||
<div className="mx-auto max-w-7xl px-6 lg:px-8">
|
||
<div className="flex flex-wrap items-center justify-center gap-5 py-2">
|
||
<span className="text-xs font-semibold uppercase tracking-wide text-[#3B2C04]/80">
|
||
Admin Navigation
|
||
</span>
|
||
<button
|
||
onClick={() => { console.log('🧭 Admin: navigate to /admin'); router.push('/admin') }}
|
||
className="text-sm font-semibold text-[#0F1D37] hover:text-[#7A5E1A]"
|
||
>
|
||
Dashboard
|
||
</button>
|
||
<button
|
||
onClick={() => { console.log('🧭 Admin: navigate to /admin/user-management'); router.push('/admin/user-management') }}
|
||
className="text-sm font-semibold text-[#0F1D37] hover:text-[#7A5E1A]"
|
||
>
|
||
User Management
|
||
</button>
|
||
<button
|
||
onClick={() => { console.log('🧭 Admin: navigate to /admin/user-verify'); router.push('/admin/user-verify') }}
|
||
className="text-sm font-semibold text-[#0F1D37] hover:text-[#7A5E1A]"
|
||
>
|
||
User Verify
|
||
</button>
|
||
{/* NEW: Matrix Management link */}
|
||
<button
|
||
onClick={() => { console.log('🧭 Admin: navigate to /admin/matrix-management'); router.push('/admin/matrix-management') }}
|
||
className="text-sm font-semibold text-[#0F1D37] hover:text-[#7A5E1A]"
|
||
>
|
||
Matrix Management
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
{/* Mobile dialog and rest of header */}
|
||
<Dialog open={mobileMenuOpen} onClose={setMobileMenuOpen} className="lg:hidden">
|
||
<Transition
|
||
appear
|
||
show={mobileMenuOpen}
|
||
>
|
||
<Transition.Child
|
||
enter="transition-opacity duration-300 ease-out"
|
||
enterFrom="opacity-0"
|
||
enterTo="opacity-100"
|
||
leave="transition-opacity duration-200 ease-in"
|
||
leaveFrom="opacity-100"
|
||
leaveTo="opacity-0"
|
||
>
|
||
<div className="fixed inset-0 z-40 bg-black/40 backdrop-blur-sm" />
|
||
</Transition.Child>
|
||
|
||
{/* Sliding panel */}
|
||
<Transition.Child
|
||
enter="transform transition-transform duration-300 ease-out"
|
||
enterFrom="translate-x-full"
|
||
enterTo="translate-x-0"
|
||
leave="transform transition-transform duration-250 ease-in"
|
||
leaveFrom="translate-x-0"
|
||
leaveTo="translate-x-full"
|
||
>
|
||
<DialogPanel className="fixed inset-y-0 right-0 z-50 w-full sm:max-w-sm h-full overflow-y-auto overflow-x-hidden bg-white dark:bg-gray-900 p-5 sm:ring-1 sm:ring-black/10 dark:sm:ring-gray-100/10">
|
||
<div className="flex items-center justify-between">
|
||
<button
|
||
onClick={() => router.push('/')}
|
||
className="p-1.5 flex items-center gap-3"
|
||
>
|
||
<span className="sr-only">ProfitPlanet</span>
|
||
<Image
|
||
src="/images/logos/pp_logo_gold_transparent.png"
|
||
alt="ProfitPlanet Logo"
|
||
width={190}
|
||
height={60}
|
||
className="h-12 w-auto flex-shrink-0"
|
||
/>
|
||
<span className="text-xl font-bold tracking-tight text-[#D4AF37]">
|
||
Profit Planet
|
||
</span>
|
||
</button>
|
||
<button
|
||
type="button"
|
||
onClick={() => setMobileMenuOpen(false)}
|
||
className="rounded-md p-2.5 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200 transition-transform duration-300 hover:scale-110"
|
||
>
|
||
<span className="sr-only">Close menu</span>
|
||
<XMarkIcon aria-hidden="true" className="size-6" />
|
||
</button>
|
||
</div>
|
||
<div className="mt-6 flow-root">
|
||
<div className="-my-6 divide-y divide-gray-200 dark:divide-white/10">
|
||
{!mounted ? (
|
||
<div className="py-6 px-3">
|
||
<div className="h-28 w-full rounded-lg bg-gray-200 dark:bg-gray-700 animate-pulse" />
|
||
</div>
|
||
) : user ? (
|
||
<>
|
||
{/* User info now FIRST under logo */}
|
||
<div className="pt-6 space-y-2">
|
||
<div className="flex flex-col border-b border-white/10 pb-4 mb-4 px-3">
|
||
<div className="font-medium text-white">
|
||
{user?.firstName && user?.lastName ? `${user.firstName} ${user.lastName}` : (user?.email || 'User')}
|
||
</div>
|
||
<div className="text-sm text-gray-400">
|
||
{user?.email || 'user@example.com'}
|
||
</div>
|
||
</div>
|
||
<button
|
||
onClick={() => { router.push('/profile'); setMobileMenuOpen(false); }}
|
||
className="block rounded-lg px-3 py-2 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5 w-full text-left"
|
||
>
|
||
Profile
|
||
</button>
|
||
<button
|
||
onClick={() => { handleLogout(); setMobileMenuOpen(false); }}
|
||
className="block rounded-lg px-3 py-2 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5 w-full text-left"
|
||
>
|
||
Logout
|
||
</button>
|
||
</div>
|
||
{/* Theme + Language now AFTER user info */}
|
||
<div className="py-6">
|
||
<button
|
||
onClick={toggleTheme}
|
||
className="flex items-center gap-x-2 rounded-lg px-3 py-2.5 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-gray-100 dark:hover:bg-white/5 w-full"
|
||
>
|
||
{isDark ? <SunIcon className="h-5 w-5" /> : <MoonIcon className="h-5 w-5" />}
|
||
{isDark ? 'Light Mode' : 'Dark Mode'}
|
||
</button>
|
||
<div className="mt-4 px-1">
|
||
<LanguageSwitcher variant="dark" />
|
||
</div>
|
||
</div>
|
||
{/* Navigation / Shop after that */}
|
||
<div className="space-y-2 py-6">
|
||
<Disclosure as="div">
|
||
<DisclosureButton className="group flex w-full items-center justify-between rounded-lg py-2 px-3 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5">
|
||
Shop
|
||
<ChevronDownIcon aria-hidden="true" className="size-5 flex-none group-data-open:rotate-180" />
|
||
</DisclosureButton>
|
||
<DisclosurePanel className="mt-2 space-y-1">
|
||
{shopItems.map(item => (
|
||
<DisclosureButton
|
||
key={item.name}
|
||
as="button"
|
||
onClick={() => { 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}
|
||
</DisclosureButton>
|
||
))}
|
||
</DisclosurePanel>
|
||
</Disclosure>
|
||
{/* Affiliate Links */}
|
||
<button
|
||
onClick={() => { router.push('/affiliate-links'); setMobileMenuOpen(false); }}
|
||
className="block rounded-lg px-3 py-2 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5 w-full text-left"
|
||
>
|
||
Affiliate-Links
|
||
</button>
|
||
{/* Memberships */}
|
||
<button
|
||
onClick={() => { router.push('/memberships'); setMobileMenuOpen(false); }}
|
||
className="block rounded-lg px-3 py-2 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5 w-full text-left"
|
||
>
|
||
Memberships
|
||
</button>
|
||
{/* Referral Management - match others (no highlight) */}
|
||
{hasReferralPerm && (
|
||
<button
|
||
onClick={() => { console.log('🧭 Header Mobile: navigate to /referral-management'); router.push('/referral-management'); setMobileMenuOpen(false); }}
|
||
className="block rounded-lg px-3 py-2 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5 w-full text-left"
|
||
>
|
||
Referral Management
|
||
</button>
|
||
)}
|
||
{/* About us */}
|
||
<button
|
||
onClick={() => { router.push('/about-us'); setMobileMenuOpen(false); }}
|
||
className="block rounded-lg px-3 py-2 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5 w-full text-left"
|
||
>
|
||
About us
|
||
</button>
|
||
</div>
|
||
</>
|
||
) : (
|
||
<div className="py-6 space-y-4">
|
||
<Disclosure as="div">
|
||
<DisclosureButton className="group flex w-full items-center justify-between rounded-lg py-2 px-3 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5">
|
||
Shop
|
||
<ChevronDownIcon aria-hidden="true" className="size-5 flex-none group-data-open:rotate-180" />
|
||
</DisclosureButton>
|
||
<DisclosurePanel className="mt-2 space-y-1">
|
||
{shopItems.map(item => (
|
||
<DisclosureButton
|
||
key={item.name}
|
||
as="button"
|
||
onClick={() => { 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}
|
||
</DisclosureButton>
|
||
))}
|
||
</DisclosurePanel>
|
||
</Disclosure>
|
||
<button
|
||
onClick={() => { router.push('/affiliate-links'); setMobileMenuOpen(false); }}
|
||
className="block rounded-lg px-3 py-2 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5 w-full text-left"
|
||
>
|
||
Affiliate-Links
|
||
</button>
|
||
<button
|
||
onClick={() => { router.push('/memberships'); setMobileMenuOpen(false); }}
|
||
className="block rounded-lg px-3 py-2 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5 w-full text-left"
|
||
>
|
||
Memberships
|
||
</button>
|
||
<button
|
||
onClick={() => { router.push('/about-us'); setMobileMenuOpen(false); }}
|
||
className="block rounded-lg px-3 py-2 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5 w-full text-left"
|
||
>
|
||
About us
|
||
</button>
|
||
<div className="px-3">
|
||
<button
|
||
onClick={() => { router.push('/login'); setMobileMenuOpen(false); }}
|
||
className="block rounded-lg px-3 py-2.5 text-base/7 font-semibold text-gray-900 dark:text-white hover:bg-white/5 w-full text-left"
|
||
>
|
||
Log in
|
||
</button>
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
</DialogPanel>
|
||
</Transition.Child>
|
||
</Transition>
|
||
</Dialog>
|
||
</header>
|
||
)
|
||
} |