MosswartOverlord/docs/superpowers/specs/2026-07-15-inventory-search-redesign-design.md
Erik edf76f50db docs(spec): inventory search redesign — React full-page view + approved mockup
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-15 05:57:10 +02:00

6 KiB

Inventory Search redesign — React full-page view

Date: 2026-07-15 Status: Approved (layout + behaviors validated interactively in visual-companion mockup) Mockup: 2026-07-15-inventory-search-redesign-mockup.html (same directory — the approved clickable reference)

Problem

The inventory search UI (static/inventory.html + inventory.js, ~2100 lines of 2010s-era dense gray vanilla HTML) is functionally complete (all filters work against the Go inventory-go service, including the July 2026 spell filters) but visually dated and inconsistent with the React dashboard. Full redesign requested.

Decisions (user-validated)

  1. Platform: new full-page view in the existing React app (frontend/), reached via /?view=inventory — same routing pattern as ?view=dashboard in App.tsx.
  2. Layout: facet sidebar (direction A of three mocked options) + active- filter chips row borrowed from direction B.
  3. Search trigger: instant auto-search, debounced ~400 ms, no Search button; in-flight requests aborted via AbortController.
  4. Item details: right-side detail panel opened by row click; ↑/↓ moves selection, Esc closes.
  5. Set analysis: dropped entirely. The new page fully replaces inventory.html/inventory.js — both deleted at cutover; the "Analyze Sets" feature ceases to exist.
  6. Shareable URLs: filter state serialized to the query string.

Layout (per approved mockup)

  • Top bar: title, name/material search box (text param), Reset (clears all filters), column picker (⚙), result count + query time.
  • Chips row: one removable chip per active filter; legendary-cantrip chips styled gold (#fc6 on #2a2418).
  • Facet sidebar (~210 px, collapsible groups with active-count badges):
    • Characters — searchable checkbox list, All/None/Online links, green online dots (from the /live data the app already polls).
    • Item type — radios: All / Armor / Jewelry / Weapons (+subtype) / Clothing (maps to armor_only/jewelry_only/weapon_only+weapon_type/ clothing_only; shirts/pants reachable as clothing subtypes).
    • Slots — jewelry slots (Ring/Bracelet/Neck/Trinket/Cloak) up front, armor slots behind a "show armor slots" expander (slot_names CSV).
    • Cantrips — find-as-you-type filter over the legendary cantrip list, grouped (Attributes / Skills / Defense / Banes / Other); checked items pin to a "Selected" section on top; gold styling; plus an "any-tier spell search" text input (spell_contains). Checkbox values = the same display names the old page sent (legendary_cantrips CSV, AND semantics).
    • Ratings (collapsed) — min inputs for the 4 common ratings, "all 20 ratings" expander for the rest (min_* params).
    • Equipment sets (collapsed) — searchable set list (item_set/item_sets).
    • Item state (collapsed) — equipped-only, bonded, attuned, rare (equipment_status, bonded, attuned, is_rare).
    • Reqs & value (collapsed) — wield level ≤, value ≥, workmanship ≥, burden ≤ (max_level, min_value, min_workmanship, max_burden).
  • Results table: server-side sort via sort_by/sort_dir (click headers); sticky header; equipped marker (green ⚔); Spells column with legendary names gold; pagination footer (200/page default, page/limit). Column set togglable via the ⚙ picker, persisted to localStorage.
  • Detail panel (~250 px): item name, slot, equipped state, character, value/burden/wield/workmanship/mana, full ordered spell list (legendary gold). Keyboard: ↑/↓/Esc.

Implementation shape

  • No backend changes. The existing GET /api/inv/search/items supports every filter above (validated; the spell filters shipped 2026-07-14).
  • New directory frontend/src/components/inventory/:
    • InventorySearchPage.tsx — page layout, keyboard handling.
    • FilterSidebar.tsx + one small component per filter group.
    • ActiveChips.tsx, ResultsTable.tsx, DetailPanel.tsx.
    • useInventorySearch.ts — single-reducer filter state, debounce, AbortController fetch, URL (de)serialization.
  • Filter state ↔ URL: serialize non-default filters into query params on change (replaceState), parse on mount — bookmarkable searches.
  • Styling: frontend/src/styles/inventory.css, .inv-* class prefix, dashboard dark tokens (#111 bg, #1a1a1a panels, #333 borders, #88f accent, #fc6 legendary gold, Segoe UI) — mirrors map-layout.css conventions.
  • No new npm dependencies.
  • Cantrip list + grouping: lift the value strings from the old inventory.html checkbox grid (they are the strings the backend matches); store as a typed constant module.
  • Character list: reuse the app's existing live-players data source for names
    • online status; fall back to /api/inv/characters/list for characters that are offline/never online.

Cutover

Same commit series: add the new view → repoint the dashboard tool-link that currently opens /inventory.html to /?view=inventory → delete static/inventory.html and static/inventory.js. /inventory.html then 404s to the SPA fallback; acceptable (single-user tool, no external links).

Testing & verification

The frontend has no test framework (build = tsc -b && vite build). Verification per task: TypeScript build green + live checks in the Vite dev server against the production API, then post-deploy spot checks of the same scenarios used for the spell-filter validation (Invuln+Summon → 2 items; Ring+Invuln → 56; filterless jewelry → ~4.8k). Adding a test harness is out of scope.

Deploy

bash deploy-frontend.sh (builds + copies into static/), commit static/

  • frontend/ + deletions, push; server picks it up via bind mount, no restart.

Out of scope

  • Set analysis (dropped per decision 5).
  • Suitbuilder page (untouched).
  • React-window (InventoryWindow.tsx) per-character inventory — unrelated.
  • Mobile layout (desktop tool); no test framework introduction.