2026-01-19 08:54:09 +05:30
|
|
|
'use client';
|
|
|
|
|
|
2026-02-01 00:59:37 +05:30
|
|
|
import { useState, useEffect, useRef, useCallback } from 'react';
|
2026-01-19 08:54:09 +05:30
|
|
|
import Link from 'next/link';
|
|
|
|
|
import Image from 'next/image';
|
|
|
|
|
import { useSession, signOut } from 'next-auth/react';
|
2026-02-01 00:35:22 +05:30
|
|
|
import { agentsService } from '@/services/agents.service';
|
2026-02-01 00:50:09 +05:30
|
|
|
import { usersService } from '@/services/users.service';
|
2026-02-01 00:35:22 +05:30
|
|
|
import { uploadService } from '@/services/upload.service';
|
2026-01-19 08:54:09 +05:30
|
|
|
|
|
|
|
|
const navLinks = [
|
|
|
|
|
{ label: 'Education', href: '/education' },
|
|
|
|
|
{ label: 'About Us', href: '/about' },
|
|
|
|
|
{ label: "FAQ's", href: '/faq' },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
export function CommonHeader() {
|
|
|
|
|
const { data: session } = useSession();
|
|
|
|
|
const [showProfileMenu, setShowProfileMenu] = useState(false);
|
2026-02-01 00:06:52 +05:30
|
|
|
const profileMenuRef = useRef<HTMLDivElement>(null);
|
2026-02-01 00:35:22 +05:30
|
|
|
const [profileImage, setProfileImage] = useState<string | null>(null);
|
2026-02-01 00:59:37 +05:30
|
|
|
const [profileName, setProfileName] = useState<string | null>(null);
|
2026-02-01 00:06:52 +05:30
|
|
|
|
|
|
|
|
// Close dropdown when clicking outside
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
function handleClickOutside(event: MouseEvent) {
|
|
|
|
|
if (profileMenuRef.current && !profileMenuRef.current.contains(event.target as Node)) {
|
|
|
|
|
setShowProfileMenu(false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (showProfileMenu) {
|
|
|
|
|
document.addEventListener('mousedown', handleClickOutside);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return () => {
|
|
|
|
|
document.removeEventListener('mousedown', handleClickOutside);
|
|
|
|
|
};
|
|
|
|
|
}, [showProfileMenu]);
|
2026-01-19 08:54:09 +05:30
|
|
|
|
2026-02-01 00:59:37 +05:30
|
|
|
// Fetch profile data (image and name) from backend based on user role
|
|
|
|
|
const fetchProfileData = useCallback(async () => {
|
|
|
|
|
try {
|
|
|
|
|
const role = (session?.user as any)?.role;
|
|
|
|
|
let avatar: string | null = null;
|
|
|
|
|
let name: string | null = null;
|
2026-02-01 00:50:09 +05:30
|
|
|
|
2026-02-01 00:59:37 +05:30
|
|
|
if (role === 'AGENT') {
|
|
|
|
|
const profile = await agentsService.getMyProfile();
|
|
|
|
|
avatar = profile.avatar;
|
|
|
|
|
name = profile.firstName ? `${profile.firstName} ${profile.lastName || ''}`.trim() : null;
|
|
|
|
|
} else if (role === 'USER') {
|
|
|
|
|
const profile = await usersService.getMyProfile();
|
|
|
|
|
avatar = profile.avatar;
|
|
|
|
|
name = profile.firstName ? `${profile.firstName} ${profile.lastName || ''}`.trim() : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (name) {
|
|
|
|
|
setProfileName(name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (avatar) {
|
|
|
|
|
try {
|
|
|
|
|
const avatarUrl = await uploadService.getPresignedDownloadUrl(avatar);
|
|
|
|
|
setProfileImage(avatarUrl);
|
|
|
|
|
} catch {
|
|
|
|
|
setProfileImage(avatar);
|
2026-02-01 00:35:22 +05:30
|
|
|
}
|
|
|
|
|
}
|
2026-02-01 00:59:37 +05:30
|
|
|
} catch (err) {
|
|
|
|
|
console.error('Failed to fetch profile data:', err);
|
|
|
|
|
}
|
|
|
|
|
}, [session]);
|
2026-02-01 00:35:22 +05:30
|
|
|
|
2026-02-01 00:59:37 +05:30
|
|
|
useEffect(() => {
|
2026-02-01 00:35:22 +05:30
|
|
|
if (session) {
|
2026-02-01 00:59:37 +05:30
|
|
|
fetchProfileData();
|
2026-02-01 00:35:22 +05:30
|
|
|
}
|
2026-02-01 00:59:37 +05:30
|
|
|
}, [session, fetchProfileData]);
|
|
|
|
|
|
|
|
|
|
// Listen for profile update events to refresh the header
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const handleProfileUpdate = () => {
|
|
|
|
|
if (session) {
|
|
|
|
|
fetchProfileData();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
window.addEventListener('profile-updated', handleProfileUpdate);
|
|
|
|
|
return () => {
|
|
|
|
|
window.removeEventListener('profile-updated', handleProfileUpdate);
|
|
|
|
|
};
|
|
|
|
|
}, [session, fetchProfileData]);
|
2026-02-01 00:35:22 +05:30
|
|
|
|
2026-02-01 00:59:37 +05:30
|
|
|
// Use fetched profile name, fallback to session name
|
|
|
|
|
const userName = profileName || session?.user?.name;
|
2026-01-19 08:54:09 +05:30
|
|
|
const userEmail = session?.user?.email;
|
|
|
|
|
const userRole = (session?.user as any)?.role;
|
2026-02-01 00:35:22 +05:30
|
|
|
// Use fetched profile image, fallback to session image
|
|
|
|
|
const userImage = profileImage || session?.user?.image;
|
2026-01-19 08:54:09 +05:30
|
|
|
|
|
|
|
|
// Determine dashboard link based on user role
|
2026-01-20 01:24:43 +05:30
|
|
|
const dashboardLink = userRole === 'AGENT' ? '/agent/dashboard' : '/user/dashboard';
|
2026-01-19 08:54:09 +05:30
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<header className="bg-[#648188] rounded-[20px] px-8">
|
|
|
|
|
<div className="flex justify-between items-center h-[70px]">
|
|
|
|
|
{/* Logo */}
|
|
|
|
|
<Link href="/">
|
|
|
|
|
<Image
|
|
|
|
|
src="/assets/logo.svg"
|
|
|
|
|
alt="RE-QuestN"
|
|
|
|
|
width={150}
|
|
|
|
|
height={40}
|
|
|
|
|
className="h-10 w-auto"
|
|
|
|
|
/>
|
|
|
|
|
</Link>
|
|
|
|
|
|
|
|
|
|
{/* Navigation */}
|
|
|
|
|
<nav className="hidden md:flex items-center gap-8 ml-auto mr-8">
|
|
|
|
|
{navLinks.map((link) => (
|
|
|
|
|
<Link
|
|
|
|
|
key={link.href}
|
|
|
|
|
href={link.href}
|
|
|
|
|
className="font-fractul font-bold text-[20px] leading-[24px] text-[#00293D] hover:text-[#e58625] transition-colors"
|
|
|
|
|
>
|
|
|
|
|
{link.label}
|
|
|
|
|
</Link>
|
|
|
|
|
))}
|
|
|
|
|
</nav>
|
|
|
|
|
|
|
|
|
|
{/* Right Side Icons */}
|
|
|
|
|
<div className="flex items-center gap-4">
|
|
|
|
|
{session ? (
|
|
|
|
|
<>
|
|
|
|
|
{/* Notification Bell */}
|
|
|
|
|
<button className="relative w-6 h-6 flex items-center justify-center hover:opacity-80 transition-opacity">
|
|
|
|
|
<Image
|
|
|
|
|
src="/assets/notification-icon.svg"
|
|
|
|
|
alt="Notifications"
|
|
|
|
|
width={20}
|
|
|
|
|
height={22}
|
|
|
|
|
/>
|
|
|
|
|
{/* Notification Dot */}
|
|
|
|
|
<span className="absolute -top-0.5 -right-0.5 w-2 h-2 bg-red-500 rounded-full"></span>
|
|
|
|
|
</button>
|
|
|
|
|
|
2026-02-01 00:06:52 +05:30
|
|
|
{/* Profile Section with Greeting - Entire area clickable */}
|
|
|
|
|
<div className="relative" ref={profileMenuRef}>
|
2026-01-19 08:54:09 +05:30
|
|
|
<button
|
|
|
|
|
onClick={() => setShowProfileMenu(!showProfileMenu)}
|
2026-02-01 00:06:52 +05:30
|
|
|
className="flex items-center gap-3 hover:opacity-80 transition-opacity cursor-pointer"
|
2026-01-19 08:54:09 +05:30
|
|
|
>
|
2026-02-01 00:06:52 +05:30
|
|
|
{/* Avatar */}
|
2026-02-01 00:35:22 +05:30
|
|
|
<div className="w-[35px] h-[35px] rounded-full overflow-hidden border-2 border-[#e58625] bg-gray-100">
|
|
|
|
|
{userImage ? (
|
|
|
|
|
<img
|
|
|
|
|
src={userImage}
|
|
|
|
|
alt="Profile"
|
|
|
|
|
className="w-full h-full object-cover"
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<Image
|
|
|
|
|
src="/assets/icons/user-placeholder-icon.svg"
|
|
|
|
|
alt="Profile"
|
|
|
|
|
width={35}
|
|
|
|
|
height={35}
|
2026-02-08 22:44:06 +05:30
|
|
|
className="object-cover"
|
|
|
|
|
style={{ width: '100%', height: '100%' }}
|
2026-02-01 00:35:22 +05:30
|
|
|
/>
|
|
|
|
|
)}
|
2026-02-01 00:06:52 +05:30
|
|
|
</div>
|
|
|
|
|
{/* Greeting Text */}
|
|
|
|
|
<div className="flex flex-col text-left">
|
|
|
|
|
<span className="font-bold text-[14px] text-[#e58625] leading-tight">
|
|
|
|
|
Hi {userName?.split(' ')[0] || 'User'} !
|
|
|
|
|
</span>
|
|
|
|
|
<span className="font-bold text-[9px] text-[#00293d] leading-tight">
|
|
|
|
|
Welcome back !
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
2026-01-19 08:54:09 +05:30
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
{/* Profile Dropdown Menu */}
|
|
|
|
|
{showProfileMenu && (
|
2026-02-01 00:06:52 +05:30
|
|
|
<div className="absolute right-0 top-full mt-3 w-[271px] bg-white rounded-[15px] border border-black/20 z-50 shadow-[0px_4px_4px_0px_rgba(0,0,0,0.25)]">
|
|
|
|
|
{/* Arrow/Triangle at top */}
|
|
|
|
|
<div className="absolute -top-2 right-6 w-0 h-0 border-l-[8px] border-l-transparent border-r-[8px] border-r-transparent border-b-[8px] border-b-white"></div>
|
|
|
|
|
|
|
|
|
|
{/* User Profile Section */}
|
|
|
|
|
<div className="flex items-center gap-3 px-4 py-4 border-b border-black/10">
|
2026-02-01 00:35:22 +05:30
|
|
|
<div className="w-[42px] h-[42px] rounded-full overflow-hidden flex-shrink-0 bg-gray-100">
|
|
|
|
|
{userImage ? (
|
|
|
|
|
<img
|
|
|
|
|
src={userImage}
|
|
|
|
|
alt="Profile"
|
|
|
|
|
className="w-full h-full object-cover"
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<Image
|
|
|
|
|
src="/assets/icons/user-placeholder-icon.svg"
|
|
|
|
|
alt="Profile"
|
|
|
|
|
width={42}
|
|
|
|
|
height={42}
|
2026-02-08 22:44:06 +05:30
|
|
|
className="object-cover"
|
|
|
|
|
style={{ width: '100%', height: '100%' }}
|
2026-02-01 00:35:22 +05:30
|
|
|
/>
|
|
|
|
|
)}
|
2026-02-01 00:06:52 +05:30
|
|
|
</div>
|
|
|
|
|
<div className="flex flex-col">
|
|
|
|
|
<p className="font-fractul font-medium text-[16px] leading-[20px] text-black">
|
|
|
|
|
{userName?.split(' ')[0] || 'User'}
|
|
|
|
|
</p>
|
|
|
|
|
<p className="font-serif text-[14px] leading-[18px] text-black/50">
|
|
|
|
|
{userEmail}
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
2026-01-19 08:54:09 +05:30
|
|
|
</div>
|
2026-02-01 00:06:52 +05:30
|
|
|
|
|
|
|
|
{/* Account Settings */}
|
|
|
|
|
<Link
|
|
|
|
|
href={userRole === 'AGENT' ? '/agent/settings' : '/user/settings'}
|
|
|
|
|
className="flex items-center gap-3 px-4 py-3 border-b border-black/10 hover:bg-black/5 transition-colors"
|
|
|
|
|
onClick={() => setShowProfileMenu(false)}
|
|
|
|
|
>
|
|
|
|
|
<Image
|
|
|
|
|
src="/assets/icons/settings-icon.svg"
|
|
|
|
|
alt="Settings"
|
|
|
|
|
width={24}
|
|
|
|
|
height={24}
|
|
|
|
|
/>
|
|
|
|
|
<div className="flex flex-col">
|
|
|
|
|
<p className="font-fractul text-[16px] leading-[18px] text-black">
|
|
|
|
|
Account Settings
|
|
|
|
|
</p>
|
|
|
|
|
<p className="font-serif text-[14px] leading-[18px] text-black/50">
|
|
|
|
|
Profile, Security
|
|
|
|
|
</p>
|
|
|
|
|
</div>
|
|
|
|
|
</Link>
|
|
|
|
|
|
|
|
|
|
{/* Notification Settings */}
|
|
|
|
|
<Link
|
|
|
|
|
href={userRole === 'AGENT' ? '/agent/settings/notifications' : '/user/settings/notifications'}
|
|
|
|
|
className="flex items-center gap-3 px-4 py-3 border-b border-black/10 hover:bg-black/5 transition-colors"
|
|
|
|
|
onClick={() => setShowProfileMenu(false)}
|
|
|
|
|
>
|
|
|
|
|
<Image
|
|
|
|
|
src="/assets/icons/notification-settings-icon.svg"
|
|
|
|
|
alt="Notifications"
|
|
|
|
|
width={24}
|
|
|
|
|
height={24}
|
|
|
|
|
/>
|
|
|
|
|
<p className="font-fractul text-[16px] leading-[18px] text-black">
|
|
|
|
|
Notification Settings
|
|
|
|
|
</p>
|
|
|
|
|
</Link>
|
|
|
|
|
|
|
|
|
|
{/* Log Out */}
|
|
|
|
|
<button
|
|
|
|
|
onClick={() => signOut({ callbackUrl: '/login' })}
|
|
|
|
|
className="w-full flex items-center gap-3 px-4 py-3 hover:bg-black/5 transition-colors"
|
|
|
|
|
>
|
|
|
|
|
<Image
|
|
|
|
|
src="/assets/icons/logout-icon.svg"
|
|
|
|
|
alt="Log Out"
|
|
|
|
|
width={24}
|
|
|
|
|
height={24}
|
|
|
|
|
/>
|
|
|
|
|
<p className="font-fractul font-bold text-[16px] leading-[18px] text-[#e58625]">
|
|
|
|
|
Log Out
|
|
|
|
|
</p>
|
|
|
|
|
</button>
|
2026-01-19 08:54:09 +05:30
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<div className="flex items-center gap-3">
|
|
|
|
|
<Link
|
|
|
|
|
href="/login"
|
|
|
|
|
className="font-fractul font-bold text-[14px] text-[#00293D] hover:text-[#e58625] transition-colors"
|
|
|
|
|
>
|
|
|
|
|
Login
|
|
|
|
|
</Link>
|
|
|
|
|
<Link
|
|
|
|
|
href="/signup"
|
|
|
|
|
className="bg-[#e58625] text-[#00293D] px-4 py-2 rounded-[15px] font-fractul font-bold text-[14px] hover:bg-[#d47720] transition-colors"
|
|
|
|
|
>
|
|
|
|
|
Sign Up
|
|
|
|
|
</Link>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Mobile Menu Button */}
|
|
|
|
|
<button className="md:hidden p-2 hover:opacity-80 transition-opacity">
|
|
|
|
|
<svg
|
|
|
|
|
className="w-6 h-6 text-[#00293d]"
|
|
|
|
|
fill="none"
|
|
|
|
|
stroke="currentColor"
|
|
|
|
|
viewBox="0 0 24 24"
|
|
|
|
|
>
|
|
|
|
|
<path
|
|
|
|
|
strokeLinecap="round"
|
|
|
|
|
strokeLinejoin="round"
|
|
|
|
|
strokeWidth={2}
|
|
|
|
|
d="M4 6h16M4 12h16M4 18h16"
|
|
|
|
|
/>
|
|
|
|
|
</svg>
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</header>
|
|
|
|
|
);
|
|
|
|
|
}
|