Image upload tweaks/fixes

This commit is contained in:
2026-04-02 14:04:56 -04:00
parent 54f8cc2706
commit e43abdafd0
3 changed files with 54 additions and 22 deletions

View File

@@ -329,7 +329,13 @@ export const ImageUploadStep = ({
handleAddImageFromUrl(index, urlInputs[index]);
}
}}
onImageUpload={(files: FileList | File[]) => handleImageUpload(files, index)}
onImageUpload={async (files: FileList | File[]) => {
const { skipped } = await handleImageUpload(files, index);
if (skipped > 0) {
const label = product.name || `Product #${index + 1}`;
toast.info(`Skipped ${skipped} duplicate ${skipped === 1 ? 'image' : 'images'} for ${label}`);
}
}}
onDragOver={(e: React.DragEvent) => {
e.preventDefault();
e.stopPropagation();

View File

@@ -2,7 +2,7 @@ import { useState } from "react";
import { toast } from "sonner";
import { UnassignedImage, Product } from "../types";
type HandleImageUploadFn = (files: FileList | File[], productIndex: number) => Promise<void>;
type HandleImageUploadFn = (files: FileList | File[], productIndex: number) => Promise<{ uploaded: number; skipped: number }>;
interface UseBulkImageUploadProps {
data: Product[];
@@ -88,27 +88,31 @@ export const useBulkImageUpload = ({ data, handleImageUpload }: UseBulkImageUplo
// Function to handle bulk image upload
const handleBulkUpload = async (files: File[]) => {
if (!files.length) return;
setProcessingBulk(true);
const unassigned: UnassignedImage[] = [];
let totalUploaded = 0;
let totalSkipped = 0;
for (const file of files) {
// Extract identifiers from filename
const identifiers = extractIdentifiers(file.name);
let assigned = false;
// Try to match each identifier
for (const identifier of identifiers) {
const productIndex = findProductByIdentifier(identifier);
if (productIndex !== -1) {
// Found a match, upload to this product
await handleImageUpload([file], productIndex);
const result = await handleImageUpload([file], productIndex);
totalUploaded += result.uploaded;
totalSkipped += result.skipped;
assigned = true;
break;
}
}
// If no match was found, add to unassigned
if (!assigned) {
unassigned.push({
@@ -117,15 +121,17 @@ export const useBulkImageUpload = ({ data, handleImageUpload }: UseBulkImageUplo
});
}
}
// Update unassigned images
setUnassignedImages(prev => [...prev, ...unassigned]);
setProcessingBulk(false);
// Show summary toast
const assignedCount = files.length - unassigned.length;
if (assignedCount > 0) {
toast.success(`Auto-assigned ${assignedCount} ${assignedCount === 1 ? 'image' : 'images'} to products`);
// Show summary toasts
if (totalUploaded > 0) {
toast.success(`Auto-assigned ${totalUploaded} ${totalUploaded === 1 ? 'image' : 'images'} to products`);
}
if (totalSkipped > 0) {
toast.info(`Skipped ${totalSkipped} ${totalSkipped === 1 ? 'image' : 'images'} already uploaded for their products`);
}
if (unassigned.length > 0) {
toast.warning(`Could not auto-assign ${unassigned.length} ${unassigned.length === 1 ? 'image' : 'images'}`);

View File

@@ -144,20 +144,37 @@ export const useProductImageOperations = ({
product.product_images = [product.product_images].filter(Boolean);
}
// Only add if the URL doesn't already exist
// Only add if the URL doesn't already exist (spread to avoid mutating frozen Immer arrays)
if (!product.product_images.includes(imageUrl)) {
product.product_images.push(imageUrl);
product.product_images = [...product.product_images, imageUrl];
}
return newData;
};
// Function to handle image upload - update product data
const handleImageUpload = async (files: FileList | File[], productIndex: number) => {
if (!files || files.length === 0) return;
const handleImageUpload = async (files: FileList | File[], productIndex: number): Promise<{ uploaded: number; skipped: number }> => {
if (!files || files.length === 0) return { uploaded: 0, skipped: 0 };
let uploaded = 0;
let skipped = 0;
for (let i = 0; i < files.length; i++) {
const file = files[i];
// Check for exact duplicate (same filename, size, and type already uploaded for this product)
const isDuplicate = productImages.some(
img => img.productIndex === productIndex &&
!img.loading &&
img.imageUrl &&
img.fileName === file.name &&
img.metadata?.size === file.size
);
if (isDuplicate) {
skipped++;
continue;
}
const imageId = `image-${productIndex}-${Date.now()}-${i}`;
const productLabel = data[productIndex].name || `Product #${productIndex + 1}`;
@@ -305,17 +322,20 @@ export const useProductImageOperations = ({
// Update the product data with the new image URL
addImageToProduct(productIndex, result.imageUrl);
uploaded++;
toast.success(`Image uploaded for ${productLabel}`);
} catch (error) {
console.error('Upload error:', error);
// Remove the failed image from our state
setProductImages(prev => prev.filter(img => img.id !== imageId));
toast.error(`Failed to upload image: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
return { uploaded, skipped };
};
// Function to remove an image - update to work with product_images