attendence additional sheet ui changes done v1

This commit is contained in:
yokesh22 2024-06-15 01:21:32 +05:30
parent 33147c8b0e
commit 5e091b9a53
12 changed files with 1622 additions and 6170 deletions

4
.env
View File

@ -1,4 +1,4 @@
# VITE_REACT_APP_BACKEND_URL="https://sandbox.exampaper.vidh.ai"
# VITE_REACT_APP_BACKEND_URL="http://localhost:9999"
VITE_REACT_APP_BACKEND_URL="http://localhost:9999"
METABASE_BASE_URL="http://metabase.usln.in/public/question/d8774923-09bb-4cd9-903b-559d417e12cf"
VITE_REACT_APP_BACKEND_URL="https://api.exampaper.vidh.ai"
# VITE_REACT_APP_BACKEND_URL="https://api.exampaper.vidh.ai"

View File

@ -0,0 +1,14 @@
filename*,enter filename,"INSTUCTIONS: Present means mark '0', Absent means mark '1', Assigned means mark as 0, Reassigned means mark '1'. NOTE: all '*' fields are mandatory so please all '*' compulsory. Get filename from the UI which is as Imagename",,,
,,,,,
subject_code*,enter subject_code,,,,
exam_centre_code*,enter exam_centre_code,,,,
subject_title*,enter subject_title,,,,
degree_with_branch*,enter degree_with_branch,,,,
exam_date*,enter exam_date,,,,
exam_date_session,enter exam_date_session,,,,
cover_a,enter cover_a,,,,
cover_b,enter cover_b,,,,
,,,,,
student_data,,,,,
sno*,register_number*,Present/Absent*,Assigned/Reassigned*,reassigned_sno*,signed*
1,,,,,
1 filename* enter filename INSTUCTIONS: Present means mark '0', Absent means mark '1', Assigned means mark as 0, Reassigned means mark '1'. NOTE: all '*' fields are mandatory so please all '*' compulsory. Get filename from the UI which is as Imagename
2
3 subject_code* enter subject_code
4 exam_centre_code* enter exam_centre_code
5 subject_title* enter subject_title
6 degree_with_branch* enter degree_with_branch
7 exam_date* enter exam_date
8 exam_date_session enter exam_date_session
9 cover_a enter cover_a
10 cover_b enter cover_b
11
12 student_data
13 sno* register_number* Present/Absent* Assigned/Reassigned* reassigned_sno* signed*
14 1

6177
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@ import Statistics from "./Components/Statistics";
import AnomolyAttendencePage from "./Components/AnomolyAttendencePage";
import AttendenceAdditionalRecord from "./Components/AttendenceAdditionalRecord";
import AttendenceAdditionalRecordCorrection from "./Components/AttendenceAdditionalRecordCorrection";
import AttendanceAdditionalSheet from "./Components/AttendanceAdditionalSheet";
function App() {
return (
@ -26,6 +27,10 @@ function App() {
path="/anomoly/attendence/reassigned"
element={<AnomolyReassigned />}
></Route>
<Route
path='/anomoly/attendence/additionalSheet'
element={<AttendanceAdditionalSheet/>}
></Route>
<Route
path="/anomoly/reassigned/booklet"
element={<AttendenceCorrection />}

View File

@ -6,11 +6,11 @@ import {useState,useEffect} from "react"
const AnomolyAttendencePage = () =>{
const cards = [
{
title: "Reassingned Serial No",
title: "Reassigned Serial Number Updation",
url: "/anomoly/attendence/reassigned"
},
{
title: "Additional Record Insertion",
title: "Additional Student Record Insertion",
url: "/anomoly/attendence/additionalRecord",
}, {
title: "Additional Sheet Insertion",

View File

@ -0,0 +1,285 @@
// import React, { useState } from "react";
// import {
// DesktopOutlined,
// FileOutlined,
// PieChartOutlined,
// TeamOutlined,
// UserOutlined,
// } from "@ant-design/icons";
// import { Breadcrumb, Layout, Menu, Typography, theme } from "antd";
// import BookletInput from "./BookletInput";
// import { Box, Button } from "@mui/material";
// import { useEffect } from "react";
// import TextField from "@mui/material/TextField";
// import EditButton from "./EditButton";
// import { width } from "@mui/system";
// import { ToastContainer, toast } from "react-toastify";
// import "react-toastify/dist/ReactToastify.css";
// import { TablePagination } from "@mui/base/TablePagination";
// import TableComponent from "./TableComponent";
// import LoadingContainer from "./LoadingContainer";
// import HomeIcon from "@mui/icons-material/Home";
// import ArrowBackIcon from "@mui/icons-material/ArrowBack";
// import { useNavigate } from "react-router-dom";
// import QueryStatsIcon from "@mui/icons-material/QueryStats";
// import { updateAttendenceAnomolyData } from "../redux/actions/actions";
// import { useDispatch, useSelector } from "react-redux";
// import SystemNumberDialog from "./SystemNumberDialog";
// const { Header, Content, Footer, Sider } = Layout;
// function getItem(label, key, icon, children) {
// return {
// key,
// icon,
// children,
// label,
// };
// }
// const items = [getItem("Reassigned Booklet No", "1", <PieChartOutlined />)];
// const AnomolyReassigned = () => {
// const [collapsed, setCollapsed] = useState(false);
// const [anomolyData, setAnomolyData] = useState([]);
// const [filteredAnomolyData,setFilterAnomolyData] = useState([])
// const [tableRowData, setTableRowData] = useState([]);
// const [isLoading, setIsLoading] = useState(false);
// const [windowWidth, setWindowWidth] = useState(window.innerWidth);
// const [distinctExamCentreCodes,setDistinctExamCentreCodes] = useState([])
// const dispatch = useDispatch();
// const reduxAnomolyData = useSelector((state) => state.attendenceAnomolyData);
// const [filterSelectedExamCentreCode,setFilterSelectedExamCentreCode] = useState("")
// const [showSystemNoContainer, setShowSystemNoContainer] = useState(false);
// // Log Redux store state
// console.log("Redux store state after dispatch:", reduxAnomolyData);
// const reduxSystemNo = useSelector((state) => state?.systemNumber);
// console.log("redux system no: ", reduxSystemNo)
// useEffect(()=>{
// if(!reduxSystemNo){
// setShowSystemNoContainer(true);
// }else{
// fetchAnomalyData(reduxSystemNo)
// }
// },[])
// useEffect(() => {
// const handleResize = () => {
// setWindowWidth(window.innerWidth);
// };
// window.addEventListener("resize", handleResize);
// return () => {
// window.removeEventListener("resize", handleResize);
// };
// }, []);
// useEffect(() => {
// if (windowWidth < 800) {
// setCollapsed(true);
// }
// if (windowWidth > 800) {
// setCollapsed(false);
// }
// }, [windowWidth]);
// const navigate = useNavigate();
// function createData(
// attendence_serial_no,
// answer_booklet_sno,
// exam_centre_code,
// exam_centre,
// student_name,
// register_number,
// reassigned_serial_no
// ) {
// return {
// attendence_serial_no,
// answer_booklet_sno,
// exam_centre_code,
// exam_centre,
// student_name,
// register_number,
// reassigned_serial_no,
// };
// }
// const fetchAnomalyData = (reduxSystemNo) => {
// console.log("Fetching.......");
// setIsLoading(true);
// fetch(
// `${
// import.meta.env.VITE_REACT_APP_BACKEND_URL
// }/fetchAnamolyAttendenceData?sysNo=${reduxSystemNo}`,
// {
// method: "GET",
// headers: {
// "Content-Type": "application/json",
// },
// }
// )
// .then((response) => {
// console.log("Response fetched..");
// return response.json();
// })
// .then((responseData) => {
// console.log("Response Data is : ", responseData);
// setIsLoading(false);
// if (responseData.status === "success") {
// setAnomolyData(responseData?.data);
// const tmpExamCentreCodes = [];
// const distinctExamCentreCodesSet = new Set(distinctExamCentreCodes);
// for (var data of responseData?.data) {
// if (!distinctExamCentreCodesSet.has(data.exam_centre_code)) {
// distinctExamCentreCodesSet.add(data.exam_centre_code);
// tmpExamCentreCodes.push(data.exam_centre_code);
// }
// setFilterAnomolyData([...tmpExamCentreCodes])
// }
// setDistinctExamCentreCodes([...distinctExamCentreCodesSet]);
// console.log("Tmp exam centre code: ", tmpExamCentreCodes);
// // console.log("Data to be stored in store : ", responseData?.data);
// dispatch(updateAttendenceAnomolyData(responseData?.data));
// }
// })
// .catch((error) => {
// console.error("Error fetching data: ", error);
// setIsLoading(false);
// });
// };
// const handleSystemNoChange = () => {
// console.log("System No Change is called");
// setShowSystemNoContainer(true);
// };
// useEffect(()=>{
// console.log("redux sys no: ", reduxSystemNo)
// },[])
// const {
// token: { colorBgContainer, borderRadiusLG },
// } = theme.useToken();
// return (
// <Layout
// style={{
// minHeight: "100vh",
// }}
// >
// <ToastContainer />
// <Sider
// collapsible
// collapsed={collapsed}
// onCollapse={(value) => setCollapsed(value)}
// >
// <div className="demo-logo-vertical" />
// <Menu
// theme="dark"
// defaultSelectedKeys={["1"]}
// mode="inline"
// items={items}
// />
// </Sider>
// <Layout>
// <Header
// style={{
// padding: 0,
// background: colorBgContainer,
// }}
// >
// <Box className="d-flex justify-content-between h-100 py-1 px-2">
// <Button
// className="bg-primary p-1 text-light"
// onClick={() => {
// navigate(-1);
// }}
// >
// <ArrowBackIcon />
// </Button>
// <Box className="d-flex justify-content-between gap-2">
// <Box className="d-flex justify-content-between gap-md-4 gap-1 align-items-center">
// {reduxSystemNo && (
// <Box
// className="h6 p-0 m-0 text-light bg-primary rounded h-100 d-flex align-items-center px-3"
// style={{ cursor: "pointer" }}
// onClick={handleSystemNoChange}
// >
// <b>System No : </b> {reduxSystemNo}
// </Box>
// )}
// </Box>
// <Button
// className="bg-primary p-1 text-light"
// onClick={() => {
// navigate("/anomoly/reassigned/stats");
// }}
// >
// <QueryStatsIcon />
// </Button>
// <Button
// className="bg-primary p-1 text-light"
// onClick={() => {
// navigate("/");
// }}
// >
// <HomeIcon />
// </Button>
// </Box>
// </Box>
// </Header>
// <Content
// style={{
// margin: "16px 16px",
// }}
// >
// <Box className="w-100 d-flex justify-content-between">
// <Box className="w-100 d-flex justify-content-center">
// {tableRowData.length > 0 && (
// <TableComponent
// filterSelectedExamCentreCode = {filterSelectedExamCentreCode}
// setFilterSelectedExamCentreCode = {setFilterSelectedExamCentreCode}
// rows={tableRowData}
// type={"AnomolyReassigned"}
// distinctExamCentreCodes = {distinctExamCentreCodes}
// />
// )}
// {tableRowData.length == 0 && (
// <Box className="w-100 d-flex justify-content-center py-2 align-items-center text-center">
// <h6>No Data Found !!</h6>
// </Box>
// )}
// </Box>
// </Box>
// </Content>
// <Footer
// style={{
// textAlign: "center",
// }}
// >
// exampaper.vidh.ai ©{new Date().getFullYear()}
// </Footer>
// </Layout>
// {isLoading && <LoadingContainer loadingText={"Loading"} />}
// {showSystemNoContainer && (
// <SystemNumberDialog
// setShowSystemNoContainer={setShowSystemNoContainer}
// showSystemNoContainer={showSystemNoContainer}
// />
// )}
// </Layout>
// );
// };
// export default AnomolyReassigned;

View File

@ -0,0 +1,224 @@
import React, { useEffect, useState } from "react";
import { Box, Button} from "@mui/material"
import { ToastContainer } from "react-toastify";
import ArrowBackIcon from "@mui/icons-material/ArrowBack";
import HomeIcon from "@mui/icons-material/Home";
import { Breadcrumb, Layout, Menu, Typography, theme } from "antd";
import { useNavigate } from "react-router-dom";
import TableComponent from "./TableComponent";
import { useDispatch, useSelector } from "react-redux";
import TableComponentAdditionalSheet from "./TableComponentAdditionalSheet";
const { Header, Content, Footer, Sider } = Layout;
function AttendanceAdditionalSheet() {
const [tableRowData, setTableRowData] = useState([]);
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
const [collapsed, setCollapsed] = useState(false);
const [anomolyData, setAnomolyData] = useState([]);
const [filteredAnomolyData,setFilterAnomolyData] = useState([])
const [isLoading, setIsLoading] = useState(false);
const [distinctExamCentreCodes,setDistinctExamCentreCodes] = useState([])
const dispatch = useDispatch()
const reduxAnomolyData = useSelector((state) => state.attendenceAnomolyData);
const [filterSelectedExamCentreCode,setFilterSelectedExamCentreCode] = useState("")
useEffect(() => {
const handleResize = () => {
setWindowWidth(window.innerWidth);
};
window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
useEffect(() => {
if (windowWidth < 800) {
setCollapsed(true);
}
if (windowWidth > 800) {
setCollapsed(false);
}
}, [windowWidth]);
function createData(
image_name,
s3_image_path
) {
return {
image_name,
s3_image_path
};
}
useEffect(()=>{
const tmpData = [];
for (const data of anomolyData) {
tmpData.push(
createData(
data.image_name,
data.s3_image_path
// data.attendence_serial_no,
// data.answer_booklet_sno,
// data.exam_centre_code,
// data.exam_centre,
// data.student_name,
// data.register_number,
// data.reassigned_serial_no
)
);
}
console.log("Tmp data is : ", tmpData);
if (tmpData.length > 0) {
setTableRowData(tmpData);
}
},[anomolyData])
const fetchAnomalyData = () => {
console.log("Fetching.......");
setIsLoading(true);
fetch(
`${
import.meta.env.VITE_REACT_APP_BACKEND_URL
}/fetchAnamolyAttendenceAdditionalSheetData`,
{
method: "GET",
headers: {
"Content-Type": "application/json",
},
}
)
.then((response) => {
console.log("Response fetched..");
return response.json();
})
.then((responseData) => {
console.log("Response Data is : ", responseData);
setIsLoading(false);
if (responseData.status === "success") {
setAnomolyData(responseData?.data);
// const tmpExamCentreCodes = [];
// const distinctExamCentreCodesSet = new Set(distinctExamCentreCodes);
// for (var data of responseData?.data) {
// if (!distinctExamCentreCodesSet.has(data.exam_centre_code)) {
// distinctExamCentreCodesSet.add(data.exam_centre_code);
// tmpExamCentreCodes.push(data.exam_centre_code);
// }
// setFilterAnomolyData([...tmpExamCentreCodes])
// }
// setDistinctExamCentreCodes([...distinctExamCentreCodesSet]);
// console.log("Tmp exam centre code: ", tmpExamCentreCodes);
// // console.log("Data to be stored in store : ", responseData?.data);
// dispatch(updateAttendenceAnomolyData(responseData?.data));
}
})
.catch((error) => {
console.error("Error fetching data: ", error);
setIsLoading(false);
});
};
useEffect(() => {
if (reduxAnomolyData.length > 0) {
console.log("Redux anomoly data found")
setAnomolyData(reduxAnomolyData)
} else {
console.log("Redux anomoly data not found")
fetchAnomalyData();
}
}, []);
useEffect(()=>{
const tmpData = []
for(var data in anomolyData){
if(data?.exam_centre_code == filterSelectedExamCentreCode){
tmpData.push(data)
}
}
},[filterSelectedExamCentreCode])
const {
token: { colorBgContainer, borderRadiusLG },
} = theme.useToken();
const navigate = useNavigate()
return (
<Layout
style={{
minHeight: "100vh",
}}
>
<ToastContainer />
<Layout>
<Header
style={{
padding: 0,
background: colorBgContainer,
}}
>
<Box className="d-flex justify-content-between h-100 py-1 px-2">
<Button
className="bg-primary p-1 text-light"
onClick={() => {
navigate(-1);
}}
>
<ArrowBackIcon />
</Button>
<Box className="d-flex justify-content-between gap-2">
<Button
className="bg-primary p-1 text-light"
onClick={() => {
navigate("/");
}}
>
<HomeIcon />
</Button>
</Box>
</Box>
</Header>
<Content
style={{
margin: "16px 16px",
}}
>
<Box className="w-100 d-flex justify-content-between">
<Box className="w-100 d-flex justify-content-center">
{tableRowData.length > 0 && (
<TableComponent
filterSelectedExamCentreCode = {filterSelectedExamCentreCode}
setFilterSelectedExamCentreCode = {setFilterSelectedExamCentreCode}
rows={tableRowData}
type={"AdditionalSheet"}
distinctExamCentreCodes = {distinctExamCentreCodes}
/>
)}
{tableRowData.length == 0 && (
<Box className="w-100 d-flex justify-content-center py-2 align-items-center text-center">
<h6>No Data Found !!</h6>
</Box>
)}
</Box>
</Box>
</Content>
</Layout>
</Layout>
);
}
export default AttendanceAdditionalSheet;

View File

@ -5,7 +5,7 @@ import {useState,useEffect} from "react"
const Home = () => {
const cards = [
{
title: "Reassingned Serial No Anomoly Manual Updation",
title: "Reassigned Serial No Anomoly Manual Updation (ATTENDANCE)",
url: "/anomoly/attendence",
},
// {

View File

@ -0,0 +1,136 @@
import * as React from 'react';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import AppBar from '@mui/material/AppBar';
import Toolbar from '@mui/material/Toolbar';
import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography';
import CloseIcon from '@mui/icons-material/Close';
import Slide from '@mui/material/Slide';
import ZoomInIcon from '@mui/icons-material/ZoomIn';
import ZoomOutIcon from '@mui/icons-material/ZoomOut';
import RotateRightIcon from '@mui/icons-material/RotateRight';
const Transition = React.forwardRef(function Transition(props, ref) {
return <Slide direction="up" ref={ref} {...props} />;
});
export default function ImageDialog({ imagePath, setIsDialogOpen }) {
const [open, setOpen] = React.useState(false);
const [scaleWidthValue, setScaleWidthValue] = React.useState(80);
const [rotateValue, setRotateValue] = React.useState(0);
const handleClickOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
setIsDialogOpen(false);
};
React.useEffect(() => {
handleClickOpen();
}, []);
const ZoomInImage = () => {
console.log("Zooming In Image ....");
const elements = document.getElementsByClassName("scanned-img");
for (var ele of elements) {
console.log("Ele is : ", ele);
const newScaleWidthValue = scaleWidthValue + 10;
setScaleWidthValue(newScaleWidthValue);
// ele.style.transform = `scale(${newScaleValue})`;
ele.style.width = `${newScaleWidthValue}%`;
}
};
const ZoomOutImage = () => {
console.log("Zooming Out Image ....");
const elements = document.getElementsByClassName("scanned-img");
for (var ele of elements) {
console.log("Ele is : ", ele);
const newScaleWidthValue = scaleWidthValue - 10;
setScaleWidthValue(newScaleWidthValue);
// ele.style.transform = `scale(${newScaleValue})`;
ele.style.width = `${newScaleWidthValue}%`;
}
};
const RotateImageLeft = () => {
const elements = document.getElementsByClassName("scanned-img");
for (var ele of elements) {
console.log("Ele is : ", ele);
const newRotateValue = rotateValue - 90;
setRotateValue(newRotateValue);
ele.style.transform = `rotate(${newRotateValue}deg)`;
}
};
const RotateImageRight = () => {
const elements = document.getElementsByClassName("scanned-img");
for (var ele of elements) {
console.log("Ele is : ", ele);
const newRotateValue = rotateValue + 90;
setRotateValue(newRotateValue);
ele.style.transform = `rotate(${newRotateValue}deg)`;
}
};
return (
<React.Fragment>
<Dialog
fullScreen
open={open}
onClose={handleClose}
TransitionComponent={Transition}
>
<AppBar sx={{ position: 'relative' }}>
<Toolbar>
<IconButton
edge="start"
color="inherit"
onClick={handleClose}
aria-label="close"
>
<CloseIcon />
</IconButton>
<IconButton
color="inherit"
onClick={ZoomInImage}
aria-label="zoom in"
>
<ZoomInIcon />
</IconButton>
<IconButton
color="inherit"
onClick={ZoomOutImage}
aria-label="zoom out"
>
<ZoomOutIcon />
</IconButton>
<IconButton
color="inherit"
onClick={RotateImageLeft}
aria-label="rotate"
>
<RotateRightIcon />
</IconButton>
</Toolbar>
</AppBar>
<div style={{ display: 'flex', justifyContent: 'center', alignItems: 'center', height: '100%', overflow:'auto' }}>
<img
className="scanned-img"
style={{marginTop:'60%'}}
width={`${scaleWidthValue}%`}
src={`https://docs.exampaper.vidh.ai/${imagePath}`}
alt="S3 Image"
/>
</div>
</Dialog>
</React.Fragment>
);
}

View File

@ -11,6 +11,12 @@ 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 Snackbar from '@mui/material/Snackbar';
import ImageDialog from "./ImageDialog";
import { ToastContainer, toast } from "react-toastify";
export default function TableComponent({
filterSelectedExamCentreCode,
@ -26,6 +32,33 @@ export default function TableComponent({
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);
};
const [state, setState] = React.useState({
open: false,
vertical: 'top',
horizontal: 'center',
message: ''
});
const { vertical, horizontal, open, message } = state;
const handleClick = (newState) => () => {
setState({ ...newState, open: true });
};
const handleClose = () => {
setState({ ...state, open: false });
};
// 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;
@ -44,6 +77,66 @@ export default function TableComponent({
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;
}
const formData = new FormData();
formData.append('file', file);
try {
handleOpenLoader()
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()
toast.success(data.message);
// handleClick({ vertical: 'top', horizontal: 'right', message:data.message })
} else {
console.error('File upload failed:', response.statusText);
handleCloseLoader()
toast.error("failed to upload");
// handleClick({ vertical: 'top', horizontal: 'right', message:data.message })
}
} catch (error) {
console.error('Error uploading file:', error);
handleCloseLoader()
toast.error("failed to upload");
// handleClick({ vertical: 'top', horizontal: 'right', message:data.message })
}
};
return (
<Root className="overflow-auto" sx={{ maxWidth: "100%", width: 1200 }}>
{type === "AnomolyReassigned" && (
@ -435,6 +528,138 @@ export default function TableComponent({
</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={"/src/assets/additional_attendance_template.csv"}
download={"additional_attendance_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>
<Box sx={{ width: 500 }}>
<Snackbar
anchorOrigin={{ vertical, horizontal }}
open={open}
onClose={handleClose}
message={message}
key={vertical + horizontal}
/>
</Box>
<ToastContainer />
</Root>
);
}

View File

@ -0,0 +1,702 @@
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;
}
`;

View File

@ -0,0 +1,14 @@
filename*,enter filename,"INSTUCTIONS: Present means mark '0', Absent means mark '1', Assigned means mark as 0, Reassigned means mark '1'. NOTE: all '*' fields are mandatory so please all '*' compulsory. Get filename from the UI which is as Imagename",,,
,,,,,
subject_code*,enter subject_code,,,,
exam_centre_code*,enter exam_centre_code,,,,
subject_title*,enter subject_title,,,,
degree_with_branch*,enter degree_with_branch,,,,
exam_date*,enter exam_date,,,,
exam_date_session,enter exam_date_session,,,,
cover_a,enter cover_a,,,,
cover_b,enter cover_b,,,,
,,,,,
student_data,,,,,
sno*,register_number*,Present/Absent*,Assigned/Reassigned*,reassigned_sno*,signed*
1,,,,,
1 filename* enter filename INSTUCTIONS: Present means mark '0', Absent means mark '1', Assigned means mark as 0, Reassigned means mark '1'. NOTE: all '*' fields are mandatory so please all '*' compulsory. Get filename from the UI which is as Imagename
2
3 subject_code* enter subject_code
4 exam_centre_code* enter exam_centre_code
5 subject_title* enter subject_title
6 degree_with_branch* enter degree_with_branch
7 exam_date* enter exam_date
8 exam_date_session enter exam_date_session
9 cover_a enter cover_a
10 cover_b enter cover_b
11
12 student_data
13 sno* register_number* Present/Absent* Assigned/Reassigned* reassigned_sno* signed*
14 1