7.7 KiB
7.7 KiB
Phase 35: Bug Fixes - Context
Gathered: 2026-04-19 Status: Ready for planning
## Phase BoundaryResolve 5 known v2.3 regressions and UX polish gaps before starting admin work. All fixes are self-contained — no new capabilities, no schema changes. The phase is complete when all 5 success criteria in ROADMAP.md are verifiably true.
## Implementation DecisionsFIX-01: Add Candidate modal (thread page)
- D-01: Wire the "Add Candidate" toolbar button on the thread detail page to open
CatalogSearchOverlay(same as the FAB), not the inlineAddCandidateModal. - D-02: Remove the inline
AddCandidateModalcomponent from the thread detail page entirely. Manual-add is already accessible viaCatalogSearchOverlay→ "Can't find it? Add manually" — no separate thread-page entry point needed. This deletes dead code. - D-03: The
CatalogSearchOverlaymust be opened in thread-candidate mode (pre-scoped to add a candidate to the current thread), same as the FAB already does it.
FIX-02: Item images missing on collection overview
- D-04: Add
imageUrl: string | null,dominantColor: string | null,cropZoom: number | null,cropX: number | null,cropY: number | null, andpriceCurrency: string | nullto theItemWithCategoryinterface insrc/client/hooks/useItems.ts. The server already returns these fields viawithImageUrls()and the DB query — the TypeScript type just hasn't been updated to include them. - D-05: No server-side changes needed —
GET /api/itemsalready enriches withimageUrlviawithImageUrls().
FIX-03: Slow image loading
- D-06: Scope is UX-only — do not touch presigned URL generation or caching. Presigned URL performance is deferred to a future phase.
- D-07: Add
loading="lazy"to all<img>tags across the app. - D-08: Add image skeleton/loading states (gray animated placeholder in the image area) to all card types that display images:
ItemCard,CandidateCard, and catalog item cards (GlobalItemCard, discovery cards). Use the existinganimate-pulsepattern already present on the collection overview skeleton loader.
FIX-04: Auth prompt sign-in redirect
- D-09: The
/loginReact route currently renders an intermediate "Sign in to GearBox" page that makes the user click a button before hitting Logto. Change it to auto-redirect immediately viauseEffect→window.location.href = "/login"(the server-side Logto redirect). The intermediate React UI page is not needed. - D-10:
AuthPromptModalalready links to/login(TanStack Router navigate) — no changes needed there once the React login route auto-redirects.
FIX-05: Cursor pointer on clickable elements
- D-11: Audit all interactive elements and add
cursor-pointerwhere missing. Known gaps:ItemCardouter button uses conditional cursor logic (cursor-defaultwhenlinkTo === null) — ensure all cases are covered. Check all cards, badges, action buttons, and links. - D-12: Add a global CSS rule
[role="button"] { cursor: pointer; }or use Tailwind'scursor-pointerconsistently on all clickable elements. Prefer Tailwind utility over global CSS to stay consistent with the codebase style.
Testing
- D-13: No new regression tests required for Phase 35. These are UI/type fixes — manual verification in the browser and existing passing tests are sufficient. The codebase already has 20+ test files; adding tests for cursor behavior and loading states has low ROI.
Claude's Discretion
- Which specific catalog card component file implements the loading skeleton for
GlobalItemCard/ discovery cards — researcher should identify the right component(s). - Whether
priceCurrencyis already in the items list API response or needs to be added server-side (likely already there given the currency system built in v2.3).
Folded Todos
All 5 pending todos are directly mapped to Phase 35 requirements and are folded into scope:
fix-add-candidate-button-shows-wrong-modal-on-thread-page→ FIX-01fix-item-image-not-showing-on-collection-overview→ FIX-02investigate-slow-image-loading→ FIX-03auth-prompt-sign-in-button-should-redirect-directly-to-logto→ FIX-04add-cursor-pointer-to-all-clickable-links→ FIX-05
<canonical_refs>
Canonical References
Downstream agents MUST read these before planning or implementing.
Phase Requirements
.planning/REQUIREMENTS.md— FIX-01 through FIX-05 acceptance criteria (the definition of done for each fix).planning/ROADMAP.md§Phase 35 — Success criteria and phase goal
Key Source Files
src/client/routes/threads/$threadId/index.tsx— Thread detail page with the broken "Add Candidate" button and the inlineAddCandidateModalto be removedsrc/client/hooks/useItems.ts—ItemWithCategoryinterface missingimageUrland related fields (FIX-02)src/client/components/GearImage.tsx— Core image component;loading="lazy"should be added heresrc/client/components/ItemCard.tsx— Image skeleton state targetsrc/client/components/CandidateCard.tsx— Image skeleton state targetsrc/client/components/GlobalItemCard.tsx— Image skeleton state target (catalog cards)src/client/routes/login.tsx— Intermediate login page that needs to auto-redirect (FIX-04)src/server/services/storage.service.ts—withImageUrls()/getImageUrl()— confirms server already returnsimageUrl
</canonical_refs>
<code_context>
Existing Code Insights
Reusable Assets
CatalogSearchOverlay(src/client/components/CatalogSearchOverlay.tsx) — already opens in thread-candidate mode via the FAB; FIX-01 wires the top button to the sameopenCatalogSearchZustand actionuseUIStore(src/client/stores/uiStore.ts) — hasopenCatalogSearchaction the FAB uses; FIX-01 calls this same actionGearImagecomponent — all image rendering goes through this;loading="lazy"added here propagates everywhereanimate-pulseskeleton pattern — already used on collection overview load state; reuse for image placeholders
Established Patterns
- Images:
imageFilenamestored on records → server enriches withimageUrlviawithImageUrls()→ client receives full presigned URL and passes toGearImage - Zustand UI state: modals and overlays opened via
useUIStoreactions, not local component state - Tailwind
cursor-pointeron interactive elements (not global CSS) useEffect+window.location.hreffor hard navigation (already used in login page for the button)
Integration Points
GET /api/items→ already returnsimageUrl— only the TypeScript type needs updatingopenCatalogSearchinuseUIStore— FIX-01 calls this from the thread page buttonAddCandidateModalinline component in thread route — delete this component and its state
</code_context>
## Specific Ideas- FIX-01: Delete the entire
AddCandidateModalfunction andaddCandidateOpenstate fromsrc/client/routes/threads/$threadId/index.tsx. Clean code removal, not a hide. - FIX-03:
loading="lazy"belongs on the<img>elements insideGearImage.tsx— one change, affects all usages. - FIX-04: The
LoginPagecomponent can be simplified to just auseEffectredirect (or even a minimal<Redirect>) — the full page UI with a button is unnecessary.
- Presigned URL server-side caching (TTL-based in-memory or Redis cache) — mentioned during FIX-03 discussion, intentionally out of scope for Phase 35. Consider for a future performance phase.
- Image resizing/thumbnails on upload — separate concern, deferred.
- Cache-Control headers on S3 objects — deferred.
Phase: 35-bug-fixes Context gathered: 2026-04-19