ACE / components /FileUpload.tsx
Severian's picture
Upload 27 files
159f9c9 verified
import React, { useCallback, useState } from 'react';
import { CsvInputRow, REQUIRED_CSV_HEADERS } from '../types';
import { UploadIcon } from './Icons';
// This tells TypeScript that `Papa` is available on the global window object.
declare const Papa: any;
interface FileUploadProps {
onFileParsed: (data: CsvInputRow[], file: File) => void;
onParseError: (message: string) => void;
disabled: boolean;
}
const FileUpload: React.FC<FileUploadProps> = ({ onFileParsed, onParseError, disabled }) => {
const [isDragging, setIsDragging] = useState(false);
const handleFile = useCallback((file: File) => {
if (!file) {
onParseError('No file selected.');
return;
}
if (file.type !== 'text/csv') {
onParseError('Invalid file type. Please upload a CSV file.');
return;
}
Papa.parse(file, {
header: true,
skipEmptyLines: true,
complete: (results: any) => {
if (results.errors.length > 0) {
console.error('CSV Parsing Errors:', results.errors);
onParseError(`Error parsing CSV: ${results.errors[0].message}`);
return;
}
const headers = results.meta.fields;
const missingHeaders = REQUIRED_CSV_HEADERS.filter(
(requiredHeader) => !headers.includes(requiredHeader)
);
if (missingHeaders.length > 0) {
onParseError(`Missing required CSV columns: ${missingHeaders.join(', ')}`);
return;
}
onFileParsed(results.data as CsvInputRow[], file);
},
error: (error: any) => {
console.error('PapaParse Error:', error);
onParseError('An unexpected error occurred while parsing the file.');
},
});
}, [onFileParsed, onParseError]);
const handleDragEnter = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
if (!disabled) setIsDragging(true);
};
const handleDragLeave = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
};
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
};
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
if (disabled) return;
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
handleFile(e.dataTransfer.files[0]);
e.dataTransfer.clearData();
}
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files.length > 0) {
handleFile(e.target.files[0]);
}
};
const borderColor = isDragging ? 'border-blue-500' : 'border-gray-600';
const bgColor = isDragging ? 'bg-gray-700' : 'bg-gray-800';
return (
<div
className={`relative p-8 border-2 ${borderColor} border-dashed rounded-xl text-center transition-all duration-300 ${bgColor} ${disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'}`}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
onDragOver={handleDragOver}
onDrop={handleDrop}
>
<input
type="file"
id="file-upload"
className="hidden"
accept=".csv"
onChange={handleInputChange}
disabled={disabled}
/>
<label htmlFor="file-upload" className={`${disabled ? 'cursor-not-allowed' : 'cursor-pointer'}`}>
<div className="flex flex-col items-center">
<UploadIcon className="w-12 h-12 text-gray-400 mb-4" />
<p className="text-lg font-semibold text-white">
Drag & drop your CSV file here
</p>
<p className="text-gray-400">or click to browse</p>
</div>
</label>
</div>
);
};
export default FileUpload;