---
phase: quick
plan: 260411-1h2
subsystem: client/routes
tags: [global-items, filters, search, ui, sticky-toolbar]
dependency_graph:
  requires: []
  provides: [rebuilt-global-items-page]
  affects: [src/client/routes/global-items/index.tsx]
tech_stack:
  added: []
  patterns: [inline-filter-popovers, click-outside-useref, debounced-search, client-side-range-filter]
key_files:
  created: []
  modified:
    - src/client/routes/global-items/index.tsx
decisions:
  - Inline popovers (not sidebar) for tag/weight/price filters — matches plan spec, avoids layout shift
  - click-outside via useRef + mousedown listener (3 refs) — no framer-motion needed
  - GlobalItemListRow defined in same file — no new component file needed
metrics:
  duration: ~15 minutes
  completed: 2026-04-10T23:13:02Z
  tasks_completed: 1
  files_modified: 1
---

# Quick 260411-1h2: Rebuild Global Items Page with Sticky Toolbar Summary

**One-liner:** Rebuilt global items page with sticky two-row toolbar (search + view toggle + inline tag/weight/price filter popovers), grid/list view toggle using GlobalItemCard and Link-based list rows.

## Tasks Completed

| Task | Name | Commit | Files |
|------|------|--------|-------|
| 1 | Rebuild global items page with sticky toolbar, filters, and dual view | ee3b6f7 | src/client/routes/global-items/index.tsx |

## What Was Built

`src/client/routes/global-items/index.tsx` was completely rewritten from a simple 94-line grid-only page to a 640+ line full-featured catalog browsing page:

**Sticky toolbar** (`sticky top-14 z-[5]`) with two rows:
- Row 1: Back link to Discover, centered search input with clear (X) button, grid/list view toggle
- Row 2 (conditional): Tags, Weight, Price filter pills — only shown when tags exist or any filter is active

**Filter popovers:**
- Tags: dropdown listing all tags from `useTags()` — click to toggle, active tags highlighted blue, count badge on pill
- Weight: min/max range sliders (0–5000g, 50g steps) in a compact card popover
- Price: min/max range sliders (0–100000 cents, 500 cent steps) in a compact card popover
- All popovers close on click-outside via `useRef` + `mousedown` event listener
- Only one popover open at a time (opening one closes others)

**Active filter pills:**
- Selected tag names shown as removable blue pills
- Active weight/price range bounds shown as removable pills
- "Clear all" button clears all filters at once

**Results area:**
- Grid view: `GlobalItemCard` (existing component, untouched)
- List view: `GlobalItemListRow` (inline component, uses `<Link>` for navigation, no Add button)
- Loading: `SkeletonGrid` / `SkeletonList` (6-item animate-pulse layouts)
- Empty: contextual message based on whether query/filters are active

**Data flow:**
- `searchInput` debounced 300ms to `debouncedQuery` → passed to `useGlobalItems()`
- `selectedTags` passed to `useGlobalItems()` for server-side tag filtering
- Weight/price filters applied client-side via `useMemo`
- Search input pre-filled from `?q=` URL param on mount

## Deviations from Plan

None — plan executed exactly as written.

## Known Stubs

None — all functionality is wired to real data hooks.

## Self-Check

- [x] `src/client/routes/global-items/index.tsx` exists and is 640+ lines
- [x] Commit ee3b6f7 exists
- [x] `bun run lint` passes with no errors
- [x] `createFileRoute("/global-items/")` preserved
- [x] `validateSearch` with `z.object({ q: z.string().optional().catch(undefined) })` preserved
- [x] `CatalogSearchOverlay.tsx` untouched
- [x] `GlobalItemCard.tsx` untouched

## Self-Check: PASSED
