242 lines
6.6 KiB
Markdown
242 lines
6.6 KiB
Markdown
# 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_bounds
|
|
- `GET /api/items/{id}` — returns photo URLs
|
|
- `GET /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.tsx`
|
|
- `frontend/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):**
|
|
```json
|
|
{
|
|
"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:**
|
|
```json
|
|
{
|
|
"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
|
|
|
|
1. Dispatch Task 1 implementer (ItemPhotoUpload component)
|
|
2. Review spec compliance + code quality
|
|
3. Continue with remaining tasks
|
|
4. Final integration review before merge
|
|
|
|
Ready to proceed.
|