Make upload by URL input always visible, fix deleting URL images

This commit is contained in:
2025-02-27 13:22:04 -05:00
parent c1159f518c
commit e0a7787139

View File

@@ -42,6 +42,7 @@ import {
PopoverContent, PopoverContent,
PopoverTrigger, PopoverTrigger,
} from "@/components/ui/popover"; } from "@/components/ui/popover";
import { createPortal } from "react-dom";
type Props<T extends string = string> = { type Props<T extends string = string> = {
data: any[]; data: any[];
@@ -767,24 +768,31 @@ export const ImageUploadStep = <T extends string>({
if (!image) return; if (!image) return;
try { try {
// Extract the filename from the URL // Check if this is an external URL-based image or an uploaded image
const urlParts = image.imageUrl.split('/'); const isExternalUrl = image.imageUrl.startsWith('http') &&
const filename = urlParts[urlParts.length - 1]; !image.imageUrl.includes(config.apiUrl.replace(/^https?:\/\//, ''));
// Call API to delete the image // Only call the API to delete the file if it's an uploaded image
const response = await fetch(`${config.apiUrl}/import/delete-image`, { if (!isExternalUrl) {
method: 'DELETE', // Extract the filename from the URL
headers: { const urlParts = image.imageUrl.split('/');
'Content-Type': 'application/json', const filename = urlParts[urlParts.length - 1];
},
body: JSON.stringify({ // Call API to delete the image
imageUrl: image.imageUrl, const response = await fetch(`${config.apiUrl}/import/delete-image`, {
filename method: 'DELETE',
}), headers: {
}); 'Content-Type': 'application/json',
},
if (!response.ok) { body: JSON.stringify({
throw new Error('Failed to delete image'); imageUrl: image.imageUrl,
filename
}),
});
if (!response.ok) {
throw new Error('Failed to delete image');
}
} }
// Remove the image from our state // Remove the image from our state
@@ -1232,84 +1240,37 @@ export const ImageUploadStep = <T extends string>({
); );
}; };
// Add a URL input component // Add a URL input component that doesn't expand/collapse
const ImageUrlInput = ({ productIndex }: { productIndex: number }) => { const ImageUrlInput = ({ productIndex }: { productIndex: number }) => {
const [isOpen, setIsOpen] = useState(false); // Use a stable format that won't get affected by DndContext events
// Add input reference to maintain focus
const inputRef = useRef<HTMLInputElement>(null);
return ( return (
<Popover open={isOpen} onOpenChange={setIsOpen}> <form
<PopoverTrigger asChild> className="flex items-center gap-1"
<Button onSubmit={(e) => {
variant="secondary" e.preventDefault();
size="sm" if (urlInputs[productIndex]) {
className="h-8 flex gap-1 items-center text-xs whitespace-nowrap" handleAddImageFromUrl(productIndex, urlInputs[productIndex]);
> }
<Link2 className="h-4 w-4" /> }}
Add from URL >
</Button> <Input
</PopoverTrigger> placeholder="Image URL"
<PopoverContent value={urlInputs[productIndex] || ''}
className="w-80" onChange={(e) => updateUrlInput(productIndex, e.target.value)}
onInteractOutside={(e) => { className="text-xs h-8 max-w-[180px]"
// Prevent closing when interacting with the input />
if (inputRef.current?.contains(e.target as Node)) { <Button
e.preventDefault(); type="submit"
} size="sm"
}} className="h-8 whitespace-nowrap"
onOpenAutoFocus={() => { disabled={processingUrls[productIndex] || !urlInputs[productIndex]}
// Focus the input field when the popover opens
setTimeout(() => inputRef.current?.focus(), 0);
}}
> >
<div className="space-y-2" onClick={(e) => e.stopPropagation()}> {processingUrls[productIndex] ?
<h4 className="font-medium text-sm">Add image from URL</h4> <Loader2 className="h-3.5 w-3.5 animate-spin mr-1" /> :
<div className="flex gap-2"> <Link2 className="h-3.5 w-3.5 mr-1" />}
<Input Add
ref={inputRef} </Button>
placeholder="Enter image URL" </form>
value={urlInputs[productIndex] || ''}
onChange={(e) => updateUrlInput(productIndex, e.target.value)}
className="text-sm"
// Full prevention of event bubbling
onClick={(e) => e.stopPropagation()}
onMouseDown={(e) => e.stopPropagation()}
onPointerDown={(e) => e.stopPropagation()}
onKeyDown={(e) => {
e.stopPropagation();
if (e.key === 'Enter' && urlInputs[productIndex]) {
e.preventDefault();
handleAddImageFromUrl(productIndex, urlInputs[productIndex] || '');
setIsOpen(false);
}
}}
// Important for paste operation
onPaste={(e) => {
e.stopPropagation();
}}
/>
<Button
size="sm"
disabled={processingUrls[productIndex] || !urlInputs[productIndex]}
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
handleAddImageFromUrl(productIndex, urlInputs[productIndex] || '');
setIsOpen(false);
}}
onMouseDown={(e) => e.stopPropagation()}
onPointerDown={(e) => e.stopPropagation()}
>
{processingUrls[productIndex] ?
<Loader2 className="h-4 w-4 animate-spin" /> :
"Add"}
</Button>
</div>
</div>
</PopoverContent>
</Popover>
); );
}; };
@@ -1504,7 +1465,34 @@ export const ImageUploadStep = <T extends string>({
</div> </div>
</div> </div>
<div className="flex-shrink-0"> <div className="flex-shrink-0">
<ImageUrlInput productIndex={index} /> <form
className="flex items-center gap-2"
onSubmit={(e) => {
e.preventDefault();
if (urlInputs[index]) {
handleAddImageFromUrl(index, urlInputs[index]);
updateUrlInput(index, '');
}
}}
>
<Input
placeholder="Image URL"
value={urlInputs[index] || ''}
onChange={(e) => updateUrlInput(index, e.target.value)}
className="text-xs h-8 w-[180px]"
/>
<Button
type="submit"
size="sm"
className="h-8 whitespace-nowrap flex gap-1 items-center text-xs"
disabled={processingUrls[index] || !urlInputs[index]}
>
{processingUrls[index] ?
<Loader2 className="h-3.5 w-3.5 animate-spin" /> :
<Link2 className="h-3.5 w-3.5" />}
Add Image
</Button>
</form>
</div> </div>
</div> </div>