--- phase: 05-image-handling plan: 01 type: execute wave: 1 depends_on: [] files_modified: - src/client/components/ImageUpload.tsx - src/client/components/ItemForm.tsx - src/client/components/CandidateForm.tsx autonomous: true requirements: [IMG-01, IMG-03, IMG-04] must_haves: truths: - "Uploaded images display correctly in the ImageUpload preview area (not broken/missing)" - "Item form shows a full-width 4:3 hero image area at the top of the form" - "When no image is set, hero area shows gray background with centered icon and 'Click to add photo' text" - "Clicking the placeholder opens file picker and uploaded image replaces placeholder immediately" - "When image exists, a small circular X button in top-right removes the image" - "Clicking an existing image opens file picker to replace it" - "CandidateForm has the same hero area redesign as ItemForm" artifacts: - path: "src/client/components/ImageUpload.tsx" provides: "Hero image area with placeholder, upload, preview, remove" min_lines: 60 - path: "src/client/components/ItemForm.tsx" provides: "ImageUpload moved to top of form as first element" - path: "src/client/components/CandidateForm.tsx" provides: "ImageUpload moved to top of form as first element" key_links: - from: "src/client/components/ImageUpload.tsx" to: "/api/images" via: "apiUpload call in handleFileChange" pattern: "apiUpload.*api/images" - from: "src/client/components/ItemForm.tsx" to: "src/client/components/ImageUpload.tsx" via: "ImageUpload component at top of form" pattern: " Fix the image display bug so uploaded images render correctly, then redesign the ImageUpload component into a hero image preview area and move it to the top of both ItemForm and CandidateForm. Purpose: Images upload but don't display -- fixing this is the prerequisite for all image UX. The hero area redesign makes images prominent and the upload interaction intuitive (click placeholder to add, click image to replace). Output: Working image display, redesigned ImageUpload component, updated ItemForm and CandidateForm. @/home/jean-luc-makiola/.claude/get-shit-done/workflows/execute-plan.md @/home/jean-luc-makiola/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @src/client/components/ImageUpload.tsx @src/client/components/ItemForm.tsx @src/client/components/CandidateForm.tsx @src/client/lib/api.ts @src/server/routes/images.ts @src/server/index.ts @vite.config.ts From src/client/components/ImageUpload.tsx: ```typescript interface ImageUploadProps { value: string | null; onChange: (filename: string | null) => void; } ``` From src/client/lib/api.ts: ```typescript export async function apiUpload(url: string, file: File): Promise // Uses FormData with field name "image" ``` From src/server/routes/images.ts: ```typescript // POST /api/images -> { filename: string } (201) // Saves to ./uploads/{timestamp}-{uuid}.{ext} ``` From src/server/index.ts: ```typescript // Static serving: app.use("/uploads/*", serveStatic({ root: "./" })); ``` From vite.config.ts: ```typescript // Dev proxy: "/uploads": "http://localhost:3000" ``` Task 1: Fix image display bug and investigate root cause src/client/components/ImageUpload.tsx, src/server/routes/images.ts, src/server/index.ts, vite.config.ts Investigate why uploaded images don't render in the UI. The upload flow works (apiUpload POSTs to /api/images, server saves to ./uploads/ with UUID filename, returns { filename }), but images don't display. Debugging checklist (work through systematically): 1. Start dev servers (`bun run dev:server` and `bun run dev:client`) and upload a test image 2. Check the uploads/ directory -- does the file exist on disk? 3. Try accessing the image directly via browser: `http://localhost:5173/uploads/{filename}` -- does it load? 4. If not, try `http://localhost:3000/uploads/{filename}` -- does the backend serve it? 5. Check Vite proxy config in vite.config.ts -- `/uploads` proxy to `http://localhost:3000` is configured 6. Check Hono static serving in src/server/index.ts -- `serveStatic({ root: "./" })` should serve `./uploads/*` 7. Check if the `imageFilename` field is actually being saved to the database and returned by GET /api/items Common suspects: - The serveStatic middleware path might not match (root vs rewrite issue) - The imageFilename might not be persisted in the database (check the item update/create service) - The Vite proxy might need a rewrite rule Fix the root cause. If the issue is in static file serving, fix the serveStatic config. If it's a database persistence issue, fix the service layer. If it's a proxy issue, fix vite.config.ts. After fixing, verify an uploaded image displays at `/uploads/{filename}` in the browser. curl -s -o /dev/null -w "%{http_code}" http://localhost:3000/uploads/ 2>/dev/null; echo "Server static route configured" Uploaded images display correctly when referenced via /uploads/{filename} path. The root cause is identified, documented in the summary, and fixed. Task 2: Redesign ImageUpload as hero area and move to top of forms src/client/components/ImageUpload.tsx, src/client/components/ItemForm.tsx, src/client/components/CandidateForm.tsx Redesign ImageUpload.tsx into a hero image preview area per user decisions: **ImageUpload component redesign:** - Full-width container with `aspect-[4/3]` ratio (matches ItemCard) - Rounded corners (`rounded-xl`), overflow-hidden - The entire area is clickable (triggers hidden file input) **When no image (placeholder state):** - Light gray background (bg-gray-50 or bg-gray-100) - Centered Lucide `ImagePlus` icon (install lucide-react if not present, or use inline SVG) in gray-300/gray-400 - "Click to add photo" text below the icon in text-sm text-gray-400 - Cursor pointer on hover **When image exists (preview state):** - Full-width image with `object-cover` filling the 4:3 area - Small circular X button in top-right corner: `absolute top-2 right-2`, white/semi-transparent bg, rounded-full, ~28px, with X icon. onClick calls onChange(null) and stops propagation (so it doesn't trigger file picker) - Clicking the image itself opens file picker to replace **When uploading:** - Spinner overlay centered on the hero area (simple CSS spinner or Loader2 icon from lucide-react with animate-spin) - Semi-transparent overlay (bg-white/60 or bg-black/20) over the placeholder/current image **Error state:** - Red text below the hero area (same as current) **Move ImageUpload to top of forms:** - In ItemForm.tsx: Move the `` from the bottom of the form (currently after Product Link) to the very first element, BEFORE the Name field. Remove the wrapping `
` with the "Image" label -- the hero area is self-explanatory. - In CandidateForm.tsx: Same change -- move ImageUpload to the top, remove the "Image" label wrapper. Keep the existing ImageUploadProps interface unchanged ({ value, onChange }) so no other code needs updating. cd /home/jean-luc-makiola/Development/projects/GearBox && bun run lint 2>&1 | tail -5 ImageUpload renders as a 4:3 hero area with placeholder icon when empty, full image preview when set, spinner during upload, and X button to remove. Both ItemForm and CandidateForm show ImageUpload as the first form element. 1. Upload an image via ItemForm -- it should appear in the hero preview area immediately 2. The hero area shows a placeholder icon when no image is set 3. Clicking the placeholder opens the file picker 4. Clicking an existing image opens the file picker to replace 5. The X button removes the image 6. CandidateForm has identical hero area behavior 7. `bun run lint` passes - Uploaded images display correctly (bug fixed) - Hero image area renders at top of ItemForm and CandidateForm - Placeholder with icon shown when no image set - Upload via click works, preview updates immediately - Remove button clears the image After completion, create `.planning/phases/05-image-handling/05-01-SUMMARY.md`