Make upload by URL input always visible, fix deleting URL images
This commit is contained in:
@@ -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
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to delete image');
|
||||
// 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];
|
||||
|
||||
// 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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user