6.6 KiB
Phase 2: Frontend Photo Upload UI — Implementation Plan
Status: Planning → Ready for subagent dispatch
Branch: feature/phase2-photo-ui (created from dev)
Timeline: 2-3 weeks (estimated)
Prior work: Phase 1 backend complete (commit e46777b9 on dev)
Scope
Add frontend UI for photo upload, manual crop, and display. Backend API ready at:
POST /api/items/{id}/photo— upload with optional crop_boundsGET /api/items/{id}— returns photo URLsGET /images/{category}/{filename}— static file serving
Tasks (in priority order)
Task 1: ItemPhotoUpload Component
Complexity: Medium | Files: 2-3 | Model: Standard
Create reusable React component for photo upload flow:
- File input + camera capture (mobile)
- Accept multipart file upload
- Validate file size (<10MB), MIME type (image/jpeg, image/png, image/webp, image/gif)
- Show loading state during upload
- Return
{photo: {thumbnail_url, full_url, uploaded_at}} - Error handling (size, MIME, network)
Files to create/modify:
frontend/components/photos/ItemPhotoUpload.tsx(new)frontend/hooks/usePhotoUpload.ts(new)- Tests:
frontend/tests/ItemPhotoUpload.test.tsx
Acceptance criteria:
- Accepts file via input or camera capture
- Validates and uploads to
POST /api/items/{id}/photo - Shows success/error states
- Returns photo object
- Mobile camera works on iOS/Android
Task 2: Manual Crop UI with Drag Handles
Complexity: High | Files: 2-3 | Model: Most capable
Create interactive crop preview with drag handles:
- Display photo with bounding box (auto-crop from backend or manual)
- Four corner + four edge drag handles
- Real-time crop bounds calculation (x, y, width, height)
- "Use Full Photo" toggle
- "Apply Crop" button passes crop_bounds to upload
- Shows visual feedback (crosshairs, handles highlight on hover)
Files to create/modify:
frontend/components/photos/ManualCropUI.tsx(new)frontend/hooks/useCropHandles.ts(new)- Tests:
frontend/tests/ManualCropUI.test.tsx
Acceptance criteria:
- Drag any handle updates bounds in real-time
- Bounds sent as JSON:
{x: 100, y: 50, width: 300, height: 300} - Works on touch (mobile) and mouse (desktop)
- "Use Full Photo" clears crop_bounds
- Visually clear which handle is being dragged
Task 3: Integrate Photo Upload into Item Creation
Complexity: Medium | Files: 2-3 | Model: Standard
Add photo upload step to item creation flow:
- Item details form → Photo upload step
- Auto-crop preview from backend
- Manual crop override UI (always visible)
- Preview thumbnail before save
- Upload photo before/during item creation
Files to modify:
frontend/pages/items/create.tsx(or equivalent in app router)frontend/hooks/useItemCreate.ts- Tests: integration test for create flow
Acceptance criteria:
- Photo upload step appears in item creation
- Manual crop handles visible by default
- Users can toggle "Use Full Photo"
- Photo uploaded successfully before item saved
- Works on mobile camera capture
Task 4: Admin Photo Replacement Button
Complexity: Low | Files: 1-2 | Model: Fast
Add replace-photo button to admin dashboard:
- Show current thumbnail
- "Replace Photo" button → upload new file
- Delete old file on backend
- Confirm success/error
- Update item card thumbnail
Files to modify:
frontend/pages/admin/inventory.tsx(or items detail view)- Tests: button click, API call
Acceptance criteria:
- Button visible on item detail page
- Clicking opens photo upload modal
- New photo replaces old in thumbnail
- Backend deletes old file (via
replace_existing=true)
Task 5: Mobile Camera Integration & Testing
Complexity: Medium | Files: Tests | Model: Standard
Test photo flow on mobile (iOS/Android):
- Camera capture works
- Photo uploads successfully
- Manual crop works on touch
- Thumbnail displays correctly
- No lag or dropped frames during crop
Test scenarios:
- iPhone Safari: camera capture → crop → upload
- Android Chrome: camera capture → crop → upload
- Offline photo queue (Phase 5, skip for Phase 2)
Acceptance criteria:
- Camera captures work on iOS/Android
- Photos upload <3s on 4G
- Manual crop responsive to touch
- No console errors
Task 6: Inventory Card Photo Display
Complexity: Low | Files: 1-2 | Model: Fast
Update ItemCard component to show photo thumbnail:
- Show thumbnail (200px square) if photo exists
- Tap to open full-res modal (no carousel, just single image)
- Fallback to text label if no photo
- Add border/frame styling to distinguish photo
Files to modify:
frontend/components/ItemCard.tsxfrontend/components/photos/PhotoModal.tsx(new)- Tests: card renders photo, modal opens
Acceptance criteria:
- Thumbnail displays in card
- Tap opens modal with full-res photo
- Modal closeable (X button, click outside)
- Fallback text if no photo
- Works on mobile and desktop
Design Reference
Backend response (from Phase 1):
{
"status": "ok",
"photo": {
"thumbnail_url": "/images/networking/SFP-LR_thumb.jpg",
"full_url": "/images/networking/SFP-LR_original.jpg",
"uploaded_at": "2026-04-19T14:32:10Z"
}
}
Crop bounds format:
{
"x": 100,
"y": 50,
"width": 300,
"height": 300
}
Tech Stack
- Framework: Next.js 15+ (existing)
- Styling: Tailwind CSS (existing)
- Icons: Lucide Icons (existing)
- Image handling: Canvas API (built-in, no new dependencies)
- Form handling: React Hook Form (existing)
- HTTP: Axios (existing)
No new dependencies required.
Success Criteria (Phase 2 Complete)
✅ Photo upload with camera capture (mobile)
✅ Manual crop UI with drag handles
✅ Auto-crop preview from backend
✅ Photo integrated into item creation
✅ Admin replace-photo button
✅ Inventory card shows thumbnail
✅ Full-res photo modal viewer
✅ Mobile testing (iOS/Android)
✅ All components have tests
✅ No TypeScript errors
Known Dependencies
- Phase 1 backend — must be deployed and accessible
- Static file serving — /images/ mount working
- Photo API endpoints — POST/GET /api/items/{id}/photo
Out of Scope (Phase 3+)
- Offline photo queueing (Phase 5)
- Batch photo import (Phase 6)
- Photo compression on slow networks (Phase 6)
- Gallery/version history (not in scope)
Next Steps
- Dispatch Task 1 implementer (ItemPhotoUpload component)
- Review spec compliance + code quality
- Continue with remaining tasks
- Final integration review before merge
Ready to proceed.