Files
yourwillyourwish/components/Navbar.tsx
2026-02-06 21:44:04 -06:00

160 lines
7.6 KiB
TypeScript
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import Link from "next/link";
import ThemeToggle from "./ThemeToggle";
import { useEffect, useState, useRef } from "react";
export default function Navbar() {
const [me, setMe] = useState<any>(null);
const [menuOpen, setMenuOpen] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
const loadUser = () => {
fetch("/api/auth/me")
.then((r) => r.json())
.then((d) => setMe(d))
.catch(() => setMe(null));
};
useEffect(() => {
loadUser();
// Listen for profile updates
const handleProfileUpdate = () => {
loadUser();
};
window.addEventListener('profile-updated', handleProfileUpdate);
return () => {
window.removeEventListener('profile-updated', handleProfileUpdate);
};
}, []);
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
setMenuOpen(false);
}
}
if (menuOpen) {
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}
}, [menuOpen]);
const user = me?.user;
const initials = user?.firstName && user?.lastName ? `${user.firstName[0]}${user.lastName[0]}`.toUpperCase() : "U";
return (
<>
<header className="sticky top-0 z-40 border-b border-gray-200/50 dark:border-slate-700/50 bg-white/80 dark:bg-darkbg/80 backdrop-blur-md shadow-sm">
<div className="max-w-7xl mx-auto px-6 h-16 flex items-center justify-between">
<Link href="/" className="flex items-center gap-3 hover:opacity-90 transition-opacity duration-300 group">
<div className="h-10 w-10 rounded-lg bg-gradient-to-br from-primary to-primary-dark text-white flex items-center justify-center shadow-md group-hover:shadow-lg transition-all duration-300 text-lg">
📊
</div>
<div className="leading-tight hidden sm:block">
<div className="font-bold text-sm text-gray-900 dark:text-white">
Estate Pro
</div>
<div className="text-xs text-gray-500 dark:text-gray-400 font-medium">Planning Hub</div>
</div>
</Link>
<nav className="hidden md:flex items-center gap-8 text-sm font-medium">
<Link href="/" className="text-gray-700 dark:text-gray-300 hover:text-primary transition-colors duration-300 relative group">
Home
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-primary group-hover:w-full transition-all duration-300" />
</Link>
<Link href="/webinars" className="text-gray-700 dark:text-gray-300 hover:text-primary transition-colors duration-300 relative group">
Webinars
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-primary group-hover:w-full transition-all duration-300" />
</Link>
<Link href="/resources" className="text-gray-700 dark:text-gray-300 hover:text-primary transition-colors duration-300 relative group">
Resources
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-primary group-hover:w-full transition-all duration-300" />
</Link>
<Link href="/about" className="text-gray-700 dark:text-gray-300 hover:text-primary transition-colors duration-300 relative group">
About
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-primary group-hover:w-full transition-all duration-300" />
</Link>
<Link href="/contact" className="text-gray-700 dark:text-gray-300 hover:text-primary dark:hover:text-primary transition-colors duration-300 relative group">
Contact
<span className="absolute bottom-0 left-0 w-0 h-0.5 bg-primary group-hover:w-full transition-all duration-300" />
</Link>
</nav>
<div className="flex items-center gap-4">
<ThemeToggle />
{user ? (
<div className="relative" ref={menuRef}>
<button
className="h-11 w-11 rounded-full bg-primary text-white flex items-center justify-center shadow-glow hover:shadow-xl hover:scale-110 transition-all duration-300 active:scale-95"
onClick={() => setMenuOpen((v) => !v)}
aria-label="Account menu"
>
{user.avatarUrl ? (
// eslint-disable-next-line @next/next/no-img-element
<img src={user.avatarUrl} alt="Avatar" className="h-11 w-11 rounded-full object-cover border-2 border-white/20" />
) : (
<span className="text-sm font-bold">{initials}</span>
)}
</button>
{menuOpen && (
<div className="absolute right-0 mt-3 w-56 rounded-2xl border border-gray-200 dark:border-gray-700 bg-white dark:bg-slate-800 shadow-elevation-3 dark:shadow-elevation-4 backdrop-blur-xl p-2 text-sm z-50 animate-slideUp">
{user.role === "ADMIN" && (
<>
<div className="px-3 py-2 text-xs font-semibold text-primary uppercase tracking-wider mb-1">Admin</div>
<Link className="block px-4 py-2.5 rounded-lg hover:bg-primary/10 dark:hover:bg-primary/20 transition-colors duration-200 font-medium text-gray-700 dark:text-gray-200" href="/admin">
📊 Dashboard
</Link>
<Link className="block px-4 py-2.5 rounded-lg hover:bg-primary/10 dark:hover:bg-primary/20 transition-colors duration-200 font-medium text-gray-700 dark:text-gray-200" href="/admin/contact-messages">
📧 Messages
</Link>
<hr className="my-2 border-gray-200 dark:border-gray-700" />
</>
)}
<Link className="block px-4 py-2.5 rounded-lg hover:bg-primary/10 dark:hover:bg-primary/20 transition-colors duration-200 font-medium text-gray-700 dark:text-gray-200" href="/account/webinars">
🎓 My Webinars
</Link>
<Link className="block px-4 py-2.5 rounded-lg hover:bg-primary/10 dark:hover:bg-primary/20 transition-colors duration-200 font-medium text-gray-700 dark:text-gray-200" href="/account/settings">
Settings
</Link>
<hr className="my-2 border-gray-200 dark:border-gray-700" />
<button
className="w-full text-left px-4 py-2.5 rounded-lg hover:bg-danger/10 dark:hover:bg-danger/20 transition-colors duration-200 font-medium text-danger"
onClick={async () => {
await fetch("/api/auth/logout", { method: "POST" });
window.location.href = "/";
}}
>
🚪 Logout
</button>
</div>
)}
</div>
) : (
<>
<Link
href="/signin"
className="px-4 py-2 text-sm font-semibold text-gray-700 dark:text-gray-300 hover:text-primary transition-colors duration-300"
>
Sign In
</Link>
<Link
href="/signup"
className="btn-primary btn-sm"
>
Get Started
</Link>
</>
)}
</div>
</div>
</header>
</>
);
}