JB/web/src/components/Navbar.tsx

188 lines
7.2 KiB
TypeScript

"use client";
import { useState } from "react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useSession, signOut } from "next-auth/react";
import { NotificationBell } from "./NotificationBell";
export function Navbar() {
const pathname = usePathname();
const { data: session, status } = useSession();
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const role = (session?.user as any)?.role || "GUEST";
const isEmployer = role === "EMPLOYER";
const isAdmin = role === "ADMIN";
// Build role-tailored navigation items
const navLinks: Array<{ href: string; label: string; badge?: string }> = [
{ href: "/jobs", label: "Search Directory" },
{ href: "/companies", label: "Companies" },
{ href: "/salary-insights", label: "Salaries" },
];
if (status === "authenticated") {
if (isEmployer) {
navLinks.push(
{ href: "/employer/jobs", label: "Listings" },
{ href: "/employer/jobs/new", label: "Post a Job", badge: "New" },
{ href: "/employer/ats", label: "Pipeline" },
{ href: "/employer/team", label: "Team" },
{ href: "/employer/candidates", label: "Talent" }
);
} else if (isAdmin) {
navLinks.push(
{ href: "/employer/jobs", label: "Jobs" },
{ href: "/employer/jobs/new", label: "Post Job" },
{ href: "/employer/ats", label: "ATS" },
{ href: "/admin", label: "Admin Console", badge: "Admin" }
);
} else {
// Job Seeker
navLinks.push(
{ href: "/saved-jobs", label: "Saved" },
{ href: "/applications", label: "Applications" },
{ href: "/resume", label: "Resume" },
{ href: "/alerts", label: "Alerts" },
{ href: "/profile", label: "Profile" }
);
}
}
return (
<header className="bg-[#fbfbf9] border-b border-stone-200 sticky top-0 z-40">
<div className="max-w-7xl mx-auto px-4 sm:px-6">
<div className="flex justify-between h-13 items-center">
{/* Logo & Desktop Nav */}
<div className="flex items-center space-x-8">
<Link
href="/"
className="flex items-baseline space-x-2 text-stone-900 group"
aria-label="DirectWire Home"
>
<span className="font-serif-editorial text-2xl font-bold tracking-tight text-stone-900">
DirectWire
</span>
<span className="text-[10px] font-mono tracking-widest text-stone-400 uppercase">
/ beta
</span>
</Link>
<nav className="hidden md:flex items-center space-x-1 border-l border-stone-200 pl-6 h-6">
{navLinks.map((link) => {
const isActive =
link.href === "/"
? pathname === "/"
: pathname.startsWith(link.href);
return (
<Link
key={link.href}
href={link.href}
className={`px-2.5 py-1 text-xs transition-colors flex items-center space-x-1.5 ${
isActive
? "text-stone-950 font-semibold border-b-2 border-stone-900 -mb-[1px]"
: "text-stone-600 hover:text-stone-900"
}`}
>
<span>{link.label}</span>
{link.badge && (
<span className="text-[9px] px-1 py-0.2 bg-stone-100 text-stone-700 border border-stone-300 rounded font-mono">
{link.badge}
</span>
)}
</Link>
);
})}
</nav>
</div>
{/* User Session & Actions */}
<div className="flex items-center space-x-4 text-xs">
{status === "authenticated" && session?.user ? (
<div className="flex items-center space-x-3">
<NotificationBell />
<div className="hidden sm:flex items-center space-x-1.5 text-stone-700">
<span className="w-1.5 h-1.5 rounded-full bg-amber-600"></span>
<span className="font-medium">
{session.user.name || session.user.email}
</span>
<span className="text-[10px] text-stone-400 font-mono">
[{role}]
</span>
</div>
<button
onClick={() => signOut({ callbackUrl: "/login" })}
className="text-stone-500 hover:text-stone-900 transition-colors px-1 py-1 font-medium underline underline-offset-4"
>
Sign Out
</button>
</div>
) : (
<div className="flex items-center space-x-4">
<Link
href="/login"
className="text-stone-700 hover:text-stone-950 font-medium transition-colors"
>
Log In
</Link>
<Link
href="/register"
className="bg-stone-900 hover:bg-stone-800 text-stone-50 font-medium px-3.5 py-1.5 text-xs transition-colors border border-stone-900"
>
Get Started →
</Link>
</div>
)}
{/* Mobile Menu Toggle */}
<button
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
className="md:hidden p-1.5 text-stone-700 hover:text-stone-900 border border-stone-200"
aria-label={mobileMenuOpen ? "Close menu" : "Open menu"}
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
{mobileMenuOpen ? (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M6 18L18 6M6 6l12 12" />
) : (
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M4 6h16M4 12h16M4 18h16" />
)}
</svg>
</button>
</div>
</div>
</div>
{/* Mobile Dropdown */}
{mobileMenuOpen && (
<div className="md:hidden bg-[#fbfbf9] border-t border-b border-stone-200 px-4 py-3 space-y-1">
{navLinks.map((link) => {
const isActive =
link.href === "/"
? pathname === "/"
: pathname.startsWith(link.href);
return (
<Link
key={link.href}
href={link.href}
onClick={() => setMobileMenuOpen(false)}
className={`flex items-center justify-between px-3 py-2 text-xs transition-colors ${
isActive
? "bg-stone-200/70 text-stone-950 font-bold"
: "text-stone-700 hover:bg-stone-100"
}`}
>
<span>{link.label}</span>
{link.badge && (
<span className="text-[10px] px-1.5 py-0.5 border border-stone-300 font-mono">
{link.badge}
</span>
)}
</Link>
);
})}
</div>
)}
</header>
);
}