"use client"; import { IconCompass, IconHome, IconLogout, IconSearch, IconSettings, } from "@tabler/icons-react"; import { useSetAtom } from "jotai"; import { motion } from "motion/react"; import Link from "next/link"; import { usePathname, useRouter } from "next/navigation"; import { useLayoutEffect, useRef, useState } from "react"; import { SofaLogo } from "@/components/sofa-logo"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Kbd } from "@/components/ui/kbd"; import { Separator } from "@/components/ui/separator"; import { commandPaletteOpenAtom } from "@/lib/atoms/command-palette"; import { signOut } from "@/lib/auth/client"; /** Horizontal inset (px) of the desktop indicator within each link (Tailwind `inset-x-2`). */ const DESKTOP_INDICATOR_INSET = 8; /** Width (px) of the mobile indicator bar (Tailwind `w-8` = 2rem). */ const MOBILE_INDICATOR_WIDTH = 32; const springTransition = { type: "spring", stiffness: 380, damping: 30, } as const; const navLinks = [ { href: "/dashboard", label: "Home" }, { href: "/explore", label: "Explore" }, ] as const; const mobileTabs = [ { href: "/dashboard", label: "Home", icon: IconHome }, { href: "/explore", label: "Explore", icon: IconCompass }, { href: "/settings", label: "Settings", icon: IconSettings }, ] as const; function isLinkActive(pathname: string, href: string) { return ( pathname === href || pathname.startsWith(`${href}/`) || (href === "/dashboard" && pathname === "/") ); } function measureDesktopIndicator(itemRect: DOMRect, containerRect: DOMRect) { return { left: itemRect.left - containerRect.left + DESKTOP_INDICATOR_INSET, width: itemRect.width - DESKTOP_INDICATOR_INSET * 2, }; } function measureMobileIndicator(itemRect: DOMRect, containerRect: DOMRect) { const center = itemRect.left + itemRect.width / 2 - containerRect.left; return center - MOBILE_INDICATOR_WIDTH / 2; } /** * Tracks the active navigation item's position, recalculating on resize * and visibility changes across breakpoints. */ function useActiveIndicator( activeIndex: number, containerRef: React.RefObject, itemRefs: React.MutableRefObject<(HTMLElement | null)[]>, measure: (itemRect: DOMRect, containerRect: DOMRect) => T, ): { value: T | null; instant: boolean } { const [value, setValue] = useState(null); const instantRef = useRef(true); useLayoutEffect(() => { instantRef.current = false; const update = () => { if (activeIndex === -1) { setValue(null); return; } const item = itemRefs.current[activeIndex]; const container = containerRef.current; if (item && container && container.offsetWidth > 0) { setValue( measure( item.getBoundingClientRect(), container.getBoundingClientRect(), ), ); } else { setValue(null); } }; update(); const container = containerRef.current; if (!container) return; const observer = new ResizeObserver(() => { instantRef.current = true; update(); }); observer.observe(container); return () => observer.disconnect(); }, [activeIndex, containerRef, itemRefs, measure]); return { value, instant: instantRef.current }; } export function NavBar({ userName, userEmail, userImage, userRole, }: { userName: string; userEmail: string; userImage?: string; userRole?: string; }) { const router = useRouter(); const pathname = usePathname(); const setCommandPaletteOpen = useSetAtom(commandPaletteOpenAtom); const initial = userName?.charAt(0).toUpperCase() ?? "?"; const activeIndex = navLinks.findIndex((link) => isLinkActive(pathname, link.href), ); const navRef = useRef(null); const linkRefs = useRef<(HTMLAnchorElement | null)[]>([]); const { value: indicator, instant: desktopInstant } = useActiveIndicator( activeIndex, navRef, linkRefs, measureDesktopIndicator, ); return (
{/* Mobile search trigger */} {/* Desktop search trigger pill */} {/* User avatar dropdown */} {initial}
{initial}

{userName} {userRole === "admin" && ( Admin )}

{userEmail}

} className="cursor-pointer text-[13px]" > Settings { await signOut(); router.push("/"); router.refresh(); }} className="cursor-pointer text-[13px]" > Sign out
{/* Mobile: simple avatar link to settings */} {initial}
); } export function MobileTabBar() { const pathname = usePathname(); const activeIndex = mobileTabs.findIndex((tab) => isLinkActive(pathname, tab.href), ); const containerRef = useRef(null); const tabRefs = useRef<(HTMLAnchorElement | null)[]>([]); const { value: indicatorLeft, instant: mobileInstant } = useActiveIndicator( activeIndex, containerRef, tabRefs, measureMobileIndicator, ); return ( ); }