'use client' import { useState, useEffect } from 'react' import Link from 'next/link' import { usePathname } from 'next/navigation' import { signOut, useSession } from 'next-auth/react' import { cn } from '@/lib/utils' import { Button } from '@/components/ui/button' import { UserAvatar } from '@/components/shared/user-avatar' import { trpc } from '@/lib/trpc/client' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import type { Route } from 'next' import type { LucideIcon } from 'lucide-react' import { LogOut, Menu, Moon, Settings, Sun, User, X, LayoutDashboard, Scale, Handshake, Eye, ArrowRightLeft, ExternalLink as ExternalLinkIcon, HelpCircle, Mail, } from 'lucide-react' import type { UserRole } from '@prisma/client' import { useTheme } from 'next-themes' import { Logo } from '@/components/shared/logo' import { NotificationBell } from '@/components/shared/notification-bell' export type NavItem = { name: string href: string icon: LucideIcon external?: boolean } export type RoleNavUser = { name?: string | null email?: string | null } type RoleNavProps = { navigation: NavItem[] roleName: string user: RoleNavUser /** The base path for the role (e.g., '/jury', '/mentor', '/observer'). Used for active state detection on the dashboard link. */ basePath: string /** Optional status badge displayed next to the logo (e.g., remaining evaluations count) */ statusBadge?: React.ReactNode /** Optional slot rendered in the mobile hamburger menu (between nav links and sign out) and desktop header */ editionSelector?: React.ReactNode /** Optional support email — when provided, shows a Help button in the header */ helpEmail?: string } // Role switcher config — maps roles to their dashboard views const ROLE_SWITCH_OPTIONS: Record = { SUPER_ADMIN: { label: 'Admin View', path: '/admin', icon: LayoutDashboard }, PROGRAM_ADMIN: { label: 'Admin View', path: '/admin', icon: LayoutDashboard }, JURY_MEMBER: { label: 'Jury View', path: '/jury', icon: Scale }, MENTOR: { label: 'Mentor View', path: '/mentor', icon: Handshake }, OBSERVER: { label: 'Observer View', path: '/observer', icon: Eye }, } function isNavItemActive(pathname: string, href: string, basePath: string): boolean { return pathname === href || (href !== basePath && pathname.startsWith(href)) } export function RoleNav({ navigation, roleName, user, basePath, statusBadge, editionSelector, helpEmail }: RoleNavProps) { const pathname = usePathname() const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false) const { data: session, status: sessionStatus } = useSession() const isAuthenticated = sessionStatus === 'authenticated' const { data: avatarUrl } = trpc.avatar.getUrl.useQuery(undefined, { enabled: isAuthenticated, }) const { theme, setTheme } = useTheme() const [mounted, setMounted] = useState(false) useEffect(() => setMounted(true), []) // Roles the user can switch to (excluding current view) const userRoles = (session?.user?.roles as UserRole[] | undefined) ?? [] const switchableRoles = Object.entries(ROLE_SWITCH_OPTIONS) .filter(([role, opt]) => userRoles.includes(role as UserRole) && opt.path !== basePath) // Deduplicate admin paths (SUPER_ADMIN and PROGRAM_ADMIN both go to /admin) .filter((entry, i, arr) => arr.findIndex(([, o]) => o.path === entry[1].path) === i) return (
{/* Logo */} {statusBadge} {/* Desktop nav */} {/* User menu & mobile toggle */}
{editionSelector &&
{editionSelector}
} {helpEmail && (

Need Help?

Contact our support team

{helpEmail}
)} {mounted && ( )} {user.email} Settings {switchableRoles.length > 0 && ( <> {switchableRoles.map(([, opt]) => ( {opt.label} ))} )} signOut({ callbackUrl: '/login' })} className="text-destructive focus:text-destructive" > Sign Out
{/* Mobile menu — animated with CSS grid */}
) }