'use client'; import { Plus } from 'lucide-react'; import { useRouter } from 'next/navigation'; import type { Route } from 'next'; import type { ReactNode } from 'react'; import { useUIStore } from '@/stores/ui-store'; import { Button } from '@/components/ui/button'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { Separator } from '@/components/ui/separator'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { BackButton } from '@/components/layout/back-button'; import { CommandSearch } from '@/components/search/command-search'; import { Inbox } from '@/components/layout/inbox'; import { UserMenu } from '@/components/layout/user-menu'; import type { Port } from '@/lib/db/schema/ports'; interface TopbarProps { ports: Port[]; user?: { name: string; email: string }; /** Optional leading slot rendered before the breadcrumbs on tablet * viewports - used by AppShell to mount a sidebar trigger button * (logo) when the sidebar is hidden behind a slide-over Sheet. */ leadingSlot?: ReactNode; } export function Topbar({ ports, user, leadingSlot }: TopbarProps) { const router = useRouter(); const currentPortSlug = useUIStore((s) => s.currentPortSlug); const base = currentPortSlug ? `/${currentPortSlug}` : ''; return ( // Three-column grid: smart back button left, search center, actions right. // The brand logo lives in the sidebar header so the topbar center is // dedicated to the global search bar. // // Grid is `auto auto 1fr` so the left + right columns size to their // actual content (back-button label on the left; New / Inbox / Avatar // on the right) and the search column soaks up the rest. // // Wayfinding model: the legacy breadcrumb chain was removed in favor // of a single contextual back button ("Back to Clients", "Back to // Sarah Doe"). Detail pages register their parent via // `useBreadcrumbHint` so the label is entity-aware; everything else // is URL-derived. See src/hooks/use-smart-back.ts.
{/* LEFT: optional sidebar trigger (tablet) + smart back button. Hard-capped width so the column never extends into the absolutely-positioned search bar's footprint. The cap is conservative on smaller widths to leave the search bar breathing room, more generous at xl. */}
{leadingSlot}
{/* CENTER: global search lives IN the 1fr grid track so it is bounded by the left (back-button) and right (actions) columns and can never overlap them. The previous approach absolutely positioned the bar at viewport-center, which ignored the side columns and crowded the "New" button at narrower widths (UAT 2026-06-03). `mx-auto` keeps it visually centered within the available middle space; `max-w-xl` stops it sprawling on wide screens; `min-w-0` lets it shrink rather than push the side columns. The grid `gap-3` guarantees breathing room from both neighbours. */}
{/* RIGHT: action row */}
{/* + New dropdown */} Create {/* Each item routes to the list page with ?create=1 so the relevant create sheet pops automatically (see useCreateFromUrl). The legacy `/clients/new`-style routes this menu used to push to landed on the dynamic detail page with id="new" and silently 404'd. */} {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} router.push(`${base}/clients?create=1` as any)}> New Client {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} router.push(`${base}/yachts?create=1` as any)}> New Yacht {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} router.push(`${base}/companies?create=1` as any)}> New Company {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} router.push(`${base}/interests?create=1` as any)}> New Interest {/* eslint-disable-next-line @typescript-eslint/no-explicit-any */} router.push(`${base}/expenses?create=1` as any)}> New Expense {/* /reminders 301s to /inbox#reminders (the merged page) and the server redirect strips the query string, so point straight at the new path. The Reminders section's useCreateFromUrl handler still picks up ?create=1. */} router.push(`${base}/inbox?create=1#reminders` as unknown as Route)} > New Reminder {/* Unified inbox - combines system alerts (operational) and personal notifications (user-targeted) into a single bell with two tabs. Replaces the previous AlertBell + NotificationBell pair. */} {/* User menu - single source of truth for the profile dropdown. Same component is mounted in the sidebar footer so the menu items (incl. port switcher) stay consistent across triggers. */} {(user?.name ?? 'U').slice(0, 1).toUpperCase()} } />
); }