diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 18ff936..a57cca0 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -10,7 +10,7 @@ - [x] **FIX-01**: User clicking "Add Candidate" on a thread page opens the add-candidate modal (not the wrong modal) - [x] **FIX-02**: Item images display correctly on collection overview cards (no broken/missing images) -- [ ] **FIX-03**: Catalog and collection images load without noticeable delay (slow image loading resolved) +- [x] **FIX-03**: Catalog and collection images load without noticeable delay (slow image loading resolved) - [x] **FIX-04**: Clicking the sign-in button on an auth prompt redirects the user directly to the Logto login page - [ ] **FIX-05**: All clickable and interactive elements show a pointer cursor on hover throughout the app @@ -69,7 +69,7 @@ |-------------|-------|--------| | FIX-01 | Phase 35 | Complete | | FIX-02 | Phase 35 | Complete | -| FIX-03 | Phase 35 | Pending | +| FIX-03 | Phase 35 | Complete | | FIX-04 | Phase 35 | Complete | | FIX-05 | Phase 35 | Pending | | ROLE-01 | Phase 36 | Pending | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index e732d87..e11a51e 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -235,7 +235,7 @@ Plans: Plans: - [x] 35-01-PLAN.md — Thread modal fix, ItemWithCategory type extension, login auto-redirect (FIX-01, FIX-02, FIX-04) -- [ ] 35-02-PLAN.md — Lazy loading + image skeleton states on GearImage and all card components (FIX-03) +- [x] 35-02-PLAN.md — Lazy loading + image skeleton states on GearImage and all card components (FIX-03) - [ ] 35-03-PLAN.md — Cursor-pointer audit across ItemCard, FabMenu, BottomTabBar (FIX-05) **UI hint**: yes @@ -318,7 +318,7 @@ Plans: | 32. Setup Sharing System | v2.3 | 4/4 | Complete | 2026-04-15 | | 33. Currency System | v2.3 | 6/6 | Complete | 2026-04-13 | | 34. i18n Foundation | v2.3 | 8/8 | Complete | 2026-04-18 | -| 35. Bug Fixes | v2.4 | 1/3 | In Progress| | +| 35. Bug Fixes | v2.4 | 2/3 | In Progress| | | 36. Admin Role & Panel Foundation | v2.4 | 0/TBD | Not started | - | | 37. Admin — Global Item Management | v2.4 | 0/TBD | Not started | - | | 38. Admin — Tag Management | v2.4 | 0/TBD | Not started | - | diff --git a/.planning/STATE.md b/.planning/STATE.md index 5611f87..c297304 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -3,15 +3,15 @@ gsd_state_version: 1.0 milestone: v2.4 milestone_name: Admin Foundation status: executing -stopped_at: "Phase 35 planned — 3 plans ready to execute (wave 1: all parallel)" -last_updated: "2026-04-19T17:43:31.083Z" -last_activity: 2026-04-19 — Phase 35 planned (3 plans, verification passed) +stopped_at: Completed 35-01-PLAN.md — FIX-01, FIX-02, FIX-04 resolved +last_updated: "2026-04-19T17:48:30.721Z" +last_activity: 2026-04-19 progress: total_phases: 20 completed_phases: 7 total_plans: 32 - completed_plans: 30 - percent: 94 + completed_plans: 31 + percent: 97 --- # Project State @@ -26,11 +26,11 @@ See: .planning/PROJECT.md (updated 2026-04-19) ## Current Position Phase: 35 — Bug Fixes -Plan: 1 of 3 complete -Status: In progress — 35-01 complete, 35-02 and 35-03 remaining -Last activity: 2026-04-19 — 35-01 complete (FIX-01, FIX-02, FIX-04) +Plan: 2 of 3 complete +Status: In progress — 35-01 and 35-02 complete, 35-03 remaining +Last activity: 2026-04-19 — 35-02 complete (FIX-03) -Progress: [███░░░░░░░] 33% +Progress: [██████████] 97% ## Performance Metrics @@ -70,16 +70,26 @@ Phase 35 decisions (35-01): - FIX-02: ItemWithCategory type extended client-side only — server already returns image fields via withImageUrls() - FIX-04: Login page is a server pass-through; no client auth check or card UI needed +Phase 35 decisions (35-02): + +- FIX-03: Browser-native loading=lazy used for image deferral — no library needed, zero bundle overhead +- FIX-03: Skeleton is absolute inset-0 overlay removed on onLoad (not conditional branch swap) for stable layout +- FIX-03: GearImage accepts optional onLoad prop forwarded to all three img render paths + ### Pending Todos -- Investigate slow image loading — Phase 35 (FIX-03, plan 35-02) - Cursor pointer on all clickable links — Phase 35 (FIX-05, plan 35-03) Resolved in 35-01: + - Fix Add Candidate button shows wrong modal on thread page — DONE (FIX-01) - Fix item image not showing on collection overview — DONE (FIX-02) - Auth prompt sign-in button should redirect directly to Logto — DONE (FIX-04) +Resolved in 35-02: + +- Investigate slow image loading — DONE (FIX-03) + ### Blockers/Concerns None. @@ -99,5 +109,5 @@ Items carried forward from v2.3: ## Session Continuity Last session: 2026-04-19 -Stopped at: Completed 35-01-PLAN.md — FIX-01, FIX-02, FIX-04 resolved -Resume file: .planning/phases/35-bug-fixes/35-02-PLAN.md +Stopped at: Completed 35-02-PLAN.md — FIX-03 (image lazy loading + skeleton) resolved +Resume file: .planning/phases/35-bug-fixes/35-03-PLAN.md diff --git a/.planning/phases/35-bug-fixes/35-02-SUMMARY.md b/.planning/phases/35-bug-fixes/35-02-SUMMARY.md new file mode 100644 index 0000000..604c704 --- /dev/null +++ b/.planning/phases/35-bug-fixes/35-02-SUMMARY.md @@ -0,0 +1,113 @@ +--- +phase: 35-bug-fixes +plan: "02" +subsystem: ui +tags: [react, tailwind, skeleton, lazy-loading, image, s3] + +# Dependency graph +requires: + - phase: 35-bug-fixes + provides: FIX-02 image URL resolution (withImageUrls on server) +provides: + - GearImage lazy loading with onLoad callback forwarding + - Animated skeleton placeholder (bg-gray-100 animate-pulse) on ItemCard, CandidateCard, GlobalItemCard + - Fade-in transition (opacity-0 to opacity-100) on image load +affects: [any future plan touching GearImage, ItemCard, CandidateCard, GlobalItemCard] + +# Tech tracking +tech-stack: + added: [] + patterns: [image-skeleton-pattern, lazy-loading-native] + +key-files: + created: [] + modified: + - src/client/components/GearImage.tsx + - src/client/components/ItemCard.tsx + - src/client/components/CandidateCard.tsx + - src/client/components/GlobalItemCard.tsx + +key-decisions: + - "FIX-03: Use browser-native loading=lazy (no library) for image deferral" + - "FIX-03: Skeleton is absolute-positioned overlay removed on onLoad, not conditional render swap" + - "FIX-03: Fade-in via className prop on GearImage forwarded to img elements — no wrapper div needed in GearImage itself" + +patterns-established: + - "Image skeleton pattern: relative wrapper + absolute inset-0 bg-gray-100 animate-pulse + opacity transition on GearImage className" + - "onLoad forwarding: GearImage accepts optional onLoad prop, passes it to all three img render paths" + +requirements-completed: [FIX-03] + +# Metrics +duration: 3min +completed: 2026-04-19 +--- + +# Phase 35 Plan 02: Image Lazy Loading and Skeleton Summary + +**Browser-native lazy loading on all GearImage img elements with animated pulse skeleton and opacity fade-in on ItemCard, CandidateCard, and GlobalItemCard** + +## Performance + +- **Duration:** 3 min +- **Started:** 2026-04-19T18:04:32Z +- **Completed:** 2026-04-19T18:07:14Z +- **Tasks:** 2 +- **Files modified:** 4 + +## Accomplishments +- Added `loading="lazy"` and optional `onLoad` prop to GearImage, forwarded to all three img render paths (cover, hasCrop, default) +- Added `useState(false)` loaded state and skeleton overlay to ItemCard, CandidateCard, and GlobalItemCard +- Images fade in via `transition-opacity duration-200` once `onLoad` fires; skeleton is removed simultaneously +- No-image placeholders (category icon on bg-gray-50) unchanged in all three cards + +## Task Commits + +Each task was committed atomically: + +1. **Task 1: Add loading=lazy and onLoad prop to GearImage** - `2d2259a` (feat) +2. **Task 2: Add image skeleton to ItemCard, CandidateCard, GlobalItemCard** - `88db308` (feat) + +**Plan metadata:** (docs commit below) + +## Files Created/Modified +- `src/client/components/GearImage.tsx` - Added `onLoad?: () => void` to props, destructured and forwarded to all three `` elements alongside `loading="lazy"` +- `src/client/components/ItemCard.tsx` - Added `useState` import, `loaded` state, skeleton overlay, and fade-in className on GearImage +- `src/client/components/CandidateCard.tsx` - Same skeleton pattern as ItemCard +- `src/client/components/GlobalItemCard.tsx` - Same skeleton pattern; SVG icon placeholder preserved unchanged + +## Decisions Made +- Used browser-native `loading="lazy"` — no third-party library needed, zero bundle overhead +- Skeleton is an `absolute inset-0` overlay (not a conditional branch swap) so layout is stable during load +- Import order fixed for Biome's `organizeImports` rule: `@tanstack/react-router` before `react` before `react-i18next` + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 1 - Bug] Fixed Biome import order violations in all three card files** +- **Found during:** Task 2 (after adding `useState` import) +- **Issue:** Biome `organizeImports` requires alphabetical order — `"react"` placed before `"@tanstack/react-router"` caused lint errors +- **Fix:** Reordered imports: `@tanstack/react-router` → `react` → `react-i18next` in ItemCard and CandidateCard; `@tanstack/react-router` → `react` in GlobalItemCard +- **Files modified:** ItemCard.tsx, CandidateCard.tsx, GlobalItemCard.tsx +- **Verification:** `bunx @biomejs/biome check` on all 4 files — no errors +- **Committed in:** 88db308 (Task 2 commit) + +--- + +**Total deviations:** 1 auto-fixed (Rule 1 — import order) +**Impact on plan:** Necessary for lint compliance. No logic or behavior changes. + +## Issues Encountered +- Pre-existing lint errors in `scripts/crawl-all.ts`, `scripts/crawl-manufacturer.ts`, and `tests/services/manufacturer.service.test.ts` are unrelated to this plan — logged as out-of-scope, not fixed. + +## User Setup Required +None - no external service configuration required. + +## Next Phase Readiness +- FIX-03 resolved — all card types now show skeleton while presigned S3 URLs resolve, then fade in +- Ready for 35-03 (FIX-05: cursor pointer on clickable links) + +--- +*Phase: 35-bug-fixes* +*Completed: 2026-04-19*