Appearance
Navigation & Dashboard Restructure — Design Spec
Date: 2026-07-09 Status: Approved by Brent (2026-07-09) Branch: claude/ui-navigation-settings-771007
Goal
Restructure the LGS Forge app navigation and landing experience for the launch userbase:
- Small/new stores (500–2,000 products, low/mid tier) watch their product count and import only what they need. Singles-level browsing and syncing must be the most accessible surface.
- Higher-end accounts sync by whole sets or one-click flows and spend little time in the app. Bulk set syncing stays one click deep under each game's Sets page.
- Growth: settings are grouped so new catalogs (FAB, Lorcana, board games, supplies like penny sleeves) slot in without another nav redesign.
Scope
In scope (this pass):
- Sidebar/navigation restructure with real routes for every view.
- New Dashboard "Store Overview" landing page with live data.
- Store Settings split into General / Catalog / Billing.
- New Queue page (unified job history).
- Per-game catalog sections: Singles, Sealed, Sets, Settings (Pricing / Sync Settings / Metafields).
- One backend change: per-store
enabledCatalogspreference.
Out of scope (future work):
- Real announcements/mailbox system (new collection, admin authoring, per-store read state). This pass ships a static placeholder card.
- Buylist feature. This pass ships a "coming soon" sidebar entry only.
- New games beyond MTG and Pokemon.
1. Sidebar & URL Map
Three sidebar groups in RadixShell.jsx. Every item is a real route. Embedded mode preserves the ?shop= param on all navigation (existing handleNavClick pattern).
Account
Dashboard /dashboard
Store Settings ▸ expands
General /settings/general
Catalog /settings/catalog
Billing /settings/billing
Queue /queue
Catalog (one entry per enabled game, from enabledCatalogs preference)
Magic: The Gathering ▸ expands
Singles /catalog/mtg/singles
Sealed /catalog/mtg/sealed
Sets /catalog/mtg/sets
Settings ▸ expands
Pricing /catalog/mtg/settings/pricing
Sync Settings /catalog/mtg/settings/sync
Metafields /catalog/mtg/settings/metafields
Pokemon ▸ same structure under /catalog/pokemon/...
+ Add catalog → navigates to /settings/catalog
Tools
Buylist "coming soon" (same disabled treatment as FAB/Lorcana today)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Expansion behavior
- Clicking a game (or Store Settings) toggles an indented submenu in the sidebar.
- The group containing the active route is auto-expanded on load.
- Game Settings is a third nesting level, matching the approved structure. If it proves cramped, it can later collapse to tabs inside one settings page — the routes stay the same either way.
Legacy redirects
| Old route | New route |
|---|---|
/sync | /catalog/mtg/sets |
/sync/pokemon | /catalog/pokemon/sets |
/settings | /settings/general |
/catalog | /catalog/mtg/singles |
/ (embedded) | /dashboard |
/catalog/:game (no sub-view) redirects to /catalog/:game/singles — Singles is the default landing per game. The embedded catch-all redirect (preserving shop) stays.
2. Dashboard — Store Overview (landing page)
Replaces the current Dashboard content. Header: "Store Overview" + "Managing {shop}". Built entirely from existing endpoints — no new backend.
- Store Info card (rows, with room to add more):
- Plan name + products used / remaining —
/api/billing/status+/api/variant-budget - Last product sync (most recent completed job) —
/api/sync/history?limit=1 - Last price sync —
/api/price-sync/history?limit=1
- Plan name + products used / remaining —
- Announcements / Mailbox card: static placeholder ("You're all caught up") with layout ready for a future feed. No backend.
- Activity section: last 5 jobs, merged from
/api/sync/historyand/api/price-sync/history, sorted by date descending. Status badges; row click opens the existingSyncJobDetailModal; "View all" links to/queue. - Existing banners stay: welcome, connection success/error, billing status.
- Standalone mode additionally keeps the ConnectedStores multi-store management on this page; embedded mode hides it (as today).
Data calls run in parallel; each card renders independently with its own loading/error state so one failed call doesn't blank the page.
3. Store Settings
/settings/general
NotificationSettings + SalesChannelSettings, moved from the old Settings tab. No metafields here — metafields are per-game (section 5).
/settings/catalog
Toggle list of catalogs:
- MTG, Pokemon: real toggles driving the
enabledCatalogsstore preference. Toggling off hides that game from the sidebar; it deletes nothing (synced products, schedules, and settings are untouched and reappear when re-enabled). - FAB, Lorcana, board games, supplies: shown as coming soon, not toggleable.
Existing stores default to both MTG and Pokemon enabled.
/settings/billing
Current plan + usage (reusing SubscriptionCard / billing status data) and a change-plan action linking to Shopify's managed-pricing plan selection page.
4. Queue — /queue
Unified job history: product syncs and price updates merged into one paginated table (job type, status, item counts, timestamps). Row click opens the existing detail modal. Built from SyncHistory / SyncJobDetailModal + /api/price-sync/history; no new backend.
5. Per-Game Catalog Pages
All routes take the game from the URL param (mtg | pokemon) — no more game-selector dropdown.
- /catalog/:game/singles — today's catalog Singles view (search/filters, grid/list toggle, prices, per-card sync controls). Primary surface for small/mid-tier stores.
- /catalog/:game/sealed — existing sealed browse/import views.
- /catalog/:game/sets — existing set browser plus the set-sync controls moved from the old MTG/Pokemon Sync pages (set selection, sync button, live sync status). Bulk set syncing for high-end accounts lives here.
- /catalog/:game/settings/pricing — pricing pipeline config (moved out of the old sync settings view).
- /catalog/:game/settings/sync — sync schedule + price update scheduling.
- /catalog/:game/settings/metafields —
SetupMetafields, scoped per game.
6. Backend Changes
One change: per-store enabledCatalogs preference.
- Add to the store preferences shape (array of game ids, e.g.
["mtg", "pokemon"]). - Expose in
GET /api/preferences; accept updates via the existing preferences update path. - Zod schema updated in
server/schemas/; validated viaserver/middleware/validate.js. - Missing/absent field is treated as all-enabled (backward compatible — no migration needed).
Everything else reuses existing endpoints.
7. Implementation Notes
- Dismantle
Home.jsx(~1,100 lines): SyncTab, SyncSettingsView, and SettingsTab contents get extracted into the new page components; the route-selection-by-prop pattern (initialTab) is removed.Home.jsxis deleted when nothing routes to it. - Split
CatalogTab.jsxviews so each new route mounts the right view directly. - New page components live in
client/src/pages/and compose extracted feature components; presentational pieces continue to useretrouiprimitives. RadixShell.jsxgains collapsible nav groups; the Catalog group is driven by theenabledCatalogspreference (via StoreContext).- Follow existing patterns: API calls through
client/src/utils/api.js, no Polaris, Tailwind + retroui styling.
8. Error Handling
- Overview cards degrade independently (per-card loading/error states).
- Unknown
:gameparam (or a disabled catalog's deep link) renders the game pages normally — the sidebar just won't highlight a group. Truly unknown games (not in/api/games) redirect to/dashboard. - All existing sync/billing error banners keep their current behavior.
9. Testing
Vitest (client config), keeping 70%+ coverage on touched files:
- RadixShell: group rendering, expansion/collapse, active-route auto-expand, enabledCatalogs-driven visibility, coming-soon items.
- Routing: each legacy redirect lands on the right new route with
shopparam preserved. - Store Overview: data assembly (merge/sort of last-5 activity), independent card error states.
- Settings > Catalog: toggle round-trip against mocked preferences API; default-all-enabled fallback.
- Queue: merged pagination of sync + price-sync histories.
- Server: preferences schema accepts/rejects
enabledCatalogsvalues correctly.
