From 78a097cba2a1674177a37b7bd87f393558263438 Mon Sep 17 00:00:00 2001 From: Jean-Luc Makiola Date: Sun, 12 Apr 2026 20:04:29 +0200 Subject: [PATCH] feat(29-03): integrate crop editor into ImageUpload Show ImageCropEditor after successful upload when onCropChange callback is provided. Editor replaces image preview temporarily. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/client/components/ImageUpload.tsx | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/client/components/ImageUpload.tsx b/src/client/components/ImageUpload.tsx index dd6689a..805c63a 100644 --- a/src/client/components/ImageUpload.tsx +++ b/src/client/components/ImageUpload.tsx @@ -1,12 +1,14 @@ import { useRef, useState } from "react"; import { apiUpload } from "../lib/api"; import { GearImage, imageContainerBg } from "./GearImage"; +import { ImageCropEditor } from "./ImageCropEditor"; interface ImageUploadProps { value: string | null; imageUrl?: string | null; dominantColor?: string | null; onChange: (filename: string | null) => void; + onCropChange?: (crop: { zoom: number; x: number; y: number }) => void; } const MAX_SIZE_BYTES = 5 * 1024 * 1024; // 5MB @@ -17,10 +19,12 @@ export function ImageUpload({ imageUrl, dominantColor, onChange, + onCropChange, }: ImageUploadProps) { const [uploading, setUploading] = useState(false); const [error, setError] = useState(null); const [localPreview, setLocalPreview] = useState(null); + const [showCropEditor, setShowCropEditor] = useState(false); const inputRef = useRef(null); async function handleFileChange(e: React.ChangeEvent) { @@ -47,6 +51,9 @@ export function ImageUpload({ try { const result = await apiUpload<{ filename: string }>("/api/images", file); onChange(result.filename); + if (onCropChange) { + setShowCropEditor(true); + } } catch { setError("Upload failed. Please try again."); setLocalPreview(null); @@ -69,7 +76,23 @@ export function ImageUpload({ return (
+ {/* Crop editor overlay */} + {showCropEditor && displayUrl && onCropChange && ( +
+ { + onCropChange(result); + setShowCropEditor(false); + }} + onCancel={() => setShowCropEditor(false)} + /> +
+ )} + {/* Hero image area */} + {!showCropEditor && (
inputRef.current?.click()} className="relative w-full aspect-[4/3] rounded-xl overflow-hidden cursor-pointer group" @@ -154,6 +177,7 @@ export function ImageUpload({
)}
+ )}