aaredb/frontend/src/components/UploadDialog.tsx
GotthardG 54975b5919 Add spreadsheet enhancements and default handling
Implemented a toggleable spreadsheet UI component for sample data, added fields such as priority and comments, and improved backend validation. Default values for "directory" are now assigned when missing, with feedback highlighted in green on the front end.
2025-01-06 14:40:02 +01:00

186 lines
8.3 KiB
TypeScript

import React, { useState, useEffect, useRef } from 'react';
import {
Dialog, DialogTitle, DialogContent, DialogActions, Button, Typography, IconButton, Box, CircularProgress
} from '@mui/material';
import CloseIcon from '@mui/icons-material/Close';
import DownloadIcon from '@mui/icons-material/Download';
import UploadFileIcon from '@mui/icons-material/UploadFile';
import logo from '../assets/Heidi-logo.png';
import { OpenAPI, SpreadsheetService } from '../../openapi';
import type { Body_upload_file_upload_post } from '../../openapi/models/Body_upload_file_upload_post';
import SpreadsheetTable from './SpreadsheetTable';
import Modal from './Modal';
interface UploadDialogProps {
open: boolean;
onClose: () => void;
selectedShipment: any;
}
const UploadDialog: React.FC<UploadDialogProps> = ({ open, onClose, selectedShipment }) => {
const [uploadError, setUploadError] = useState<string | null>(null);
const [fileSummary, setFileSummary] = useState<any>(null);
const [fileBlob, setFileBlob] = useState<Blob | null>(null);
const [isModalOpen, setIsModalOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
// Detect the current environment
const mode = import.meta.env.MODE;
// Dynamically set `OpenAPI.BASE` based on the mode
OpenAPI.BASE =
mode === 'test'
? import.meta.env.VITE_OPENAPI_BASE_TEST
: mode === 'prod'
? import.meta.env.VITE_OPENAPI_BASE_PROD
: import.meta.env.VITE_OPENAPI_BASE_DEV;
// Log warning if `OpenAPI.BASE` is unresolved
if (!OpenAPI.BASE) {
console.error('OpenAPI.BASE is not set. Falling back to a default value.');
OpenAPI.BASE = 'https://default-url.com'; // Use a consistent fallback
}
// Debug for mode and resolved `BASE`
console.log('Environment Mode:', mode);
console.log('Resolved OpenAPI.BASE:', OpenAPI.BASE);
}, []);
const downloadUrl = `${OpenAPI.BASE}/download-template`;
const handleFileUpload = async (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (!file) return;
setUploadError(null);
setFileSummary(null);
setFileBlob(file);
setIsLoading(true);
if (!file.name.endsWith('.xlsx')) {
setUploadError('Invalid file format. Please upload an .xlsx file.');
setIsLoading(false);
return;
}
const formData: Body_upload_file_upload_post = { file: file } as Body_upload_file_upload_post;
try {
const response = await SpreadsheetService.uploadFileUploadPost(formData);
const { headers, raw_data, errors, dewars_count, pucks_count, samples_count } = response;
setFileSummary({
data: raw_data,
errors: errors,
raw_data: raw_data,
headers: headers,
dewars_count: dewars_count,
pucks_count: pucks_count,
samples_count: samples_count,
});
setIsLoading(false);
setIsModalOpen(true);
} catch (error) {
setUploadError('Failed to upload file. Please try again.');
setIsLoading(false);
}
};
const handleCancel = () => {
setIsModalOpen(false);
setFileSummary(null);
if (fileInputRef.current) {
fileInputRef.current.value = "";
}
};
// Count rows with directory defaulted
const defaultDirectoryCount = fileSummary?.raw_data
? fileSummary.raw_data.filter((row) => row.default_set).length
: 0;
return (
<>
<Dialog open={open} onClose={onClose} fullWidth maxWidth="sm">
<DialogTitle>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Typography variant="h6">Upload Sample Data Sheet</Typography>
<IconButton onClick={onClose}>
<CloseIcon />
</IconButton>
</Box>
</DialogTitle>
<DialogContent dividers>
<Box display="flex" flexDirection="column" alignItems="center" mb={2}>
<img src={logo} alt="Logo" style={{ width: 200, marginBottom: 16 }} />
<Typography variant="subtitle1">Latest Spreadsheet Template Version 7</Typography>
<Typography variant="body2" color="textSecondary">Last update: November 7, 2024</Typography>
<Button variant="outlined" startIcon={<DownloadIcon />} href={downloadUrl} download sx={{ mt: 1 }}>
Download XLSX
</Button>
<Typography variant="subtitle1" sx={{ mt: 3 }}>Latest Spreadsheet Instructions Version 2.3</Typography>
<Typography variant="body2" color="textSecondary">Last updated: October 18, 2024</Typography>
<Button variant="outlined" startIcon={<DownloadIcon />} href="/path/to/instructions.pdf" download sx={{ mt: 1 }}>
Download PDF
</Button>
</Box>
<Box mt={3}>
<Button variant="contained" component="label" startIcon={<UploadFileIcon />}>
Choose a File
<input type="file" hidden ref={fileInputRef} onChange={handleFileUpload} />
</Button>
{uploadError && <Typography color="error">{uploadError}</Typography>}
</Box>
{isLoading && (
<Box display="flex" justifyContent="center" alignItems="center" mt={2}>
<CircularProgress />
<Typography variant="body2" ml={2}>Processing the file, please wait...</Typography>
</Box>
)}
</DialogContent>
<DialogActions>
<Button onClick={onClose} color="primary">Close</Button>
</DialogActions>
</Dialog>
{fileSummary && fileBlob && (
<Modal open={isModalOpen} onClose={() => setIsModalOpen(false)} title="File Summary">
<Typography color="success.main">File uploaded successfully!</Typography>
{fileSummary.errors.length > 0 ? (
<Typography color="error">
The file contains errors that need to be corrected before submission. Please review the highlighted cells below.
</Typography>
) : (
<Typography color="success.main">
The file is validated successfully with no errors.
</Typography>
)}
<Typography color="success.main" sx={{ mt: 2 }}>
{defaultDirectoryCount > 0
? `${defaultDirectoryCount} rows had the "directory" field auto-assigned to the default value "{sgPuck}/{sgPosition}". These rows are highlighted in green.`
: "No rows had default values assigned to the 'directory' field."}
</Typography>
<Typography>Dewars: {fileSummary.dewars_count}</Typography>
<Typography>Pucks: {fileSummary.pucks_count}</Typography>
<Typography>Samples: {fileSummary.samples_count}</Typography>
<SpreadsheetTable
raw_data={fileSummary.raw_data}
errors={fileSummary.errors}
headers={fileSummary.headers}
setRawData={(newRawData) => setFileSummary((prevSummary) => ({ ...prevSummary, raw_data: newRawData }))}
onCancel={handleCancel}
fileBlob={fileBlob}
selectedShipment={selectedShipment}
/>
</Modal>
)}
</>
);
};
export default UploadDialog;