--- phase: 29-image-presentation plan: 05 type: execute wave: 1 depends_on: [] files_modified: - src/client/components/ImageUpload.tsx autonomous: true gap_closure: true requirements: [] must_haves: truths: - "After cropping in the upload crop editor, the GearImage preview immediately reflects the crop values without needing to save the form" artifacts: - path: "src/client/components/ImageUpload.tsx" provides: "Local crop state that feeds GearImage preview" contains: "cropZoom" key_links: - from: "ImageCropEditor onSave" to: "GearImage cropZoom/cropX/cropY props" via: "local state in ImageUpload" pattern: "localCrop" --- Fix cropped image preview not updating immediately after cropping in edit mode. Purpose: When a user crops an image via the ImageCropEditor inside ImageUpload, the preview should reflect the crop immediately — not only after form save and query refetch. Output: ImageUpload component with local crop state that feeds into GearImage preview props. @$HOME/.claude/get-shit-done/workflows/execute-plan.md @$HOME/.claude/get-shit-done/templates/summary.md @.planning/PROJECT.md @.planning/ROADMAP.md @.planning/STATE.md @src/client/components/ImageUpload.tsx @src/client/components/GearImage.tsx @src/client/components/ImageCropEditor.tsx From src/client/components/GearImage.tsx: ```typescript interface GearImageProps { src: string; alt: string; dominantColor?: string | null; cropZoom?: number | null; cropX?: number | null; cropY?: number | null; className?: string; cover?: boolean; } ``` From src/client/components/ImageCropEditor.tsx: ```typescript interface CropResult { zoom: number; x: number; y: number; } // onSave: (result: CropResult) => void; ``` From src/client/components/ImageUpload.tsx: ```typescript interface ImageUploadProps { value: string | null; imageUrl?: string | null; dominantColor?: string | null; onChange: (filename: string | null, dominantColor?: string | null) => void; onCropChange?: (crop: { zoom: number; x: number; y: number }) => void; } ``` Task 1: Add local crop state to ImageUpload and wire to GearImage preview src/client/components/ImageUpload.tsx In ImageUpload.tsx, make these changes: 1. Add a local crop state to track the most recent crop values: ```typescript const [localCrop, setLocalCrop] = useState<{ zoom: number; x: number; y: number } | null>(null); ``` 2. In the ImageCropEditor onSave handler (around line 88-91), update localCrop before calling the parent onCropChange: ```typescript onSave={(result) => { setLocalCrop(result); onCropChange(result); setShowCropEditor(false); }} ``` 3. In the GearImage render (around line 110-114), pass localCrop values as props: ```typescript ``` 4. When the image is removed (handleRemove), also clear localCrop: ```typescript function handleRemove(e: React.MouseEvent) { e.stopPropagation(); setLocalPreview(null); setLocalCrop(null); onChange(null); } ``` This ensures the GearImage preview immediately reflects crop adjustments without waiting for a server round-trip and query refetch. cd /home/jlmak/Projects/jlmak/GearBox && bunx tsc --noEmit --pretty 2>&1 | head -30 After using the crop editor on an uploaded image, the GearImage preview in ImageUpload immediately shows the cropped framing. Removing the image clears both the preview and crop state. ## Trust Boundaries No new trust boundaries — this is a client-side-only state management fix within existing components. ## STRIDE Threat Register | Threat ID | Category | Component | Disposition | Mitigation Plan | |-----------|----------|-----------|-------------|-----------------| | T-29-05-01 | T (Tampering) | localCrop state | accept | Client-side display only; actual crop values are persisted via existing server mutation in parent component | 1. TypeScript compiles without errors 2. Manual: Open item in edit mode, upload image, crop it, verify preview shows crop immediately (without clicking Save) 3. Manual: Open existing item in edit mode, click crop button, adjust, save framing — preview updates immediately - Cropped image preview updates in edit state immediately after cropping, without needing to save the form - No TypeScript errors - Image removal clears crop state After completion, create `.planning/phases/29-image-presentation/29-05-SUMMARY.md`