github-actions[bot]
Sync from GitHub: 922d61677657398e61342f3cabff773c13c26de4
060dc2a
import React, { useCallback, useState } from 'react';
import { Upload, FileText, Image as ImageIcon, X } from 'lucide-react';
const FileUpload = ({ onFilesSelected, disabled }) => {
const [dragActive, setDragActive] = useState(false);
const [selectedFiles, setSelectedFiles] = 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.length > 0) {
handleFiles(Array.from(e.dataTransfer.files));
}
}, []);
const handleFiles = (files) => {
const validFiles = files.filter(file => {
const validTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/gif', 'image/webp', 'application/pdf'];
return validTypes.includes(file.type);
});
if (validFiles.length !== files.length) {
alert('Some files were skipped. Only images (JPEG, PNG, GIF, WEBP) and PDF files are supported.');
}
setSelectedFiles(prev => [...prev, ...validFiles]);
};
const handleFileInput = (e) => {
if (e.target.files && e.target.files.length > 0) {
handleFiles(Array.from(e.target.files));
}
};
const removeFile = (index) => {
setSelectedFiles(prev => prev.filter((_, i) => i !== index));
};
const handleProcess = () => {
if (selectedFiles.length > 0) {
onFilesSelected(selectedFiles);
setSelectedFiles([]);
}
};
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 Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
};
return (
<div className="w-full space-y-4">
{/* Upload Zone */}
<div
className={`upload-zone relative border-2 border-dashed rounded-xl p-8 text-center cursor-pointer transition-all ${
dragActive ? 'drag-active' : ''
} ${disabled ? 'opacity-50 cursor-not-allowed' : ''}`}
onDragEnter={handleDrag}
onDragLeave={handleDrag}
onDragOver={handleDrag}
onDrop={handleDrop}
onClick={() => !disabled && document.getElementById('fileInput').click()}
>
<input
id="fileInput"
type="file"
multiple
accept="image/*,.pdf"
onChange={handleFileInput}
className="hidden"
disabled={disabled}
/>
<div className="flex flex-col items-center justify-center space-y-4">
<div className="p-4 bg-primary-50 rounded-full">
<Upload className="w-12 h-12 text-primary-500" />
</div>
<div>
<p className="text-lg font-semibold text-gray-700 mb-1">
Drop your files here or click to browse
</p>
<p className="text-sm text-gray-500">
Support for images (JPEG, PNG, GIF, WEBP) and PDF files
</p>
</div>
<div className="flex items-center gap-2 text-xs text-gray-400">
<ImageIcon className="w-4 h-4" />
<span>Multiple files supported</span>
<span></span>
<FileText className="w-4 h-4" />
<span>PDF to image conversion</span>
</div>
</div>
</div>
{/* Selected Files List */}
{selectedFiles.length > 0 && (
<div className="space-y-3">
<div className="flex items-center justify-between">
<h3 className="text-sm font-semibold text-gray-700">
Selected Files ({selectedFiles.length})
</h3>
<button
onClick={() => setSelectedFiles([])}
className="text-xs text-red-500 hover:text-red-700 font-medium"
>
Clear All
</button>
</div>
<div className="space-y-2 max-h-64 overflow-y-auto">
{selectedFiles.map((file, index) => (
<div
key={index}
className="flex items-center justify-between bg-white p-3 rounded-lg border border-gray-200 hover:border-primary-300 transition-colors"
>
<div className="flex items-center space-x-3 flex-1 min-w-0">
<div className="flex-shrink-0">
{file.type === 'application/pdf' ? (
<FileText className="w-8 h-8 text-red-500" />
) : (
<ImageIcon className="w-8 h-8 text-blue-500" />
)}
</div>
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-gray-900 truncate">
{file.name}
</p>
<p className="text-xs text-gray-500">
{formatFileSize(file.size)}
{file.type === 'application/pdf' && ' • Will be converted to images'}
</p>
</div>
</div>
<button
onClick={() => removeFile(index)}
className="ml-2 p-1 hover:bg-red-50 rounded-full transition-colors"
title="Remove file"
>
<X className="w-5 h-5 text-red-500" />
</button>
</div>
))}
</div>
<button
onClick={handleProcess}
disabled={disabled}
className="w-full bg-gradient-to-r from-primary-500 to-primary-600 text-white font-semibold py-3 px-6 rounded-lg hover:from-primary-600 hover:to-primary-700 transition-all shadow-lg hover:shadow-xl disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center space-x-2"
>
<Upload className="w-5 h-5" />
<span>Process {selectedFiles.length} File{selectedFiles.length > 1 ? 's' : ''}</span>
</button>
</div>
)}
</div>
);
};
export default FileUpload;