179 lines
7.1 KiB
TypeScript
179 lines
7.1 KiB
TypeScript
'use client';
|
|
import { useEffect, useState } from 'react';
|
|
import { useDispatch, useSelector } from 'react-redux';
|
|
import Link from 'next/link';
|
|
import { IRootState } from '@/store';
|
|
import { toggleTheme, toggleSidebar, toggleRTL } from '@/store/themeConfigSlice';
|
|
import Image from 'next/image';
|
|
import Dropdown from '@/components/dropdown';
|
|
import IconMenu from '@/components/icon/icon-menu';
|
|
import IconSun from '@/components/icon/icon-sun';
|
|
import IconMoon from '@/components/icon/icon-moon';
|
|
import IconUser from '@/components/icon/icon-user';
|
|
import IconMail from '@/components/icon/icon-mail';
|
|
import IconLockDots from '@/components/icon/icon-lock-dots';
|
|
import IconLogout from '@/components/icon/icon-logout';
|
|
import { usePathname, useRouter } from 'next/navigation';
|
|
|
|
type UserData = { id: string; email: string; createdAt: string };
|
|
|
|
export default function Header() {
|
|
const pathname = usePathname();
|
|
const dispatch = useDispatch();
|
|
const router = useRouter();
|
|
const themeConfig = useSelector((state: IRootState) => state.themeConfig);
|
|
const isRtl = themeConfig.rtlClass === 'rtl';
|
|
|
|
const [user, setUser] = useState<UserData | null>(null);
|
|
const [loadingUser, setLoadingUser] = useState(true);
|
|
|
|
// Highlight active menu (your original effect)
|
|
useEffect(() => {
|
|
const selector = document.querySelector(
|
|
'ul.horizontal-menu a[href="' + window.location.pathname + '"]'
|
|
);
|
|
if (selector) {
|
|
document
|
|
.querySelectorAll('ul.horizontal-menu .nav-link.active')
|
|
.forEach((el) => el.classList.remove('active'));
|
|
document
|
|
.querySelectorAll('ul.horizontal-menu a.active')
|
|
.forEach((el) => el.classList.remove('active'));
|
|
selector.classList.add('active');
|
|
const ul: any = selector.closest('ul.sub-menu');
|
|
if (ul) {
|
|
const ele: any = ul.closest('li.menu')?.querySelector('.nav-link');
|
|
setTimeout(() => ele?.classList.add('active'));
|
|
}
|
|
}
|
|
}, [pathname]);
|
|
|
|
async function loadUser() {
|
|
try {
|
|
const res = await fetch('/api/auth/me', {
|
|
method: 'GET',
|
|
credentials: 'include', // send cookie
|
|
cache: 'no-store', // avoid stale cached responses
|
|
});
|
|
if (!res.ok) throw new Error();
|
|
const data = await res.json();
|
|
setUser(data.user);
|
|
} catch {
|
|
setUser(null);
|
|
} finally {
|
|
setLoadingUser(false);
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
setLoadingUser(true);
|
|
loadUser();
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [pathname]); // re-fetch on route change (after login redirect)
|
|
|
|
const handleLogout = async () => {
|
|
await fetch('/api/auth/logout', { method: 'POST' });
|
|
setUser(null);
|
|
router.push('/login'); // go to login
|
|
};
|
|
|
|
return (
|
|
<header className={`z-40 ${themeConfig.semidark && themeConfig.menu === 'horizontal' ? 'dark' : ''}`}>
|
|
<div className="shadow-sm">
|
|
<div className="relative flex w-full items-center bg-white px-5 py-2.5 dark:bg-rtgray-900">
|
|
{/* Logo */}
|
|
<div className="horizontal-logo flex items-center justify-between ltr:mr-2 rtl:ml-2 lg:hidden">
|
|
<div className="relative h-10 w-32 sm:h-11 sm:w-36 md:h-12 md:w-27 shrink-0 max-h-12">
|
|
<Image
|
|
src="/assets/images/newfulllogo.png"
|
|
alt="logo"
|
|
fill
|
|
className="object-cover"
|
|
priority
|
|
sizes="(max-width: 640px) 8rem, (max-width: 768px) 9rem, (max-width: 1024px) 10rem, 10rem"
|
|
/>
|
|
</div>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={() => dispatch(toggleSidebar())}
|
|
className="collapse-icon flex p-2 rounded-full hover:bg-rtgray-200 dark:text-white dark:hover:bg-rtgray-700"
|
|
>
|
|
<IconMenu className="h-6 w-6" />
|
|
</button>
|
|
</div>
|
|
|
|
|
|
{/* Right-side actions */}
|
|
<div className="flex items-center justify-end space-x-1.5 ltr:ml-auto rtl:mr-auto rtl:space-x-reverse dark:text-[#d0d2d6] lg:space-x-2">
|
|
{/* Theme toggle */}
|
|
{themeConfig.theme === 'light' ? (
|
|
<button
|
|
onClick={() => dispatch(toggleTheme('dark'))}
|
|
className="flex items-center p-2 rounded-full bg-white-light/40 hover:bg-white-light/90 dark:bg-rtgray-800 dark:hover:bg-rtgray-700"
|
|
>
|
|
<IconSun />
|
|
</button>
|
|
) : (
|
|
<button
|
|
onClick={() => dispatch(toggleTheme('light'))}
|
|
className="flex items-center p-2 rounded-full bg-white-light/40 hover:bg-white-light/90 dark:bg-rtgray-800 dark:hover:bg-rtgray-700"
|
|
>
|
|
<IconMoon />
|
|
</button>
|
|
)}
|
|
|
|
{/* User dropdown */}
|
|
<div className="dropdown flex shrink-0 ">
|
|
{loadingUser ? (
|
|
<div className="h-9 w-9 rounded-full animate-pulse bg-gray-300 dark:bg-rtgray-800" />
|
|
) : user ? (
|
|
<Dropdown
|
|
placement={isRtl ? 'bottom-start' : 'bottom-end'}
|
|
btnClassName="relative group block"
|
|
panelClassName="rounded-lg shadow-lg border border-white/10 bg-rtgray-100 dark:bg-rtgray-800 p-2" // ✅
|
|
button={
|
|
<div className="h-9 w-9 rounded-full bg-rtgray-200 dark:bg-rtgray-800 flex items-center justify-center group-hover:bg-rtgray-300 dark:group-hover:bg-rtgray-700">
|
|
<IconUser className="h-5 w-5 text-gray-600 dark:text-gray-300" />
|
|
</div>
|
|
}
|
|
>
|
|
<ul className="w-[230px] font-semibold text-dark"> {/* make sure this stays transparent */}
|
|
<li className="px-4 py-4 flex items-center">
|
|
<div className="truncate ltr:pl-1.5 rtl:pr-4">
|
|
<h4 className="text-sm text-left">{user.email}</h4>
|
|
</div>
|
|
</li>
|
|
<li>
|
|
<Link href="/users/profile" className="dark:hover:text-white">
|
|
<IconUser className="h-4.5 w-4.5 mr-2" /> Profile
|
|
</Link>
|
|
</li>
|
|
<li>
|
|
<Link href="/auth/boxed-lockscreen" className="dark:hover:text-white">
|
|
<IconLockDots className="h-4.5 w-4.5 mr-2" /> Lock Screen
|
|
</Link>
|
|
</li>
|
|
<li className="border-t border-white-light dark:border-white-light/10">
|
|
<button onClick={handleLogout} className="flex w-full items-center py-3 text-danger">
|
|
<IconLogout className="h-4.5 w-4.5 mr-2 rotate-90" /> Sign Out
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</Dropdown>
|
|
) : (
|
|
<Link
|
|
href="/login"
|
|
className="rounded-md bg-yellow-400 px-3 py-1.5 text-black font-semibold hover:brightness-95"
|
|
>
|
|
Sign In
|
|
</Link>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|