'use client'; import { useState, useRef, useEffect } from 'react'; interface ImageUploadProps { currentImageUrl?: string; onImageChange: (url: string) => void; type: 'wishlist' | 'item'; label?: string; onUploadStateChange?: (isUploading: boolean) => void; } export default function ImageUpload({ currentImageUrl, onImageChange, type, label = 'Image', onUploadStateChange, }: ImageUploadProps) { const [isUploading, setIsUploading] = useState(false); const [uploadProgress, setUploadProgress] = useState(0); const [uploadError, setUploadError] = useState(''); const [imageUrl, setImageUrl] = useState(currentImageUrl || ''); const [useUrl, setUseUrl] = useState(!!currentImageUrl); const fileInputRef = useRef(null); const pasteAreaRef = useRef(null); const uploadFile = (file: File) => { // Client-side validation const maxSize = 5 * 1024 * 1024; // 5MB const allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif']; if (!allowedTypes.includes(file.type)) { setUploadError('Invalid file type. Please upload a JPEG, PNG, WebP, or GIF image.'); return; } if (file.size > maxSize) { setUploadError('File is too large. Maximum size is 5MB.'); return; } setIsUploading(true); setUploadProgress(0); setUploadError(''); onUploadStateChange?.(true); const formData = new FormData(); formData.append('file', file); formData.append('type', type); const xhr = new XMLHttpRequest(); // Track upload progress xhr.upload.addEventListener('progress', (e) => { if (e.lengthComputable) { const percentComplete = (e.loaded / e.total) * 100; setUploadProgress(percentComplete); } }); // Handle completion xhr.addEventListener('load', () => { if (xhr.status >= 200 && xhr.status < 300) { try { const data = JSON.parse(xhr.responseText); setImageUrl(data.url); onImageChange(data.url); setUploadProgress(100); } catch (error) { console.error('Failed to parse response:', error); setUploadError('Failed to parse server response'); } } else { try { const data = JSON.parse(xhr.responseText); setUploadError(data.error || 'Upload failed'); } catch { setUploadError(`Upload failed with status ${xhr.status}`); } } setIsUploading(false); onUploadStateChange?.(false); }); // Handle errors xhr.addEventListener('error', () => { console.error('Upload error'); setUploadError('Network error occurred during upload'); setIsUploading(false); onUploadStateChange?.(false); }); // Handle abort xhr.addEventListener('abort', () => { setUploadError('Upload was cancelled'); setIsUploading(false); onUploadStateChange?.(false); }); xhr.open('POST', '/uploads'); xhr.send(formData); }; const handleFileUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; uploadFile(file); }; const handlePaste = (e: ClipboardEvent) => { // Only handle paste if we're in upload mode (not URL mode) if (useUrl || imageUrl) return; const items = e.clipboardData?.items; if (!items) return; for (let i = 0; i < items.length; i++) { const item = items[i]; // Check if the item is an image if (item.type.indexOf('image') !== -1) { e.preventDefault(); const file = item.getAsFile(); if (file) { uploadFile(file); } break; } } }; // Add paste event listener useEffect(() => { const handlePasteEvent = (e: Event) => handlePaste(e as ClipboardEvent); // Listen for paste events on the component's container const pasteArea = pasteAreaRef.current; if (pasteArea) { pasteArea.addEventListener('paste', handlePasteEvent); } // Also listen globally when in upload mode and no image is set if (!useUrl && !imageUrl) { document.addEventListener('paste', handlePasteEvent); } return () => { if (pasteArea) { pasteArea.removeEventListener('paste', handlePasteEvent); } document.removeEventListener('paste', handlePasteEvent); }; }, [useUrl, imageUrl]); const handleUrlChange = (url: string) => { setImageUrl(url); onImageChange(url); }; const handleRemoveImage = () => { setImageUrl(''); onImageChange(''); if (fileInputRef.current) { fileInputRef.current.value = ''; } }; return (
{label && ( )} {imageUrl ? ( /* Image Preview with Remove Button */
Preview { e.currentTarget.style.display = 'none'; }} />
) : ( <> {/* Toggle between URL and File Upload */}
{useUrl ? ( /* URL Input */
handleUrlChange(e.target.value)} />
) : ( /* File Upload */

💡 Tip: You can also paste an image directly (Ctrl+V / Cmd+V)

)} {/* Error Message */} {uploadError && (
{uploadError}
)} {/* Upload Progress Bar */} {isUploading && (
Uploading... {Math.round(uploadProgress)}%
)} )}
); }