703 lines
22 KiB
JavaScript
703 lines
22 KiB
JavaScript
import * as React from "react";
|
|
import { styled } from "@mui/system";
|
|
import {
|
|
TablePagination,
|
|
tablePaginationClasses as classes,
|
|
} from "@mui/base/TablePagination";
|
|
import { Link } from "react-router-dom";
|
|
import TextField from "@mui/material/TextField";
|
|
import { Box } from "@mui/material";
|
|
import InputLabel from "@mui/material/InputLabel";
|
|
import MenuItem from "@mui/material/MenuItem";
|
|
import FormControl from "@mui/material/FormControl";
|
|
import Select from "@mui/material/Select";
|
|
import Backdrop from '@mui/material/Backdrop';
|
|
import CircularProgress from '@mui/material/CircularProgress';
|
|
import ImageDialog from "./ImageDialog";
|
|
import { Button } from "bootstrap";
|
|
|
|
export default function TableComponentAdditionalSheet({
|
|
filterSelectedExamCentreCode,
|
|
setFilterSelectedExamCentreCode,
|
|
rows,
|
|
type,
|
|
distinctExamCentreCodes,
|
|
batchType,
|
|
}) {
|
|
// console.log("Exam centre code in table component : ",distinctExamCentreCodes)
|
|
console.log("Rows in table component : ", rows);
|
|
// console.log("Type is : ", type);
|
|
const [page, setPage] = React.useState(0);
|
|
const [rowsPerPage, setRowsPerPage] = React.useState(5);
|
|
|
|
const [isDialogOpen, setIsDialogOpen] = React.useState(false);
|
|
const [currentImagePath, setCurrentImagePath] = React.useState('');
|
|
|
|
const [openLoader, setOpenLoader] = React.useState(false);
|
|
const handleCloseLoader = () => {
|
|
setOpenLoader(false);
|
|
};
|
|
const handleOpenLoader = () => {
|
|
setOpenLoader(true);
|
|
};
|
|
|
|
// Avoid a layout jump when reaching the last page with empty rows.
|
|
const emptyRows =
|
|
page > 0 ? Math.max(0, (1 + page) * rowsPerPage - rows.length) : 0;
|
|
|
|
const handleChangePage = (event, newPage) => {
|
|
setPage(newPage);
|
|
};
|
|
|
|
const handleChangeRowsPerPage = (event) => {
|
|
setRowsPerPage(parseInt(event.target.value, 10));
|
|
setPage(0);
|
|
};
|
|
|
|
const handleFilterExamCentreCode = (e) => {
|
|
const ExamCentreCode = e.target.value;
|
|
setFilterSelectedExamCentreCode(ExamCentreCode);
|
|
};
|
|
|
|
const handleImageClick = (imagePath) => {
|
|
console.log("imagepath:",imagePath)
|
|
setCurrentImagePath(imagePath);
|
|
setIsDialogOpen(true)
|
|
};
|
|
|
|
const handleCloseDialog = () => {
|
|
setIsDialogOpen(false);
|
|
setCurrentImagePath('');
|
|
};
|
|
|
|
const [file, setFile] = React.useState(null);
|
|
const [csvData, setCsvData] = React.useState([]);
|
|
|
|
const handleFileChange = (event) => {
|
|
setFile(event.target.files[0]);
|
|
console.log("event.target.files[0]: ",event.target.files[0])
|
|
};
|
|
|
|
const handleFileUpload = async () => {
|
|
if (!file) {
|
|
alert('Please upload a CSV file first.');
|
|
return;
|
|
}
|
|
handleOpenLoader()
|
|
const formData = new FormData();
|
|
formData.append('file', file);
|
|
|
|
try {
|
|
const response = await fetch(
|
|
`${
|
|
import.meta.env.VITE_REACT_APP_BACKEND_URL
|
|
}/uploadAdditionalAttendanceCsv`,
|
|
{
|
|
method: "POST",
|
|
body: formData
|
|
}
|
|
)
|
|
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
console.log('File uploaded successfully:', data);
|
|
handleCloseLoader()
|
|
|
|
} else {
|
|
console.error('File upload failed:', response.statusText);
|
|
handleCloseLoader()
|
|
}
|
|
} catch (error) {
|
|
console.error('Error uploading file:', error);
|
|
handleCloseLoader()
|
|
}
|
|
};
|
|
|
|
const parseCSV = (text) => {
|
|
const rows = text.split('\n');
|
|
const data = rows.map(row => row.split(','));
|
|
setCsvData(data);
|
|
};
|
|
|
|
return (
|
|
<Root className="overflow-auto" sx={{ maxWidth: "100%", width: 1200 }}>
|
|
{type === "AnomolyReassigned" && (
|
|
<>
|
|
<h5 className="py-2">Manual Verification Needed Students :</h5>
|
|
{/* <Box className="py-2">
|
|
<FormControl fullWidth>
|
|
<InputLabel id="demo-simple-select-label">Exam Centre Code</InputLabel>
|
|
<Select
|
|
labelId="demo-simple-select-label"
|
|
id="demo-simple-select"
|
|
label="Exam Centre Code"
|
|
onChange={handleFilterExamCentreCode}
|
|
>
|
|
{distinctExamCentreCodes.length > 0 && (
|
|
distinctExamCentreCodes.map((exam_centre_code)=>(
|
|
<MenuItem key={exam_centre_code} value={exam_centre_code}>{exam_centre_code}</MenuItem>
|
|
))
|
|
)}
|
|
</Select>
|
|
</FormControl>
|
|
</Box> */}
|
|
<table aria-label="custom pagination table">
|
|
<thead>
|
|
<tr>
|
|
|
|
<th>Attendance Image</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{(rowsPerPage > 0
|
|
? rows.slice(
|
|
page * rowsPerPage,
|
|
page * rowsPerPage + rowsPerPage
|
|
)
|
|
: rows
|
|
).map((row) => (
|
|
<tr key={row.attendence_serial_no}>
|
|
<td>{row.attendence_serial_no}</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{/* <Link
|
|
to={`/anomoly/reassigned/booklet?sno=${row?.answer_booklet_sno}`}
|
|
>
|
|
{row.answer_booklet_sno}
|
|
</Link> */}
|
|
</td>
|
|
{/* <td style={{ width: 160 }} align="right">
|
|
{row.exam_centre_code}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.exam_centre}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.student_name}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.register_number}
|
|
</td> */}
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.s3_path}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
{emptyRows > 0 && (
|
|
<tr style={{ height: 41 * emptyRows }}>
|
|
<td colSpan={3} aria-hidden />
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<CustomTablePagination
|
|
rowsPerPageOptions={[5, 10, 25, { label: "All", value: -1 }]}
|
|
colSpan={3}
|
|
count={rows.length}
|
|
rowsPerPage={rowsPerPage}
|
|
page={page}
|
|
slotProps={{
|
|
select: {
|
|
"aria-label": "rows per page",
|
|
},
|
|
actions: {
|
|
showFirstButton: true,
|
|
showLastButton: true,
|
|
},
|
|
}}
|
|
onPageChange={handleChangePage}
|
|
onRowsPerPageChange={handleChangeRowsPerPage}
|
|
/>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</>
|
|
)}
|
|
{type === "ReassignedStats" && (
|
|
<>
|
|
<h5 className="py-2">Exam Centre Code Complete MetaData :</h5>
|
|
<table aria-label="custom pagination table">
|
|
<thead>
|
|
<tr>
|
|
<th>Exam Centre Code</th>
|
|
<th>Total Scanned Count</th>
|
|
<th>Manual Verification Count</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{(rowsPerPage > 0
|
|
? rows.slice(
|
|
page * rowsPerPage,
|
|
page * rowsPerPage + rowsPerPage
|
|
)
|
|
: rows
|
|
).map((row) => (
|
|
<tr key={row.exam_centre_code}>
|
|
<td style={{ width: 160 }} align="right">
|
|
<a
|
|
href={`/anomoly/reassigned/stats/${row.exam_centre_code}`}
|
|
>
|
|
{row.exam_centre_code}
|
|
</a>
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.total_count}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.manual_verification_count}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
{emptyRows > 0 && (
|
|
<tr style={{ height: 41 * emptyRows }}>
|
|
<td colSpan={3} aria-hidden />
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<CustomTablePagination
|
|
rowsPerPageOptions={[5, 10, 25, { label: "All", value: -1 }]}
|
|
colSpan={3}
|
|
count={rows.length}
|
|
rowsPerPage={rowsPerPage}
|
|
page={page}
|
|
slotProps={{
|
|
select: {
|
|
"aria-label": "rows per page",
|
|
},
|
|
actions: {
|
|
showFirstButton: true,
|
|
showLastButton: true,
|
|
},
|
|
}}
|
|
onPageChange={handleChangePage}
|
|
onRowsPerPageChange={handleChangeRowsPerPage}
|
|
/>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</>
|
|
)}
|
|
{type === "IndividualReassignedStats" && (
|
|
<>
|
|
<h5 className="py-2">Exam Centre Code-Date Wise MetaData :</h5>
|
|
<table aria-label="custom pagination table">
|
|
<thead>
|
|
<tr>
|
|
<th>Exam Centre Code</th>
|
|
<th>Exam Date</th>
|
|
<th>Subject Code</th>
|
|
<th>Total Count</th>
|
|
<th>Manual Verification Count</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{(rowsPerPage > 0
|
|
? rows.slice(
|
|
page * rowsPerPage,
|
|
page * rowsPerPage + rowsPerPage
|
|
)
|
|
: rows
|
|
).map((row) => (
|
|
<tr key={row.exam_centre_code}>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.exam_centre_code}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.exam_date}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.subject_code}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.total_count}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.manual_verification_needed_count}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
{emptyRows > 0 && (
|
|
<tr style={{ height: 41 * emptyRows }}>
|
|
<td colSpan={3} aria-hidden />
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<CustomTablePagination
|
|
rowsPerPageOptions={[5, 10, 25, { label: "All", value: -1 }]}
|
|
colSpan={3}
|
|
count={rows.length}
|
|
rowsPerPage={rowsPerPage}
|
|
page={page}
|
|
slotProps={{
|
|
select: {
|
|
"aria-label": "rows per page",
|
|
},
|
|
actions: {
|
|
showFirstButton: true,
|
|
showLastButton: true,
|
|
},
|
|
}}
|
|
onPageChange={handleChangePage}
|
|
onRowsPerPageChange={handleChangeRowsPerPage}
|
|
/>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</>
|
|
)}
|
|
{type === "PartAReassigned" && (
|
|
<>
|
|
<h5 className="py-2">Part-A Anomoly Data :</h5>
|
|
<table aria-label="custom pagination table">
|
|
<thead>
|
|
<tr>
|
|
<th>s3_path</th>
|
|
<th>Barcode</th>
|
|
<th>Subject Code</th>
|
|
<th>Register Number</th>
|
|
<th>Type</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{(rowsPerPage > 0
|
|
? rows.slice(
|
|
page * rowsPerPage,
|
|
page * rowsPerPage + rowsPerPage
|
|
)
|
|
: rows
|
|
).map((row) => (
|
|
<tr key={row.exam_centre_code}>
|
|
<td style={{ width: 160 }} align="right">
|
|
<Link
|
|
to={`/anomoly/partA/booklet?batchType=${batchType}&barcode=${row.barcode}&s3Path=${row.s3_path}&sysNo=${row.systemNo}`}
|
|
>
|
|
{row?.s3_path}
|
|
</Link>
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.barcode}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.subject_code}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.register_number}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.type}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
{emptyRows > 0 && (
|
|
<tr style={{ height: 41 * emptyRows }}>
|
|
<td colSpan={3} aria-hidden />
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<CustomTablePagination
|
|
rowsPerPageOptions={[5, 10, 25, { label: "All", value: -1 }]}
|
|
colSpan={3}
|
|
count={rows.length}
|
|
rowsPerPage={rowsPerPage}
|
|
page={page}
|
|
slotProps={{
|
|
select: {
|
|
"aria-label": "rows per page",
|
|
},
|
|
actions: {
|
|
showFirstButton: true,
|
|
showLastButton: true,
|
|
},
|
|
}}
|
|
onPageChange={handleChangePage}
|
|
onRowsPerPageChange={handleChangeRowsPerPage}
|
|
/>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</>
|
|
)}
|
|
{type === "AttendenceAdditionalRecord" && (
|
|
<>
|
|
<h5 className="py-2">Part-A Anomoly Data :</h5>
|
|
<table aria-label="custom pagination table">
|
|
<thead>
|
|
<tr>
|
|
<th>qrcode</th>
|
|
<th>Cover A</th>
|
|
<th>Cover B</th>
|
|
<th>Subject Code</th>
|
|
<th>Total Students</th>
|
|
<th>Total Present</th>
|
|
<th>Total Absent</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{(rowsPerPage > 0
|
|
? rows.slice(
|
|
page * rowsPerPage,
|
|
page * rowsPerPage + rowsPerPage
|
|
)
|
|
: rows
|
|
).map((row) => (
|
|
<tr key={row.exam_centre_code}>
|
|
<td style={{ width: 160 }} align="right">
|
|
<Link
|
|
to={`/anomoly/attendence/additionalRecord/correction?qrcode=${row.qrcode}`}
|
|
>
|
|
{row?.qrcode}
|
|
</Link>
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.coverA}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.coverB}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.subject_code}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.total_students}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.total_present}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.total_absent}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
{emptyRows > 0 && (
|
|
<tr style={{ height: 41 * emptyRows }}>
|
|
<td colSpan={3} aria-hidden />
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<CustomTablePagination
|
|
rowsPerPageOptions={[5, 10, 25, { label: "All", value: -1 }]}
|
|
colSpan={3}
|
|
count={rows.length}
|
|
rowsPerPage={rowsPerPage}
|
|
page={page}
|
|
slotProps={{
|
|
select: {
|
|
"aria-label": "rows per page",
|
|
},
|
|
actions: {
|
|
showFirstButton: true,
|
|
showLastButton: true,
|
|
},
|
|
}}
|
|
onPageChange={handleChangePage}
|
|
onRowsPerPageChange={handleChangeRowsPerPage}
|
|
/>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</>
|
|
)}
|
|
{type === "AdditionalSheet" && (
|
|
<>
|
|
<h5 className="py-2">Attendance Additional Sheet</h5>
|
|
<div style={{ width: '200px', height: '50px', backgroundColor: 'lightblue', display: 'flex', justifyContent: 'center', alignItems: 'center', borderRadius: '5px' }}>
|
|
<a
|
|
style={{
|
|
textDecoration: 'none',
|
|
color: 'black',
|
|
padding: '10px 20px',
|
|
backgroundColor: 'white',
|
|
border: '1px solid black',
|
|
borderRadius: '5px',
|
|
textAlign: 'center',
|
|
display: 'inline-block'
|
|
}}
|
|
href="/assets/csv/additional_attedance_sheet_template.csv"
|
|
download="additional_attedance_sheet_template.csv"
|
|
>
|
|
Download Template CSV
|
|
</a>
|
|
|
|
</div>
|
|
<p>Please download the CSV file and by verifying the image type appropriately</p>
|
|
|
|
<table aria-label="custom pagination table">
|
|
<thead>
|
|
<tr>
|
|
<th>Image Name</th>
|
|
<th>upload csv</th>
|
|
<th>Image</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{(rowsPerPage > 0
|
|
? rows.slice(
|
|
page * rowsPerPage,
|
|
page * rowsPerPage + rowsPerPage
|
|
)
|
|
: rows
|
|
).map((row) => (
|
|
<tr key={row.exam_centre_code}>
|
|
|
|
<td style={{ width: 160 }} align="right">
|
|
{row.image_name}
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
<div>
|
|
<input type="file" accept=".csv" onChange={handleFileChange} />
|
|
<button style={{width:'130px', height:'30px', fontSize:'12px', textAlign:'center'}} onClick={handleFileUpload}>Upload CSV</button>
|
|
|
|
</div>
|
|
|
|
</td>
|
|
<td style={{ width: 160 }} align="right">
|
|
<div>
|
|
<span style={{cursor:'pointer', color:'blue'}} onClick={() => handleImageClick(row.s3_image_path)}>
|
|
Preview Image
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<span>
|
|
<a style={{textDecoration: 'none'}} href={`https://docs.exampaper.vidh.ai/${row.s3_image_path}`} download>
|
|
Download Image
|
|
</a>
|
|
</span>
|
|
</div>
|
|
</td>
|
|
{/* <td style={{ width: 160 }} align="right">
|
|
<Link
|
|
to={`www.google.com`}
|
|
>
|
|
{row?.s3_image_path}
|
|
</Link>
|
|
</td> */}
|
|
</tr>
|
|
))}
|
|
{emptyRows > 0 && (
|
|
<tr style={{ height: 41 * emptyRows }}>
|
|
<td colSpan={3} aria-hidden />
|
|
</tr>
|
|
)}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<CustomTablePagination
|
|
rowsPerPageOptions={[5, 10, 25, { label: "All", value: -1 }]}
|
|
colSpan={3}
|
|
count={rows.length}
|
|
rowsPerPage={rowsPerPage}
|
|
page={page}
|
|
slotProps={{
|
|
select: {
|
|
"aria-label": "rows per page",
|
|
},
|
|
actions: {
|
|
showFirstButton: true,
|
|
showLastButton: true,
|
|
},
|
|
}}
|
|
onPageChange={handleChangePage}
|
|
onRowsPerPageChange={handleChangeRowsPerPage}
|
|
/>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
</>
|
|
)}
|
|
{
|
|
isDialogOpen && (
|
|
<ImageDialog
|
|
imagePath={currentImagePath}
|
|
setIsDialogOpen={setIsDialogOpen}
|
|
/>
|
|
)
|
|
}
|
|
<Backdrop
|
|
sx={{ color: '#fff', zIndex: (theme) => theme.zIndex.drawer + 1 }}
|
|
open={openLoader}
|
|
>
|
|
<CircularProgress color="inherit" />
|
|
</Backdrop>
|
|
|
|
</Root>
|
|
);
|
|
}
|
|
|
|
const grey = {
|
|
50: "#F3F6F9",
|
|
100: "#E5EAF2",
|
|
200: "#DAE2ED",
|
|
300: "#C7D0DD",
|
|
400: "#B0B8C4",
|
|
500: "#9DA8B7",
|
|
600: "#6B7A90",
|
|
700: "#434D5B",
|
|
800: "#303740",
|
|
900: "#1C2025",
|
|
};
|
|
|
|
const Root = styled("div")(
|
|
({ theme }) => `
|
|
table {
|
|
font-family: 'IBM Plex Sans', sans-serif;
|
|
font-size: 0.875rem;
|
|
border-collapse: collapse;
|
|
width: 100%;
|
|
}
|
|
|
|
td,
|
|
th {
|
|
border: 1px solid ${theme.palette.mode === "dark" ? grey[800] : grey[200]};
|
|
text-align: left;
|
|
padding: 8px;
|
|
}
|
|
|
|
th {
|
|
background-color: ${theme.palette.mode === "dark" ? grey[900] : "#fff"};
|
|
}
|
|
`
|
|
);
|
|
|
|
const CustomTablePagination = styled(TablePagination)`
|
|
& .${classes.toolbar} {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: flex-start;
|
|
gap: 10px;
|
|
|
|
@media (min-width: 768px) {
|
|
flex-direction: row;
|
|
align-items: center;
|
|
}
|
|
}
|
|
|
|
& .${classes.selectLabel} {
|
|
margin: 0;
|
|
}
|
|
|
|
& .${classes.displayedRows} {
|
|
margin: 0;
|
|
|
|
@media (min-width: 768px) {
|
|
margin-left: auto;
|
|
}
|
|
}
|
|
|
|
& .${classes.spacer} {
|
|
display: none;
|
|
}
|
|
|
|
& .${classes.actions} {
|
|
display: flex;
|
|
gap: 0.25rem;
|
|
}
|
|
`;
|