---
phase: 27-top-nav-restructure-and-search-bar-rethink
plan: 01
type: execute
wave: 1
depends_on: []
files_modified:
  - src/client/components/TopNav.tsx
  - src/client/components/BottomTabBar.tsx
autonomous: true
requirements:
  - NAV-01
  - NAV-02
  - NAV-03

must_haves:
  truths:
    - "TopNav renders logo, Home/Collection/Setups links, search bar, and user avatar on desktop"
    - "Clicking Collection or Setups while anonymous calls openAuthPrompt instead of navigating"
    - "Active section is visually highlighted in the nav"
    - "BottomTabBar renders 4 tabs (Home, Collection, Setups, Search) with Lucide icons on mobile"
    - "Tapping Search tab calls openCatalogSearch"
  artifacts:
    - path: "src/client/components/TopNav.tsx"
      provides: "Persistent top navigation bar replacing TotalsBar"
      exports: ["TopNav"]
    - path: "src/client/components/BottomTabBar.tsx"
      provides: "Mobile bottom tab bar with 4 navigation items"
      exports: ["BottomTabBar"]
  key_links:
    - from: "src/client/components/TopNav.tsx"
      to: "src/client/stores/uiStore.ts"
      via: "openCatalogSearch, openAuthPrompt"
      pattern: "useUIStore.*openCatalogSearch|useUIStore.*openAuthPrompt"
    - from: "src/client/components/BottomTabBar.tsx"
      to: "src/client/stores/uiStore.ts"
      via: "openCatalogSearch, openAuthPrompt"
      pattern: "useUIStore.*openCatalogSearch|useUIStore.*openAuthPrompt"
---

<objective>
Create the two new navigation components: TopNav (desktop persistent nav bar) and BottomTabBar (mobile fixed bottom tab bar).

Purpose: These components are the core UI deliverables of Phase 27, replacing the minimal TotalsBar with full navigation. They implement D-01 through D-03 (top nav structure), D-07 (nav search bar), D-12 through D-14 (mobile bottom tab bar), and D-17 (search trigger from nav).

Output: Two new component files ready to be wired into __root.tsx in Plan 03.
</objective>

<execution_context>
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/27-top-nav-restructure-and-search-bar-rethink/27-CONTEXT.md
@.planning/phases/27-top-nav-restructure-and-search-bar-rethink/27-RESEARCH.md

<interfaces>
<!-- Key types and contracts the executor needs. -->

From src/client/stores/uiStore.ts:
```typescript
openCatalogSearch: (mode: "collection" | "thread") => void;
closeCatalogSearch: () => void;
openAuthPrompt: () => void;
closeAuthPrompt: () => void;
```

From src/client/hooks/useAuth.ts:
```typescript
// Returns { data: { user: User | null }, isLoading: boolean }
export function useAuth(): UseQueryResult<{ user: User | null }>;
```

From src/client/lib/iconData.tsx:
```typescript
// Renders a Lucide icon by name string. Available icons include: package, home, layers, search
export function LucideIcon({ name, size, className }: { name: string; size?: number; className?: string }): JSX.Element;
```

From src/client/components/UserMenu.tsx:
```typescript
export function UserMenu(): JSX.Element;
```

From src/client/components/TotalsBar.tsx (pattern reference — being replaced):
```typescript
// Sticky positioning pattern: "sticky top-0 z-10 bg-white border-b border-gray-100"
// Container: "mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"
// Height: "h-14"
```
</interfaces>
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create TopNav component</name>
  <files>src/client/components/TopNav.tsx</files>
  <read_first>
    src/client/components/TotalsBar.tsx
    src/client/stores/uiStore.ts
    src/client/components/UserMenu.tsx
    src/client/hooks/useAuth.ts
  </read_first>
  <action>
Create `src/client/components/TopNav.tsx` that replaces TotalsBar with a full navigation bar.

**Structure (per D-01):**
- Sticky top bar: `sticky top-0 z-10 bg-white border-b border-gray-100` (same as TotalsBar)
- Container: `mx-auto max-w-7xl px-4 sm:px-6 lg:px-8`, flex row, `h-14`
- Left: Logo — `<Link to="/">` with `<LucideIcon name="package" size={20} />` and "GearBox" text. Same styling as TotalsBar logo.
- Center: Desktop nav links (hidden on mobile with `hidden md:flex`) — Home, Collection, Setups
- Right: Search bar (desktop only) + UserMenu or "Sign in" link

**Active route detection (per D-03):**
```typescript
const matchRoute = useMatchRoute();
const isHome = !!matchRoute({ to: "/" });
const isCollection = !!matchRoute({ to: "/collection", fuzzy: true });
const isSetups = !!matchRoute({ to: "/setups", fuzzy: true });
```
Active link gets `text-gray-900`, inactive gets `text-gray-500 hover:text-gray-700`.

**Auth-gated nav links (per D-02):**
Create a `NavLinkOrButton` internal component. When `isAuthenticated` is false and the link is protected (Collection, Setups), render a `<button type="button" onClick={openAuthPrompt}>` styled identically to the link. When authenticated, render `<Link to={to}>`. Home is always a `<Link>` (not protected).

Do NOT use `<Link>` with `e.preventDefault()` — TanStack Router fires navigation before React event handlers can intercept reliably.

**Search bar (per D-07, D-17):**
Desktop only (`hidden md:flex`). Clickable div that calls `openCatalogSearch("collection")`. Include keyboard handler for Enter. Style: `bg-gray-50 border border-gray-200 rounded-lg cursor-pointer hover:border-gray-300`. Contains `<LucideIcon name="search" size={16} />` and placeholder text "Search catalog..." (text hidden below lg: `hidden lg:inline`).

**User section:**
Same as TotalsBar: `isAuthenticated ? <UserMenu /> : <Link to="/login">Sign in</Link>`. Avatar always visible (both mobile and desktop).

**Imports:**
- `Link, useMatchRoute` from `@tanstack/react-router`
- `useAuth` from `../hooks/useAuth`
- `LucideIcon` from `../lib/iconData`
- `useUIStore` from `../stores/uiStore`
- `UserMenu` from `./UserMenu`

Export: `export function TopNav()`
  </action>
  <verify>
    <automated>cd /home/jean-luc-makiola/Development/projects/GearBox && grep -c "export function TopNav" src/client/components/TopNav.tsx && grep -c "useMatchRoute" src/client/components/TopNav.tsx && grep -c "openAuthPrompt" src/client/components/TopNav.tsx && grep -c "openCatalogSearch" src/client/components/TopNav.tsx</automated>
  </verify>
  <acceptance_criteria>
    - src/client/components/TopNav.tsx exists and exports `export function TopNav()`
    - File imports `useMatchRoute` from `@tanstack/react-router`
    - File contains `openAuthPrompt` call for anonymous nav interception
    - File contains `openCatalogSearch("collection")` for search bar click
    - File contains `hidden md:flex` for desktop-only nav links
    - File contains `<UserMenu />` for authenticated users
    - File contains `<Link to="/login"` for anonymous "Sign in"
    - File uses `LucideIcon` (not direct lucide-react imports)
    - File does NOT contain `e.preventDefault` on Link elements
  </acceptance_criteria>
  <done>TopNav component renders logo, nav links (Home/Collection/Setups), search bar, and user section. Anonymous clicks on Collection/Setups fire AuthPromptModal. Active route is highlighted. Desktop nav links hidden on mobile.</done>
</task>

<task type="auto">
  <name>Task 2: Create BottomTabBar component</name>
  <files>src/client/components/BottomTabBar.tsx</files>
  <read_first>
    src/client/components/FabMenu.tsx
    src/client/stores/uiStore.ts
    src/client/hooks/useAuth.ts
    src/client/lib/iconData.tsx
  </read_first>
  <action>
Create `src/client/components/BottomTabBar.tsx` for mobile navigation.

**Structure (per D-13):**
- Fixed bottom: `fixed bottom-0 left-0 right-0 md:hidden z-20 bg-white border-t border-gray-100`
- z-20 so CatalogSearchOverlay (which uses higher z-index) renders above it
- 4 tab items in a flex row with `justify-around`
- Each tab: icon (LucideIcon, size 20) + label (text-xs) stacked vertically

**Tab items (per D-13, D-14):**
1. Home — icon: `home`, label: "Home", always `<Link to="/">`
2. Collection — icon: `package`, label: "Collection", `<Link to="/collection">` if authenticated, `<button onClick={openAuthPrompt}>` if anonymous (per D-02)
3. Setups — icon: `layers`, label: "Setups", `<Link to="/setups">` if authenticated, `<button onClick={openAuthPrompt}>` if anonymous (per D-02)
4. Search — icon: `search`, label: "Search", always `<button onClick={() => openCatalogSearch("collection")}>` (per D-14, D-17)

**Icon availability:** The `layers` icon is confirmed available in the curated icon set (`src/client/lib/iconData.tsx`). If for any reason it's not found at implementation time, use `briefcase` or `grid-2x2` as fallback.

**Active state:**
Use same `useMatchRoute` pattern as TopNav. Active tab: `text-gray-900`, inactive: `text-gray-400`. Search tab is never "active" (it opens an overlay, not a route).

**Entry animation (Framer Motion):**
```typescript
<motion.div
  initial={{ y: 20, opacity: 0 }}
  animate={{ y: 0, opacity: 1 }}
  transition={{ duration: 0.2, ease: "easeOut" }}
  className="fixed bottom-0 ..."
>
```

**Safe area:**
Add `pb-[env(safe-area-inset-bottom)]` to the container for iOS devices with home indicator.

**Imports:**
- `Link, useMatchRoute` from `@tanstack/react-router`
- `motion` from `framer-motion`
- `useAuth` from `../hooks/useAuth`
- `LucideIcon` from `../lib/iconData`
- `useUIStore` from `../stores/uiStore`

Export: `export function BottomTabBar()`
  </action>
  <verify>
    <automated>cd /home/jean-luc-makiola/Development/projects/GearBox && grep -c "export function BottomTabBar" src/client/components/BottomTabBar.tsx && grep -c "md:hidden" src/client/components/BottomTabBar.tsx && grep -c "openCatalogSearch" src/client/components/BottomTabBar.tsx && grep -c "openAuthPrompt" src/client/components/BottomTabBar.tsx && grep -c "layers" src/client/lib/iconData.tsx</automated>
  </verify>
  <acceptance_criteria>
    - src/client/components/BottomTabBar.tsx exists and exports `export function BottomTabBar()`
    - File contains `md:hidden` to only show on mobile
    - File contains `fixed bottom-0` for fixed positioning
    - File contains `z-20` for z-index
    - File contains 4 tab items: Home, Collection, Setups, Search (grep for all 4 labels)
    - File contains `openCatalogSearch("collection")` for Search tab
    - File contains `openAuthPrompt` for anonymous Collection/Setups taps
    - File uses `LucideIcon` with names: `home`, `package`, `layers` (or fallback `briefcase`/`grid-2x2`), `search`
    - File imports `motion` from `framer-motion` for entry animation
    - The `layers` icon exists in `src/client/lib/iconData.tsx` (verified: present)
  </acceptance_criteria>
  <done>BottomTabBar renders 4 tabs with Lucide icons on mobile viewports. Search tab opens CatalogSearchOverlay. Collection/Setups tabs fire AuthPromptModal for anonymous users. Active tab is highlighted. Component hidden on md+ screens.</done>
</task>

</tasks>

<verification>
- Both component files exist and export their named functions
- Both use `useMatchRoute` for active route detection
- Both use `openAuthPrompt` for anonymous user interception on protected links
- Both use `openCatalogSearch("collection")` for search trigger
- TopNav uses `hidden md:flex` for desktop nav; BottomTabBar uses `md:hidden` for mobile only
- Neither imports directly from `lucide-react` — both use `LucideIcon` wrapper
- The `layers` icon is available in the curated icon set
</verification>

<success_criteria>
- TopNav.tsx is a complete, self-contained component ready to replace TotalsBar in __root.tsx
- BottomTabBar.tsx is a complete, self-contained component ready to add to __root.tsx
- Both handle authenticated and anonymous states correctly
- Both follow existing codebase patterns (Tailwind, LucideIcon, uiStore, useAuth)
</success_criteria>

<output>
After completion, create `.planning/phases/27-top-nav-restructure-and-search-bar-rethink/27-01-SUMMARY.md`
</output>
