# 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.