feat: referral Management nav in Header if permission there

This commit is contained in:
DeathKaioken 2025-10-16 07:56:46 +02:00
parent d0bf865552
commit 09083e066a

View File

@ -48,8 +48,13 @@ export default function Header() {
const [mounted, setMounted] = useState(false) // added const [mounted, setMounted] = useState(false) // added
const user = useAuthStore((s) => s.user); const user = useAuthStore((s) => s.user);
const logout = useAuthStore((s) => s.logout); const logout = useAuthStore((s) => s.logout);
const accessToken = useAuthStore((s) => s.accessToken);
const refreshAuthToken = useAuthStore((s) => s.refreshAuthToken);
const router = useRouter(); const router = useRouter();
// NEW: permission flag
const [hasReferralPerm, setHasReferralPerm] = useState(false)
const handleLogout = async () => { const handleLogout = async () => {
try { try {
await logout(); await logout();
@ -101,6 +106,84 @@ export default function Header() {
}) })
}, []) }, [])
// 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 isLoggedIn = !!user
const userPresent = mounted && isLoggedIn const userPresent = mounted && isLoggedIn
@ -145,12 +228,13 @@ export default function Header() {
</button> </button>
</div> </div>
<PopoverGroup className="hidden lg:flex lg:gap-x-12"> <PopoverGroup className="hidden lg:flex lg:gap-x-12">
{/* Shop dropdown stays first */}
<Popover> <Popover>
<PopoverButton className="flex items-center gap-x-1 text-sm/6 font-semibold text-gray-900 dark:text-white"> <PopoverButton className="flex items-center gap-x-1 text-sm/6 font-semibold text-gray-900 dark:text-white">
Shop Shop
<ChevronDownIcon aria-hidden="true" className="size-5 flex-none text-gray-500" /> <ChevronDownIcon aria-hidden="true" className="size-5 flex-none text-gray-500" />
</PopoverButton> </PopoverButton>
{/* Redesigned mega menu panel */} {/* ...existing Shop PopoverPanel... */}
<PopoverPanel <PopoverPanel
transition 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" 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"
@ -185,16 +269,39 @@ export default function Header() {
</PopoverPanel> </PopoverPanel>
</Popover> </Popover>
{navLinks.map(l => ( {/* Affiliate Links */}
<button <button
key={l.name} onClick={() => router.push('/affiliate-links')}
onClick={() => router.push(l.href)}
className="text-sm/6 font-semibold text-gray-900 dark:text-white hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors" className="text-sm/6 font-semibold text-gray-900 dark:text-white hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors"
> >
{l.name} 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> </button>
))}
{/* Removed user profile Popover from here (now on right side) */}
</PopoverGroup> </PopoverGroup>
<div className="hidden lg:flex lg:flex-1 lg:justify-end lg:items-center lg:gap-x-4"> <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 */} {/* Stable auth slot to avoid SSR/CSR structural drift */}
@ -267,6 +374,12 @@ export default function Header() {
</button> </button>
</div> </div>
</nav> </nav>
{/* REMOVE sub header bar */}
{/* ...existing code... */}
{/* Deleted previous Sub header nav for Referral Management */}
{/* ...existing code... */}
<Dialog open={mobileMenuOpen} onClose={setMobileMenuOpen} className="lg:hidden"> <Dialog open={mobileMenuOpen} onClose={setMobileMenuOpen} className="lg:hidden">
<Transition <Transition
appear appear
@ -383,15 +496,36 @@ export default function Header() {
))} ))}
</DisclosurePanel> </DisclosurePanel>
</Disclosure> </Disclosure>
{navLinks.map(link => ( {/* Affiliate Links */}
<button <button
key={link.name} onClick={() => { router.push('/affiliate-links'); setMobileMenuOpen(false); }}
onClick={() => { router.push(link.href); 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" 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"
> >
{link.name} 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> </button>
))}
</div> </div>
</> </>
) : ( ) : (
@ -414,15 +548,24 @@ export default function Header() {
))} ))}
</DisclosurePanel> </DisclosurePanel>
</Disclosure> </Disclosure>
{navLinks.map(link => (
<button <button
key={link.name} onClick={() => { router.push('/affiliate-links'); setMobileMenuOpen(false); }}
onClick={() => { router.push(link.href); 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" 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"
> >
{link.name} 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> </button>
))}
<div className="px-3"> <div className="px-3">
<button <button
onClick={() => { router.push('/login'); setMobileMenuOpen(false); }} onClick={() => { router.push('/login'); setMobileMenuOpen(false); }}