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) <noreply@anthropic.com>
This commit is contained in:
2026-04-12 20:04:29 +02:00
parent 23f62fde3d
commit 78a097cba2

View File

@@ -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<string | null>(null);
const [localPreview, setLocalPreview] = useState<string | null>(null);
const [showCropEditor, setShowCropEditor] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
async function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
@@ -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 (
<div>
{/* Crop editor overlay */}
{showCropEditor && displayUrl && onCropChange && (
<div className="mb-4">
<ImageCropEditor
imageUrl={displayUrl}
dominantColor={dominantColor}
onSave={(result) => {
onCropChange(result);
setShowCropEditor(false);
}}
onCancel={() => setShowCropEditor(false)}
/>
</div>
)}
{/* Hero image area */}
{!showCropEditor && (
<div
onClick={() => inputRef.current?.click()}
className="relative w-full aspect-[4/3] rounded-xl overflow-hidden cursor-pointer group"
@@ -154,6 +177,7 @@ export function ImageUpload({
</div>
)}
</div>
)}
<input
ref={inputRef}