From 683db58f4bc997e52f92de855b3ce10a9ef860d8 Mon Sep 17 00:00:00 2001 From: Mathieu Date: Mon, 8 Jun 2026 17:44:45 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20composant=20ImageCard=20avec=20miniatur?= =?UTF-8?q?es=20avant/apr=C3=A8s,=20feather,=20rotation,=20upload?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/images/ImageCard.tsx | 146 ++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 src/components/images/ImageCard.tsx diff --git a/src/components/images/ImageCard.tsx b/src/components/images/ImageCard.tsx new file mode 100644 index 0000000..77a490c --- /dev/null +++ b/src/components/images/ImageCard.tsx @@ -0,0 +1,146 @@ +'use client' +import { useEffect, useState } from 'react' +import type { ImageItem } from '@/types/images' + +interface ImageCardProps { + item: ImageItem + onFeatherChange: (id: string, value: number) => void + onRotate: (id: string, direction: 'cw' | 'ccw') => void + onUpload: (id: string) => void + onRemove: (id: string) => void + onOpenLightbox: (item: ImageItem) => void +} + +const STATUS_LABELS: Record = { + pending: { label: '⏳ En attente', color: 'text-slate-400' }, + converting: { label: '🔄 Conversion HEIC', color: 'text-yellow-400' }, + processing: { label: '🤖 Détourage IA', color: 'text-blue-400' }, + searching: { label: '🔍 Recherche…', color: 'text-purple-400' }, + ready: { label: '✓ Prêt', color: 'text-green-400' }, + uploading: { label: '⬆️ Upload…', color: 'text-blue-400' }, + uploaded: { label: '✅ Uploadé', color: 'text-green-500' }, + not_found: { label: '❌ Réf. introuvable', color: 'text-red-400' }, + error: { label: '❌ Erreur', color: 'text-red-400' }, +} + +function BlobThumbnail({ blob, alt }: { blob: Blob; alt: string }) { + const [url, setUrl] = useState(null) + + useEffect(() => { + const objectUrl = URL.createObjectURL(blob) + setUrl(objectUrl) + return () => URL.revokeObjectURL(objectUrl) + }, [blob]) + + if (!url) return
+ return {alt} +} + +export function ImageCard({ + item, + onFeatherChange, + onRotate, + onUpload, + onRemove, + onOpenLightbox, +}: ImageCardProps) { + const statusInfo = STATUS_LABELS[item.status] ?? { label: item.status, color: 'text-slate-400' } + const isProcessing = ['pending', 'converting', 'processing', 'searching', 'uploading'].includes(item.status) + const canAdjust = !!item.rawPngBlob && !isProcessing + + return ( +
+ {/* Miniatures avant / après */} +
onOpenLightbox(item)} + > +
+ +
+ {item.processedBlob && ( +
+ +
+ )} + {/* Barre de progression pendant l'IA */} + {item.status === 'processing' && item.progress && ( +
+
+
+
+

{item.progress.key} — {item.progress.percent}%

+
+ )} +
+ + {/* Infos */} +
+
+

{item.ref}

+ {item.shopifyProductTitle && ( +

{item.shopifyProductTitle}

+ )} +

{statusInfo.label}

+ {item.error &&

{item.error}

} +
+ + {/* Contrôles feather + rotation */} +
+ + onFeatherChange(item.id, Number(e.target.value))} + className="w-full accent-indigo-500" + /> + +
+ + +
+
+ + {/* Boutons d'action */} +
+ {item.status === 'ready' && ( + + )} + {item.status === 'uploaded' && item.uploadedUrl && ( + + Voir sur Shopify → + + )} + +
+
+
+ ) +}