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)
- Platform: new full-page view in the existing React app (
frontend/), reached via/?view=inventory— same routing pattern as?view=dashboardinApp.tsx. - Layout: facet sidebar (direction A of three mocked options) + active- filter chips row borrowed from direction B.
- Search trigger: instant auto-search, debounced ~400 ms, no Search
button; in-flight requests aborted via
AbortController. - Item details: right-side detail panel opened by row click; ↑/↓ moves selection, Esc closes.
- Set analysis: dropped entirely. The new page fully replaces
inventory.html/inventory.js— both deleted at cutover; the "Analyze Sets" feature ceases to exist. - Shareable URLs: filter state serialized to the query string.
Layout (per approved mockup)
- Top bar: title, name/material search box (
textparam), 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
/livedata 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_namesCSV). - 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_cantripsCSV, 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).
- Characters — searchable checkbox list, All/None/Online links, green
online dots (from the
- 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 tolocalStorage. - 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/itemssupports 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) — mirrorsmap-layout.cssconventions. - No new npm dependencies.
- Cantrip list + grouping: lift the value strings from the old
inventory.htmlcheckbox 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/listfor characters that are offline/never online.
- online status; fall back to
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.