Spaces:
Sleeping
Sleeping
| import React, { useState, useCallback } from 'react'; | |
| import { Upload, File, X, AlertCircle } from 'lucide-react'; | |
| import { Button } from '@/components/ui/button'; | |
| import { Card, CardContent } from '@/components/ui/card'; | |
| const FileUpload = ({ onFileSelect, acceptedTypes = '.pdf,.png,.jpg,.jpeg,.tiff,.tif,.txt' }) => { | |
| const [dragActive, setDragActive] = useState(false); | |
| const [selectedFile, setSelectedFile] = useState(null); | |
| const [error, setError] = useState(''); | |
| const handleDrag = useCallback((e) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| if (e.type === 'dragenter' || e.type === 'dragover') { | |
| setDragActive(true); | |
| } else if (e.type === 'dragleave') { | |
| setDragActive(false); | |
| } | |
| }, []); | |
| const handleDrop = useCallback((e) => { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| setDragActive(false); | |
| if (e.dataTransfer.files && e.dataTransfer.files[0]) { | |
| handleFileSelection(e.dataTransfer.files[0]); | |
| } | |
| }, []); | |
| const handleFileSelection = (file) => { | |
| setError(''); | |
| // Check file size (50MB limit) | |
| if (file.size > 50 * 1024 * 1024) { | |
| setError('File size must be less than 50MB'); | |
| return; | |
| } | |
| // Check file type | |
| const allowedTypes = acceptedTypes.split(',').map(type => type.trim().toLowerCase()); | |
| const fileExtension = '.' + file.name.split('.').pop().toLowerCase(); | |
| if (!allowedTypes.includes(fileExtension)) { | |
| setError(`File type not supported. Allowed types: ${acceptedTypes}`); | |
| return; | |
| } | |
| setSelectedFile(file); | |
| onFileSelect(file); | |
| }; | |
| const handleFileInput = (e) => { | |
| if (e.target.files && e.target.files[0]) { | |
| handleFileSelection(e.target.files[0]); | |
| } | |
| }; | |
| const removeFile = () => { | |
| setSelectedFile(null); | |
| setError(''); | |
| onFileSelect(null); | |
| }; | |
| const formatFileSize = (bytes) => { | |
| if (bytes === 0) return '0 Bytes'; | |
| const k = 1024; | |
| const sizes = ['Bytes', 'KB', 'MB', 'GB']; | |
| const i = Math.floor(Math.log(bytes) / Math.log(k)); | |
| return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]; | |
| }; | |
| return ( | |
| <Card className="w-full"> | |
| <CardContent className="p-6"> | |
| <div className="space-y-4"> | |
| {!selectedFile ? ( | |
| <div | |
| className={`relative border-2 border-dashed rounded-lg p-8 text-center transition-colors ${ | |
| dragActive | |
| ? 'border-blue-500 bg-blue-50' | |
| : 'border-gray-300 hover:border-gray-400' | |
| }`} | |
| onDragEnter={handleDrag} | |
| onDragLeave={handleDrag} | |
| onDragOver={handleDrag} | |
| onDrop={handleDrop} | |
| > | |
| <input | |
| type="file" | |
| accept={acceptedTypes} | |
| onChange={handleFileInput} | |
| className="absolute inset-0 w-full h-full opacity-0 cursor-pointer" | |
| /> | |
| <div className="space-y-4"> | |
| <Upload className="mx-auto h-12 w-12 text-gray-400" /> | |
| <div> | |
| <p className="text-lg font-medium text-gray-900"> | |
| Drop your file here, or{' '} | |
| <span className="text-blue-600 hover:text-blue-500">browse</span> | |
| </p> | |
| <p className="text-sm text-gray-500 mt-2"> | |
| Supports: PDF, PNG, JPG, TIFF, TXT (max 50MB) | |
| </p> | |
| </div> | |
| </div> | |
| </div> | |
| ) : ( | |
| <div className="flex items-center justify-between p-4 bg-gray-50 rounded-lg"> | |
| <div className="flex items-center space-x-3"> | |
| <File className="h-8 w-8 text-blue-600" /> | |
| <div> | |
| <p className="font-medium text-gray-900">{selectedFile.name}</p> | |
| <p className="text-sm text-gray-500">{formatFileSize(selectedFile.size)}</p> | |
| </div> | |
| </div> | |
| <Button | |
| variant="ghost" | |
| size="sm" | |
| onClick={removeFile} | |
| className="text-gray-500 hover:text-red-600" | |
| > | |
| <X className="h-4 w-4" /> | |
| </Button> | |
| </div> | |
| )} | |
| {error && ( | |
| <div className="flex items-center space-x-2 text-red-600 bg-red-50 p-3 rounded-lg"> | |
| <AlertCircle className="h-4 w-4" /> | |
| <span className="text-sm">{error}</span> | |
| </div> | |
| )} | |
| </div> | |
| </CardContent> | |
| </Card> | |
| ); | |
| }; | |
| export default FileUpload; | |