feat(F-135): completed feature
This commit is contained in:
@@ -93,18 +93,54 @@ export function ImagesSection({ productId }: ImagesSectionProps) {
|
||||
|
||||
const handleFileInput = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file) uploadFile(file);
|
||||
if (file) {
|
||||
if (productId) uploadFile(file);
|
||||
else queueFile(file);
|
||||
}
|
||||
};
|
||||
|
||||
// Cola de archivos dropeados antes de tener productId — se suben automáticamente
|
||||
// cuando productId se setea desde el padre (F-135).
|
||||
const [pendingFiles, setPendingFiles] = useState<File[]>([]);
|
||||
const queueFile = (file: File) => {
|
||||
setPendingFiles(prev => [...prev, file]);
|
||||
};
|
||||
|
||||
const handleDrop = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
setDragOver(false);
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (file && file.type.startsWith('image/')) {
|
||||
uploadFile(file);
|
||||
const files = Array.from(e.dataTransfer.files).filter(f => f.type.startsWith('image/'));
|
||||
if (files.length === 0) return;
|
||||
if (productId) {
|
||||
// Subir inmediatamente (en serie para evitar carreras)
|
||||
void (async () => {
|
||||
for (const f of files) {
|
||||
await uploadFile(f);
|
||||
}
|
||||
})();
|
||||
} else {
|
||||
// Pendiente: encolar para subir tras crear el producto
|
||||
setPendingFiles(prev => [...prev, ...files]);
|
||||
}
|
||||
};
|
||||
|
||||
// Efecto: cuando productId pasa de undefined a definido, subir la cola
|
||||
const prevProductIdRef = useRef<string | undefined>(productId);
|
||||
useEffect(() => {
|
||||
const prev = prevProductIdRef.current;
|
||||
prevProductIdRef.current = productId;
|
||||
if (!productId || prev === productId) return;
|
||||
if (pendingFiles.length === 0) return;
|
||||
const queue = pendingFiles;
|
||||
setPendingFiles([]);
|
||||
void (async () => {
|
||||
for (const f of queue) {
|
||||
await uploadFile(f);
|
||||
}
|
||||
})();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [productId]);
|
||||
|
||||
const setMain = async (imageId: string) => {
|
||||
// Reorder: put this image first
|
||||
await fetch(`/api/products/${productId}/images/reorder`, {
|
||||
@@ -126,12 +162,13 @@ export function ImagesSection({ productId }: ImagesSectionProps) {
|
||||
};
|
||||
|
||||
if (!productId) {
|
||||
// Pending state: render UI with disabled actions and a caption.
|
||||
// Pending state: render UI con drop zone funcional — los archivos dropeados
|
||||
// se encolan y se suben automáticamente cuando productId se setea (F-135).
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="text-sm font-bold text-gray-900">Imágenes</h3>
|
||||
<span className="text-xs text-gray-400">Se guardarán al crear el producto</span>
|
||||
<span className="text-xs text-gray-400">Se subirán al crear el producto</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 sm:flex-row opacity-50 pointer-events-none">
|
||||
<input
|
||||
@@ -156,10 +193,22 @@ export function ImagesSection({ productId }: ImagesSectionProps) {
|
||||
📤 Subir imagen
|
||||
</button>
|
||||
</div>
|
||||
<div className="border-2 border-dashed border-gray-200 rounded-xl p-8 text-center">
|
||||
<p className="text-gray-400 text-sm">
|
||||
<div
|
||||
onDragOver={e => { e.preventDefault(); setDragOver(true); }}
|
||||
onDragLeave={() => setDragOver(false)}
|
||||
onDrop={handleDrop}
|
||||
className={`border-2 border-dashed rounded-xl p-8 text-center transition-colors ${
|
||||
dragOver ? 'border-[#2D6A4F] bg-[#2D6A4F]/5' : 'border-gray-300'
|
||||
}`}
|
||||
>
|
||||
<p className="text-gray-600 text-sm">
|
||||
🖼️ Arrastra imágenes aquí para añadirlas al producto
|
||||
</p>
|
||||
{pendingFiles.length > 0 && (
|
||||
<p className="mt-2 text-xs text-amber-700">
|
||||
{pendingFiles.length} imagen{pendingFiles.length === 1 ? '' : 'es'} en cola — se subirán al crear el producto
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user