8.5 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 05-image-handling | 01 | execute | 1 |
|
true |
|
|
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.
<execution_context> @/home/jean-luc-makiola/.claude/get-shit-done/workflows/execute-plan.md @/home/jean-luc-makiola/.claude/get-shit-done/templates/summary.md </execution_context>
@.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:
interface ImageUploadProps {
value: string | null;
onChange: (filename: string | null) => void;
}
From src/client/lib/api.ts:
export async function apiUpload<T>(url: string, file: File): Promise<T>
// Uses FormData with field name "image"
From src/server/routes/images.ts:
// POST /api/images -> { filename: string } (201)
// Saves to ./uploads/{timestamp}-{uuid}.{ext}
From src/server/index.ts:
// Static serving: app.use("/uploads/*", serveStatic({ root: "./" }));
From vite.config.ts:
// Dev proxy: "/uploads": "http://localhost:3000"
Debugging checklist (work through systematically):
- Start dev servers (
bun run dev:serverandbun run dev:client) and upload a test image - Check the uploads/ directory -- does the file exist on disk?
- Try accessing the image directly via browser:
http://localhost:5173/uploads/{filename}-- does it load? - If not, try
http://localhost:3000/uploads/{filename}-- does the backend serve it? - Check Vite proxy config in vite.config.ts --
/uploadsproxy tohttp://localhost:3000is configured - Check Hono static serving in src/server/index.ts --
serveStatic({ root: "./" })should serve./uploads/* - Check if the
imageFilenamefield 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.
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
ImagePlusicon (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-coverfilling 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
<ImageUpload>from the bottom of the form (currently after Product Link) to the very first element, BEFORE the Name field. Remove the wrapping<div>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<success_criteria>
- 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 </success_criteria>