Files
GearBox/.planning/research/FEATURES.md

19 KiB

Feature Research: v1.2 Collection Power-Ups

Domain: Gear management -- search/filter, weight classification, weight visualization, candidate status tracking, weight unit selection Researched: 2026-03-16 Confidence: HIGH Scope: New features only. v1.0/v1.1 features (item CRUD, categories, threads, setups, dashboard, onboarding, images, icons) are already shipped.

Table Stakes

Features that gear management users expect. Missing these makes the app feel incomplete for collections beyond ~20 items.

Feature Why Expected Complexity Dependencies on Existing
Search items by name Every competitor with an inventory concept has search. Hikt highlights "searchable digital closet." PackLight Supporter Edition has inventory search. Once a collection exceeds 30 items, scrolling to find something is painful. LighterPack notably lacks this and users complain. LOW Items query (useItems), collection view. Client-side only -- no API changes needed for <500 items.
Filter items by category Already partially exists in Planning view (category dropdown for threads). Collection view groups by category visually but has no filter. Users need to quickly narrow to "show me just my shelter items." LOW Categories query (useCategories), collection view. Client-side filtering of already-fetched items.
Weight unit selection (g, oz, lb, kg) Universal across all competitors. LighterPack supports toggling between g/oz/lb/kg. Packrat offers per-item input in any unit with display conversion. Backpacking Light forum users specifically praise apps that let you "enter item weights in grams and switch the entire display to lbs & oz." Gear specs come in mixed units -- a sleeping bag in lbs/oz, a fuel canister in grams. LOW formatWeight() in lib/formatters.ts, settings table (already exists with key/value store), TotalsBar, ItemCard, CandidateCard, SetupCard -- every weight display.
Weight classification (base/worn/consumable) LighterPack pioneered this three-way split and it is now universal. Hikt, PackLight, Packstack, HikeLite, 99Boulders spreadsheet -- all support it. "Base weight" is the core metric of the ultralight community. Without classification, weight totals are a single number with no actionable insight. MEDIUM setup_items join table (needs new column), setup detail view, setup service, totals computation. Schema migration required.

Differentiators

Features that set GearBox apart or add meaningful value beyond what competitors offer.

Feature Value Proposition Complexity Dependencies on Existing
Weight distribution visualization (donut/pie chart) LighterPack's pie chart is iconic and widely cited as its best feature. "The pie chart at the top is a great way to visualize how your pack weight breaks down by category." PackLight uses bar graphs. GearBox can do this per-setup with a modern donut chart that also shows base/worn/consumable breakdown -- a combination no competitor offers cleanly. MEDIUM Totals data (already computed server-side per category), weight classification (new), a chart library (react-minimal-pie-chart at 2kB or Recharts).
Candidate status tracking (researching/ordered/arrived) No competitor has this. Research confirmed: the specific workflow of tracking purchase status through stages does not exist in any gear management app. This is unique to GearBox's planning thread concept. It makes threads a living document of the purchase lifecycle, not just a comparison tool. LOW thread_candidates table (needs new status column), CandidateCard, CandidateForm. Simple text field migration.
Planning category filter with icon-aware dropdown Already partially built as a plain <select> in PlanningView. Upgrading to show Lucide icons alongside category names makes filtering feel polished and consistent with the icon picker UX. LOW Existing CategoryPicker component pattern, existing category filter state in PlanningView.
Weight classification shown per-setup (not global) In LighterPack, worn/consumable flags are per-item within a list. In GearBox, items exist in a global collection and appear in multiple setups. The same jacket might be "worn" in a summer bikepacking setup but "base weight" (packed in panniers) in a winter setup. Classification belongs on the setup_items join, not on the item itself. This is architecturally superior to competitors. MEDIUM setup_items table schema, setup sync endpoint, setup detail UI.

Anti-Features

Features to explicitly NOT build in this milestone.

Anti-Feature Why Avoid What to Do Instead
Per-item weight input in multiple units Packrat lets you enter "2 lb 3 oz" per item. This adds parsing complexity, ambiguous storage, and conversion bugs. Store grams internally (already done). Convert for display only. Users enter grams; if they want oz input, they convert mentally or we add a unit toggle on the input field later.
Interactive chart drill-down (click to zoom) LighterPack lets you click pie slices to zoom into category breakdowns. Adds significant interaction complexity. Static donut chart with hover tooltips. Drill-down is a future enhancement.
Weight goals / targets ("your target base weight is X") Some apps show ultralight thresholds. Adds opinionated norms that conflict with hobby-agnostic design. Show the numbers. Let users interpret them.
Custom weight classification labels Beyond base/worn/consumable. Some users want "luxury" or "shared" categories. Three classifications cover 95% of use cases. The notes field handles edge cases.
Server-side search / full-text search SQLite FTS5 or similar. Premature for a single-user app with <1000 items. Client-side filtering of the already-fetched items array. Simpler, faster for the expected data scale.
Worn/consumable at the global item level Tempting to add a classification column to the items table. Classification varies by setup context. A rain shell is "worn" on a day hike but "base weight" (packed) on a bike tour. The join table setup_items is the correct location.

Feature Details

1. Search and Filter

What users expect: A text input that filters visible items by name as you type. A category dropdown or pill selector to filter by category. Both should work together (search within a category).

Domain patterns observed:

  • Hikt: Searchable gear closet with category and specification filters
  • PackLight: Inventory search (premium feature) with category organization
  • Backpacking Light Calculator: Search filter in gear locker and within packs
  • LighterPack: No text search -- widely considered a gap

Recommended implementation:

  • Sticky search bar above the collection grid with a text input and category filter dropdown
  • Client-side filtering using Array.filter() on the items array from useItems()
  • Case-insensitive substring match on item name
  • Category filter as pills or dropdown (reuse the pattern from PlanningView)
  • URL search params for filter state (shareable filtered views, consistent with existing ?tab= pattern)
  • Clear filters button when any filter is active
  • Result count displayed ("showing 12 of 47 items")

Complexity: LOW. Pure client-side. No API changes. ~100 lines of new component code plus minor state management.

2. Weight Classification (Base/Worn/Consumable)

What users expect: Every item in a setup can be marked as one of three types:

  • Base weight: Items carried in the pack. The fixed weight of your loadout. This is the primary metric ultralight hikers optimize.
  • Worn weight: Items on your body while hiking (shoes, primary clothing, watch, sunglasses). Not counted toward pack weight but tracked as part of "skin-out" weight.
  • Consumable weight: Items that deplete during a trip (food, water, fuel, sunscreen). Variable weight not counted toward base weight.

Domain patterns observed:

  • LighterPack: Per-item icons (shirt icon = worn, flame icon = consumable). Default = base weight. Totals show base/worn/consumable/total separately.
  • Packstack: "Separates base weight, worn weight, and consumables so you always know exactly what your pack weighs."
  • HikeLite: "Mark heavy clothing as worn to see your true base weight."
  • 99Boulders spreadsheet: Column with dropdown: WORN / CONSUMABLE / - (dash = base).

Critical design decision -- classification scope: In LighterPack, items only exist within lists, so the flag is per-item-per-list inherently. In GearBox, items live in a global collection and are referenced by setups. The classification MUST live on the setup_items join table, not on the items table. Reason: the same item can have different classifications in different setups (a puffy jacket is "worn" on a cold-weather hike but "base weight" in a three-season setup where it stays packed).

Recommended implementation:

  • Add classification TEXT NOT NULL DEFAULT 'base' column to setup_items table
  • Valid values: "base", "worn", "consumable"
  • Default to "base" (most items are base weight; this matches user expectation)
  • UI: Small segmented control or icon toggle on each item within the setup detail view
  • LighterPack-style icons: backpack icon (base), shirt icon (worn), flame/droplet icon (consumable)
  • Setup totals recalculated: show base weight, worn weight, consumable weight, and total (skin-out) as four separate numbers
  • SQL aggregation update: SUM(CASE WHEN classification = 'base' THEN weight_grams ELSE 0 END) etc.

Complexity: MEDIUM. Requires schema migration, API changes (sync endpoint must accept classification), service layer updates, and UI for per-item classification within setup views.

3. Weight Distribution Visualization

What users expect: A chart showing where the weight is. By category is standard. By classification (base/worn/consumable) is a bonus.

Domain patterns observed:

  • LighterPack: Color-coded pie chart by category, click to drill down. "As you enter each piece of equipment, a pie chart immediately displays a breakdown of where your weight is appropriated." Colors are customizable per category.
  • PackLight: Bar graph comparing category weights
  • OutPack: Category breakdown graph

Two chart contexts:

  1. Collection-level: Weight by category across the whole collection. Uses existing useTotals() data.
  2. Setup-level: Weight by category AND by classification within a specific setup. More useful because setups represent actual loadouts.

Recommended implementation:

  • Donut chart (modern feel, consistent with GearBox's minimalist aesthetic)
  • Library: react-minimal-pie-chart (2kB gzipped, zero dependencies, SVG-based) over Recharts (40kB+). GearBox only needs pie/donut -- no line charts, bar charts, etc.
  • Setup detail view: Donut chart showing weight by category, with center text showing total base weight
  • Optional toggle: switch between "by category" and "by classification" views
  • Color assignment: Derive from category or classification type (base = neutral gray, worn = blue, consumable = amber)
  • Hover tooltips showing category name, weight, and percentage
  • Responsive: Chart should work on mobile viewports

Complexity: MEDIUM. New dependency, new component, integration with totals data. The chart itself is straightforward; the data aggregation for per-setup-per-category-per-classification is the main work.

4. Candidate Status Tracking

What users expect: This is novel -- no competitor has it. The workflow mirrors real purchase behavior:

  1. Researching (default): You found this product, added it to a thread, and are evaluating it
  2. Ordered: You decided to buy it and placed an order
  3. Arrived: The product has been delivered. Ready for thread resolution.

Why this matters: Without status tracking, threads are a flat list of candidates. With it, threads become a living purchase tracker. A user can see at a glance "I ordered the Nemo Tensor, still researching two other pads."

Recommended implementation:

  • Add status TEXT NOT NULL DEFAULT 'researching' column to thread_candidates table
  • Valid values: "researching", "ordered", "arrived"
  • UI: Status badge on CandidateCard (small colored pill, similar to existing weight/price badges)
  • Color scheme: researching = gray/neutral, ordered = amber/yellow, arrived = green
  • Status change: Dropdown or simple click-to-cycle on the candidate card
  • Thread-level summary: Show count by status ("2 researching, 1 ordered")
  • When resolving a thread, only candidates with status "arrived" should be selectable as winners (soft constraint -- show a warning, not a hard block, since users may resolve with a "researching" candidate they just bought in-store)

Complexity: LOW. Simple column addition, enum-like text field, badge rendering, optional status transition UI.

5. Weight Unit Selection

What users expect: Choose a preferred unit (grams, ounces, pounds, kilograms) and have ALL weight displays in the app use that unit. LighterPack toggles between g/oz/lb/kg at the top level. The BPL Calculator app lets you "enter item weights in grams and switch the entire display to lbs & oz."

Domain patterns observed:

  • LighterPack: Toggle at list level between lb/oz/g/kg. Only changes summary display, not per-item display.
  • Packrat: "Input items in different units, choose how they're displayed, and freely convert between them."
  • BPL Calculator: Global settings change, applied to all displays
  • WeighMyGear: Input locked to grams, less intuitive

Recommended implementation:

  • Store preference in existing settings table as { key: "weightUnit", value: "g" } (default: grams)
  • Supported units: g (grams), oz (ounces), lb (pounds + ounces), kg (kilograms)
  • Conversion constants: 1 oz = 28.3495g, 1 lb = 453.592g, 1 kg = 1000g
  • Display format per unit:
    • g: "450g" (round to integer)
    • oz: "15.9 oz" (one decimal)
    • lb: "2 lb 3 oz" (pounds + remainder ounces, traditional format)
    • kg: "1.45 kg" (two decimals)
  • Update formatWeight() to accept unit parameter or read from a React context/hook
  • Settings UI: Simple dropdown or segmented control, accessible from a settings page or inline in the TotalsBar
  • Internal storage stays as grams (already the case with weight_grams column)
  • Affects: TotalsBar, ItemCard, CandidateCard, SetupCard, CategoryHeader, setup detail view, chart tooltips

Complexity: LOW. No schema changes. Update the formatWeight() function, add a settings hook, propagate the unit to all display points. The main effort is touching every component that displays weight (there are ~6-8 call sites).

6. Planning Category Filter with Icon-Aware Dropdown

What users expect: The existing category filter in PlanningView is a plain <select> without icons. Since categories now have Lucide icons (v1.1), the filter should show them.

Recommended implementation:

  • Replace the native <select> with a custom dropdown component that renders <LucideIcon> alongside category names
  • Match the visual style of the CategoryPicker used in thread creation
  • Same functionality, better visual consistency

Complexity: LOW. UI-only change. Replace ~20 lines of <select> with a custom dropdown component.

Feature Dependencies

[Weight Unit Selection] --independent-- (affects all displays, no schema changes)
    |
    +-- should ship first (all other features benefit from correct unit display)

[Search & Filter] --independent-- (pure client-side, no schema changes)
    |
    +-- no dependencies on other v1.2 features

[Candidate Status Tracking] --independent-- (schema change on thread_candidates only)
    |
    +-- no dependencies on other v1.2 features

[Weight Classification] --depends-on--> [existing setup_items table]
    |
    +-- schema migration on setup_items
    +-- enables [Weight Distribution Visualization]

[Weight Distribution Visualization] --depends-on--> [Weight Classification]
    |
    +-- needs classification data to show base/worn/consumable breakdown
    +-- can show by-category chart without classification (partial value)
    +-- new dependency: react-minimal-pie-chart

[Planning Category Filter Icons] --depends-on--> [existing CategoryPicker pattern]
    |
    +-- pure UI enhancement

Implementation Order Rationale

  1. Weight Unit Selection first -- touches formatting everywhere, foundational for all subsequent weight displays
  2. Search & Filter second -- standalone, immediately useful, low risk
  3. Candidate Status Tracking third -- standalone schema change, simple
  4. Planning Category Filter fourth -- quick UI polish
  5. Weight Classification fifth -- most complex schema change, affects setup data model
  6. Weight Distribution Visualization last -- depends on classification, needs chart library, highest UI complexity

Complexity Summary

Feature Schema Change API Change New Dependency UI Scope Overall
Search & Filter None None None Collection view only LOW
Weight Unit Selection None (uses settings) None (settings API exists) None All weight displays (~8 components) LOW
Candidate Status Tracking thread_candidates.status column Update candidate CRUD None CandidateCard, CandidateForm LOW
Planning Category Filter None None None PlanningView dropdown LOW
Weight Classification setup_items.classification column Update setup sync + detail endpoints None Setup detail view MEDIUM
Weight Distribution Chart None Possibly new totals endpoint react-minimal-pie-chart (~2kB) New chart component MEDIUM

Sources


Feature research for: v1.2 Collection Power-Ups (search/filter, weight classification, visualization, candidate status, weight units) Researched: 2026-03-16