# GearBox

## What This Is

A gear management and discovery platform. Users catalog their gear collections (bikepacking, sim racing, or any hobby), track weight, price, and source details, research purchases through planning threads with side-by-side comparison, and compose named setups (loadouts) with weight classification and visualization. A global item database with crowd-verified specs and market-aware pricing helps users make informed purchase decisions. Multi-user with granular setup sharing (private/link/public), multi-currency support, and a fully internationalized UI (English + German).

## Core Value

Help people make better gear decisions — discover what others use, compare real-world data, and see how a potential buy affects your setup before committing.

## Current Milestone: v2.4 Admin Foundation

**Goal:** Clear the v2.3 bug backlog and ship a catalog admin panel as the foundation for managing global catalog content in future milestones.

**Target features:**
- Bug fixes: wrong add-candidate modal, missing item images on collection overview, slow image loading, auth prompt direct Logto redirect, cursor-pointer on all clickable elements
- Catalog admin panel: admin-gated route, global item management (browse/edit/delete), tag management (create/rename/parent-child hierarchy/delete), admin role flag on users

## Requirements

### Validated

- ✓ Gear collection with item CRUD (name, weight, price, category, notes, product link) — v1.0
- ✓ Image uploads for gear items — v1.0
- ✓ User-defined categories with automatic weight/cost totals — v1.0
- ✓ Planning threads for purchase research with candidate products — v1.0
- ✓ Thread resolution: pick a winner, it moves to collection — v1.0
- ✓ Named setups (loadouts) composed from collection items — v1.0
- ✓ Live weight and cost totals per setup — v1.0
- ✓ Dashboard home page with summary cards — v1.0
- ✓ Onboarding wizard for first-time setup — v1.0
- ✓ Thread creation with category assignment via modal dialog — v1.1
- ✓ Planning tab with educational empty state and pill tab navigation — v1.1
- ✓ Image display on item detail views and gear cards with placeholders — v1.1
- ✓ Hero image upload area with preview and click-to-upload — v1.1
- ✓ Lucide icon picker for categories (119 curated icons, 8 groups) — v1.1
- ✓ Automatic emoji-to-Lucide icon migration for existing categories — v1.1
- ✓ Search items by name with instant filtering — v1.2
- ✓ Filter collection items by category with icon-aware dropdown — v1.2
- ✓ Combined text search with category filter and result count — v1.2
- ✓ One-action filter clear — v1.2
- ✓ Weight unit selection (g, oz, lb, kg) with persistence — v1.2
- ✓ All weight displays respect selected unit across entire app — v1.2
- ✓ Per-setup item classification (base weight, worn, consumable) — v1.2
- ✓ Setup weight subtotals by classification — v1.2
- ✓ Donut chart visualization with category/classification toggle — v1.2
- ✓ Chart hover tooltips with weight and percentage — v1.2
- ✓ Candidate status tracking (researching/ordered/arrived) — v1.2
- ✓ Planning category filter with Lucide icons — v1.2
- ✓ Candidate pros/cons annotation and ranking with drag-to-reorder — v1.3
- ✓ Side-by-side candidate comparison table with weight/price deltas — v1.3
- ✓ Setup impact preview for candidates (replacement vs addition detection) — v1.3
- ✓ PostgreSQL database with async operations, PGlite test infra, Docker Compose — v2.0
- ✓ External OIDC auth via Logto with three-way auth middleware — v2.0
- ✓ Multi-user data model with userId isolation on all entities — v2.0
- ✓ S3 object storage (MinIO) for images replacing local filesystem — v2.0
- ✓ Global item catalog with search, owner count, and 18-item seed — v2.0
- ✓ User profiles with avatar/bio, public setup sharing — v2.0
- ✓ Reference item model with COALESCE merge for global-to-personal overlay — v2.0
- ✓ Tag system for catalog discovery with AND-filtered search — v2.0
- ✓ Global FAB with catalog search overlay and tag chip filtering — v2.0
- ✓ Item and catalog detail pages replacing slide-out panels — v2.0
- ✓ Add-from-catalog flow for collection items and thread candidates — v2.0
- ✓ Manual entry fallback with catalog submission prompt stub — v2.0
- ✓ Catalog attribution fields (sourceUrl, imageCredit, imageSourceUrl) on global items — v2.1
- ✓ Unique constraint on (brand, model) preventing catalog duplicates — v2.1
- ✓ Bulk import API with upsert semantics for catalog enrichment — v2.1
- ✓ MCP catalog tools (upsert_catalog_item, bulk_upsert_catalog) for agent seeding — v2.1
- ✓ Discovery landing page with catalog search, popular setups feed, recent items, trending categories — v2.1

- ✓ Profile page with Logto-powered account management (display name, bio, avatar, email, password, delete) — v2.2
- ✓ Image fit-within framing with dominant color background fill and crop editor — v2.2
- ✓ Catalog-driven onboarding flow with hobby picker, category-grouped item browser, and batch collection creation — v2.2
- ✓ Mobile icon-based action buttons on detail pages — v2.2

- ✓ Setup visibility toggle (private/link/public) with shares table, 128-bit token entropy, deactivate/reactivate on transition — v2.3
- ✓ ShareModal with Google Docs-style UX: visibility picker, link creation/expiry, revoke, deactivation warning — v2.3
- ✓ Shared setup viewer: `/s/:token` short URL, read-only mode, inline "Shared setup" banner — v2.3
- ✓ Multi-currency support: market_prices + community_prices tables, ECB exchange rates (24h cache), conversion service — v2.3
- ✓ Community price aggregation: ownership-validated submissions, median with 3-report minimum, market-aware MSRP on catalog detail — v2.3
- ✓ i18n foundation: react-i18next, 7 namespaces, English + German translations, language picker, locale-aware formatting — v2.3

### Active (v2.4)

- [ ] Fix wrong modal on Add Candidate button (thread page) — v2.4
- [ ] Fix item images not showing on collection overview — v2.4
- [ ] Resolve slow image loading — v2.4
- [ ] Auth prompt sign-in redirects directly to Logto — v2.4
- [ ] Cursor pointer on all clickable/interactive elements — v2.4
- [ ] Admin role flag on users table — v2.4
- [ ] Admin-gated /admin panel route — v2.4
- [ ] Admin: browse/edit/delete global catalog items — v2.4
- [ ] Admin: create/rename/delete tags with parent-child hierarchy — v2.4

### Future

- [ ] Tag-based spec schemas on global items (key/value typed specs per category, sub-tag hierarchy) — v2.5
- [ ] Global item engagement stats (view count, likes/saves, setup appearances) — v2.5
- [ ] Freeform reviews with moderation system
- [ ] Comments on setups
- [ ] Follow users / activity feeds
- [ ] OAuth / social login providers
- [ ] User-to-user messaging
- [ ] ComparisonTable currency normalization (hooks available, needs real multi-currency test data)

### Out of Scope

- Custom comparison parameters — complexity trap, weight/price covers 80% of cases
- Mobile native app — web-first, responsive design sufficient
- Price tracking / deal alerts — requires scraping, fragile
- Barcode scanning — poor UX, manual entry is fine with global database
- Real-time weather integration — only outdoor-specific, GearBox is hobby-agnostic
- Freeform UGC (reviews, comments) — defer until moderation infrastructure exists
- User-to-user messaging — high moderation burden, not core to discovery
- Wiki-style open item editing — structured contributions only for data quality
- Maintaining SQLite single-user mode in parallel — diverged at v2.0

## Context

Shipped through v2.3 with 34 phases across 7 milestones. All milestones v1.0-v2.3 complete.
Tech stack: React 19, Hono, Drizzle ORM, PostgreSQL, TanStack Router/Query, Tailwind CSS v4, Lucide React, Recharts, framer-motion, react-i18next, all on Bun.
Primary use case is bikepacking gear but data model is hobby-agnostic.
Auth: External OIDC via Logto (browser sessions) + API keys (programmatic) + MCP OAuth (Claude).
Infrastructure: PostgreSQL, MinIO (S3-compatible image storage), Docker Compose for dev/prod.
Features: MCP server (21 tools), global item catalog with attribution/bulk import/market prices, user profiles with Logto account management, granular setup sharing (private/link/public) with share tokens, multi-currency pricing (USD/EUR/GBP) with ECB rates and community aggregation, i18n (English + German, 7 namespaces), catalog-driven onboarding, fit-within image framing with crop editor, item/candidate detail pages, candidate ranking/comparison/impact preview. Public discovery landing page with catalog search, popular setups feed, recent items, and trending categories. Top nav + mobile bottom tab bar.
20+ test files (service-level, route-level integration, MCP). E2E tests pending rewrite for OIDC auth (backlog 999.1).

## Constraints

- **Runtime**: Bun — used as package manager and runtime
- **Design**: Light, airy, minimalist — white/light backgrounds, lots of whitespace, no visual clutter
- **Navigation**: Top nav bar (desktop) + bottom tab bar (mobile), discovery landing page for unauthenticated users
- **Auth**: External self-hosted provider — no in-house auth maintenance
- **Database**: PostgreSQL with Drizzle ORM
- **UGC**: Structured input only (ratings, predefined fields) — no freeform text until moderation exists
- **Scope**: Multi-user platform with public discovery

## Key Decisions

| Decision | Rationale | Outcome |
|----------|-----------|---------|
| Cookie/API key auth | Single user, public read + authenticated write | ✓ Good |
| Generic data model | Support any hobby, not just bikepacking | ✓ Good |
| Dashboard navigation | Clean entry point, not persistent nav | ✓ Good |
| Bun runtime | User preference | ✓ Good |
| Service layer with DI | Accept db as first param for testability | ✓ Good |
| Hono context variables for DB | Enables in-memory SQLite integration tests | ✓ Good |
| Prices stored as cents | Avoids float rounding issues | ✓ Good |
| Vite proxy dev setup | Required by TanStack Router plugin | ✓ Good |
| drizzle-kit needs better-sqlite3 | bun:sqlite not supported by CLI | ✓ Good |
| Tab navigation via URL params | Shareable URLs between gear/planning | ✓ Good |
| Setup item sync: delete-all + re-insert | Simpler than diffing, atomic in transaction | ✓ Good |
| Onboarding state in SQLite settings | Source of truth in DB, not Zustand | ✓ Good |
| Stay with SQLite | Single-user app, no need for Postgres complexity | ✓ Good |
| Lucide Icons for categories | Best outdoor/gear icon coverage, tree-shakeable, clean style | ✓ Good |
| categoryId on threads (NOT NULL FK) | Every thread belongs to a category | ✓ Good |
| Modal dialog for thread creation | Cleaner UX, supports category selection | ✓ Good |
| Hero image area at top of forms | Image-first UX, 4:3 aspect ratio consistent with cards | ✓ Good |
| Emoji-to-icon automatic migration | One-time schema rename + data conversion via Drizzle migration | ✓ Good |
| ALTER TABLE RENAME COLUMN for SQLite | Simpler than table recreation for column rename | ✓ Good |
| Platform pivot at v2.0 | Single-user model proven, now build for multi-user discovery | ✓ Good |
| External auth provider (Logto) | Avoid in-house auth security burden, self-hosted + open-source | ✓ Good |
| SQLite to Postgres | Multi-user platform needs proper concurrent DB; auth provider needs Postgres anyway | ✓ Good |
| Single-user mode diverges at v2.0 | Platform features irrelevant for solo use; maintained as separate artifact if needed | ✓ Good |
| Structured UGC only (no freeform) | Minimize moderation burden; ratings + predefined fields cover 80% of value | ✓ Good |
| Discovery-first, not social-first | Users come to research gear decisions, not to build social graphs | ✓ Good |
| COALESCE merge for reference items | Global base + personal overlay without data duplication | ✓ Good |
| Catalog-first add flow with manual fallback | Encourages catalog usage while preserving flexibility | ✓ Good |
| Detail pages replacing slide-out panels | Better UX for complex data, shareable URLs | ✓ Good |
| Weight conversion precision: g=0dp, oz=1dp, lb=2dp, kg=2dp | Matches common usage conventions | ✓ Good |
| Unit toggle in TotalsBar (not settings page) | Visible, quick access for frequent switching | ✓ Good |
| CategoryFilterDropdown separate from CategoryPicker | Filter vs form concerns are different | ✓ Good |
| No debounce on search input | Collection under 1000 items, instant feedback | ✓ Good |
| StatusBadge popup with click-outside dismiss | Consistent with CategoryPicker pattern | ✓ Good |
| Classification on setupItems join table | Same item can have different roles per setup | ✓ Good |
| Click-to-cycle for ClassificationBadge | Only 3 values, simpler than popup | ✓ Good |
| Classification-preserving sync via Map | Save metadata before delete, restore after re-insert | ✓ Good |
| Recharts for charting | Mature React chart library, composable API | ✓ Good |
| visibility text column (not boolean) | Future-proofs for additional sharing modes, readable in queries | ✓ Good |
| shares table separate from setups | Enables future per-person shares, write permissions, and revocation | ✓ Good |
| 128-bit base64url share tokens | URL-safe, sufficient entropy, no external dep | ✓ Good |
| Deactivate/reactivate on visibility change | Share links survive visibility round-trips, not destroyed | ✓ Good |
| EUR default price currency | Matches existing data assumption from early single-user era | ✓ Good |
| Module-level ECB rate cache | Simple, single-process, avoids DB or Redis for rate storage | ✓ Good |
| Community price median with 3-report floor | Prevents manipulation from single-user submissions | ✓ Good |
| i18next namespace-per-feature | Matches TanStack Router file-based routing, lazy-loadable | ✓ Good |
| localStorage language key (gearbox-language) | User preference wins over browser default in detection order | ✓ Good |

## Evolution

This document evolves at phase transitions and milestone boundaries.

**After each phase transition** (via `/gsd:transition`):
1. Requirements invalidated? → Move to Out of Scope with reason
2. Requirements validated? → Move to Validated with phase reference
3. New requirements emerged? → Add to Active
4. Decisions to log? → Add to Key Decisions
5. "What This Is" still accurate? → Update if drifted

**After each milestone** (via `/gsd:complete-milestone`):
1. Full review of all sections
2. Core Value check — still the right priority?
3. Audit Out of Scope — reasons still valid?
4. Update Context with current state

---
*Last updated: 2026-04-19 after v2.4 milestone start — Admin Foundation*
