'use client'; import * as React from 'react'; import { ChevronDown, LogOut, Menu, Moon, Sun, User as UserIcon, X, LayoutDashboard, Shield, } from 'lucide-react'; import { cn } from '@/lib/utils'; /* -------------------------------------------------------------------------- */ /* Types */ /* -------------------------------------------------------------------------- */ export interface NavLink { href: string; label: string; isActive?: boolean; /** Nested links shown on hover / accordion. */ children?: NavLink[]; } export interface NavUser { fullName: string; email?: string | null; phone?: string | null; role: string; avatarUrl?: string | null; } export interface NavbarProps { /** Brand name shown in the header. */ brand: string; /** Primary navigation links. */ links: NavLink[]; /** Current user or null if unauthenticated. */ user: NavUser | null; /** Dashboard href (role-dependent). */ dashboardHref: string; /** Slot: notification bell or other widgets. */ notifications?: React.ReactNode; /** Slot: language switcher. */ languageSwitcher?: React.ReactNode; /** Light / dark theme. */ theme: 'light' | 'dark'; /** Toggle theme callback. */ onToggleTheme: () => void; /** Logout callback. */ onLogout: () => Promise | void; /** i18n labels. */ labels: { login: string; register: string; dashboard: string; admin: string; profile: string; logout: string; openMenu: string; closeMenu: string; darkMode: string; lightMode: string; mainNav: string; }; /** Login / register hrefs — rendered via renderLink. */ loginHref?: string; registerHref?: string; profileHref?: string; /** Custom link renderer for framework-specific Link component (Next/i18n). */ renderLink: (props: { href: string; children: React.ReactNode; className?: string; onClick?: () => void; }) => React.ReactNode; } /* -------------------------------------------------------------------------- */ /* Helpers */ /* -------------------------------------------------------------------------- */ const ROLE_LABELS: Record = { ADMIN: 'Quản trị viên', AGENT: 'Đại lý', SELLER: 'Người bán', BUYER: 'Người mua', }; function getInitials(fullName: string): string { const parts = fullName.trim().split(/\s+/).filter(Boolean); if (parts.length === 0) return '?'; if (parts.length === 1) return parts[0]!.slice(0, 2).toUpperCase(); return (parts[0]![0]! + parts[parts.length - 1]![0]!).toUpperCase(); } /* -------------------------------------------------------------------------- */ /* Component */ /* -------------------------------------------------------------------------- */ export function Navbar({ brand, links, user, dashboardHref, notifications, languageSwitcher, theme, onToggleTheme, onLogout, labels, loginHref = '/login', registerHref = '/register', profileHref = '/dashboard/profile', renderLink, }: NavbarProps) { const [mobileOpen, setMobileOpen] = React.useState(false); const close = () => setMobileOpen(false); const handleLogout = async () => { close(); await onLogout(); }; return (
{/* -------- Main bar -------- */}
{/* Brand */} {renderLink({ href: '/', className: 'mr-6 flex items-center gap-2 group', children: ( <>
G
{brand} ), })} {/* Desktop nav */} {/* Right actions */}
{languageSwitcher} {user ? ( <>
{notifications}
{/* User pill */}
{user.avatarUrl ? ( // eslint-disable-next-line @next/next/no-img-element ) : (
{getInitials(user.fullName)}
)} {user.fullName} {ROLE_LABELS[user.role] && ( {ROLE_LABELS[user.role]} )}
{renderLink({ href: dashboardHref, className: 'hidden sm:inline-flex', children: ( ), })} ) : ( <> {renderLink({ href: loginHref, className: 'hidden sm:inline-flex', children: ( ), })} {renderLink({ href: registerHref, className: 'hidden sm:inline-flex', children: ( ), })} )} {/* Mobile hamburger */}
{/* -------- Mobile drawer -------- */} {mobileOpen && ( )}
); }