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