Skip to content
97 changes: 71 additions & 26 deletions src/frontend/src/components/uploadButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const FileUploadZone: React.FC<FileUploadZoneProps> = ({
onFileReject,
onUploadStateChange,
maxSize = 200 * 1024 * 1024,
acceptedFileTypes = { 'application/sql': ['.sql'] },
acceptedFileTypes = { 'application/sql': ['.sql'] }, // Accept only .sql files by extension
selectedCurrentLanguage,
selectedTargetLanguage
}) => {
Expand All @@ -52,6 +52,7 @@ const FileUploadZone: React.FC<FileUploadZoneProps> = ({
const [batchId, setBatchId] = useState<string>(uuidv4());
const [allUploadsComplete, setAllUploadsComplete] = useState(false);
const [fileLimitExceeded, setFileLimitExceeded] = useState(false);
const [fileRejectionErrors, setFileRejectionErrors] = useState<string[]>([]);
const [showFileLimitDialog, setShowFileLimitDialog] = useState(false);
const navigate = useNavigate();

Expand Down Expand Up @@ -162,33 +163,57 @@ const FileUploadZone: React.FC<FileUploadZoneProps> = ({

const onDrop = useCallback(
(acceptedFiles: File[], fileRejections: FileRejection[]) => {
// Manual extension check: only allow .sql files
const validFiles = acceptedFiles.filter(file => file.name.toLowerCase().endsWith('.sql'));
const invalidFiles = acceptedFiles.filter(file => !file.name.toLowerCase().endsWith('.sql'));
Comment thread
Vemarthula-Microsoft marked this conversation as resolved.
Outdated

// Check current files count and determine how many more can be added
const remainingSlots = MAX_FILES - uploadingFiles.length;


if (validFiles.length > 0) {
setFileRejectionErrors([]); // Clear error notification when valid file is selected
}

if (remainingSlots <= 0) {
// Already at max files, show dialog
setShowFileLimitDialog(true);
return;
}

// If more files are dropped than slots available
if (acceptedFiles.length > remainingSlots) {
// Take only the first `remainingSlots` files
const filesToUpload = acceptedFiles.slice(0, remainingSlots);
if (validFiles.length > remainingSlots) {
const filesToUpload = validFiles.slice(0, remainingSlots);
filesToUpload.forEach(file => simulateFileUpload(file));

if (onFileUpload) onFileUpload(filesToUpload);

// Show dialog about exceeding limit
setShowFileLimitDialog(true);
} else {
// Normal case, upload all files
acceptedFiles.forEach(file => simulateFileUpload(file));
if (onFileUpload) onFileUpload(acceptedFiles);
validFiles.forEach(file => simulateFileUpload(file));
if (onFileUpload) onFileUpload(validFiles);
}

if (onFileReject && fileRejections.length > 0) {
onFileReject(fileRejections);
// Build error messages for invalid extension files
const errors: string[] = [];
invalidFiles.forEach(file => {
errors.push(`File '${file.name}' is not a valid SQL file. Only .sql files are allowed.`);
});

// Existing fileRejections (size/type)
if (fileRejections.length > 0) {
fileRejections.forEach(rejection => {
rejection.errors.forEach(err => {
if (err.code === "file-too-large") {
errors.push(`File '${rejection.file.name}' exceeds the 200MB size limit. Please upload a file smaller than 200MB.`);
} else if (err.code === "file-invalid-type") {
errors.push(`File '${rejection.file.name}' is not a valid SQL file. Only .sql files are allowed.`);
} else {
errors.push(`File '${rejection.file.name}': ${err.message}`);
}
});
});
Comment thread
Vemarthula-Microsoft marked this conversation as resolved.
Outdated
if (onFileReject) onFileReject(fileRejections);
}
if (errors.length > 0) {
setFileRejectionErrors(errors);
}
},
[onFileUpload, onFileReject, uploadingFiles.length]
Expand All @@ -198,7 +223,7 @@ const FileUploadZone: React.FC<FileUploadZoneProps> = ({
onDrop,
noClick: true,
maxSize,
accept: acceptedFileTypes,
accept: acceptedFileTypes, // Only .sql files regardless of mime type
//maxFiles: MAX_FILES,
};

Expand Down Expand Up @@ -230,18 +255,19 @@ const FileUploadZone: React.FC<FileUploadZoneProps> = ({
};

const cancelAllUploads = useCallback(() => {
// Clear all upload intervals
dispatch(deleteBatch({ batchId, headers: null }));

Object.values(uploadIntervals).forEach(interval => clearInterval(interval));
setUploadIntervals({});
setUploadingFiles([]);
setUploadState('IDLE');
onUploadStateChange?.('IDLE');
setShowCancelDialog(false);
setShowLogoCancelDialog(false);
//setBatchId();
startNewBatch();
// Clear all upload intervals
dispatch(deleteBatch({ batchId, headers: null }));

Object.values(uploadIntervals).forEach(interval => clearInterval(interval));
setUploadIntervals({});
setUploadingFiles([]);
setUploadState('IDLE');
onUploadStateChange?.('IDLE');
setShowCancelDialog(false);
setShowLogoCancelDialog(false);
setFileRejectionErrors([]); // Clear error notification when cancel is clicked
//setBatchId();
startNewBatch();
}, [uploadIntervals, onUploadStateChange]);

useEffect(() => {
Expand Down Expand Up @@ -521,6 +547,25 @@ const FileUploadZone: React.FC<FileUploadZoneProps> = ({
</div>

<div style={{ display: 'flex', flexDirection: 'column', gap: '13px', width: '837px', paddingBottom: 10, borderRadius: '4px', }}>
{/* Show file rejection errors for invalid type or size */}
{fileRejectionErrors.length > 0 && (
Comment thread
Vemarthula-Microsoft marked this conversation as resolved.
<MessageBar
messageBarType={MessageBarType.error}
isMultiline={true}
styles={{
root: { display: "flex", flexDirection: "column", alignItems: "left", background: "#fff4f4" },
icon: { display: "none" },
}}
>
<div style={{ display: "flex", alignItems: "center" }}>
<X strokeWidth="2.5px" color="#d83b01" size="16px" style={{ marginRight: "8px" }} />
<span>{fileRejectionErrors[0]}</span>
</div>
{fileRejectionErrors.slice(1).map((err, idx) => (
<div key={idx} style={{ marginLeft: "24px", marginTop: "2px" }}>{err}</div>
))}
</MessageBar>
)}
{/* Show network error message bar if any file has error */}
{uploadingFiles.some(f => f.status === 'error') && (
<MessageBar
Expand Down