Spaces:
Runtime error
Runtime error
| import { useState, useRef } from 'react'; | |
| import { uploadDocument } from '../services/api'; | |
| export default function FileUpload({ onUploadSuccess, documentCount }) { | |
| const [isDragging, setIsDragging] = useState(false); | |
| const [isUploading, setIsUploading] = useState(false); | |
| const [progress, setProgress] = useState(''); | |
| const [error, setError] = useState(''); | |
| const fileInputRef = useRef(null); | |
| const isLimitReached = typeof documentCount === 'number' && documentCount >= 3; | |
| const handleDragOver = (e) => { | |
| e.preventDefault(); | |
| if (!isLimitReached) setIsDragging(true); | |
| }; | |
| const handleDragLeave = (e) => { | |
| e.preventDefault(); | |
| setIsDragging(false); | |
| }; | |
| const handleDrop = (e) => { | |
| e.preventDefault(); | |
| setIsDragging(false); | |
| if (!isLimitReached) { | |
| const files = e.dataTransfer.files; | |
| if (files.length > 0) handleFile(files[0]); | |
| } | |
| }; | |
| const handleFileSelect = (e) => { | |
| if (!isLimitReached && e.target.files.length > 0) handleFile(e.target.files[0]); | |
| }; | |
| const handleFile = async (file) => { | |
| if (isLimitReached) { | |
| setError('Maximum limit of 3 documents reached.'); | |
| return; | |
| } | |
| if (!file.name.toLowerCase().endsWith('.pdf')) { | |
| setError('Only PDF files are supported'); | |
| return; | |
| } | |
| if (file.size > 50 * 1024 * 1024) { | |
| setError('File size must be under 50MB'); | |
| return; | |
| } | |
| setError(''); | |
| setIsUploading(true); | |
| setProgress('Uploading and processing...'); | |
| try { | |
| const result = await uploadDocument(file); | |
| setProgress(`✅ ${result.message}`); | |
| onUploadSuccess?.(result.document); | |
| setTimeout(() => setProgress(''), 3000); | |
| } catch (err) { | |
| setError(err.message); | |
| } finally { | |
| setIsUploading(false); | |
| if (fileInputRef.current) fileInputRef.current.value = ''; | |
| } | |
| }; | |
| return ( | |
| <div className="file-upload-container"> | |
| <div | |
| className={`drop-zone ${isDragging && !isLimitReached ? 'dragging' : ''} ${isUploading ? 'uploading' : ''} ${isLimitReached ? 'disabled' : ''}`} | |
| onDragOver={handleDragOver} | |
| onDragLeave={handleDragLeave} | |
| onDrop={handleDrop} | |
| onClick={() => !isUploading && !isLimitReached && fileInputRef.current?.click()} | |
| style={isLimitReached ? { opacity: 0.5, cursor: 'not-allowed', borderColor: 'var(--border)' } : {}} | |
| > | |
| <input | |
| ref={fileInputRef} | |
| type="file" | |
| accept=".pdf" | |
| onChange={handleFileSelect} | |
| style={{ display: 'none' }} | |
| disabled={isLimitReached} | |
| /> | |
| <div className="drop-zone-content"> | |
| {isUploading ? ( | |
| <> | |
| <div className="upload-spinner"></div> | |
| <p className="upload-status">{progress}</p> | |
| </> | |
| ) : ( | |
| <> | |
| <div className="upload-icon"> | |
| <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5"> | |
| <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> | |
| <polyline points="17 8 12 3 7 8" /> | |
| <line x1="12" y1="3" x2="12" y2="15" /> | |
| </svg> | |
| </div> | |
| <p className="drop-zone-text"> | |
| {isLimitReached ? ( | |
| <span style={{color: 'salmon'}}>Limit reached (3 max). Delete a file to upload.</span> | |
| ) : ( | |
| <>Drop your PDF here or <span className="browse-link">browse</span></> | |
| )} | |
| </p> | |
| <p className="drop-zone-hint">Supports PDF up to 50MB</p> | |
| </> | |
| )} | |
| </div> | |
| </div> | |
| {error && ( | |
| <div className="upload-error"> | |
| <span>⚠️</span> {error} | |
| </div> | |
| )} | |
| {progress && !isUploading && ( | |
| <div className="upload-success">{progress}</div> | |
| )} | |
| </div> | |
| ); | |
| } | |