icleafreportui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/.env +7 -0
  2. package/README.md +70 -0
  3. package/package.json +55 -0
  4. package/public/favicon.ico +0 -0
  5. package/public/images/EResourcesImg.png +0 -0
  6. package/public/images/courseCardImg.png +0 -0
  7. package/public/images/courseInfo.png +0 -0
  8. package/public/images/exam-options.png +0 -0
  9. package/public/images/icleaf-11.png +0 -0
  10. package/public/images/icleaf_logo.png +0 -0
  11. package/public/images/template.png +0 -0
  12. package/public/images/unnamed.png +0 -0
  13. package/public/images/user.jpg +0 -0
  14. package/public/images/young-man-studying-library-using-laptop-1.png +0 -0
  15. package/public/index.html +45 -0
  16. package/public/logo192.png +0 -0
  17. package/public/logo512.png +0 -0
  18. package/public/manifest.json +25 -0
  19. package/public/robots.txt +3 -0
  20. package/src/App.css +24 -0
  21. package/src/App.js +37 -0
  22. package/src/Login.js +159 -0
  23. package/src/Reports/CourseReport.js +209 -0
  24. package/src/Reports/ExamPackReport.js +554 -0
  25. package/src/Reports/ExamReport.js +269 -0
  26. package/src/Reports/Report.js +271 -0
  27. package/src/api/client.jsx +42 -0
  28. package/src/components/Header.css +301 -0
  29. package/src/components/Header.jsx +192 -0
  30. package/src/components/Loader.jsx +23 -0
  31. package/src/components/imagePathUrl.jsx +11 -0
  32. package/src/components/sidebar.css +947 -0
  33. package/src/components/sidebar.jsx +81 -0
  34. package/src/context/TenantProvider.jsx +22 -0
  35. package/src/fonts/210000.jpg +0 -0
  36. package/src/fonts/210001.jpg +0 -0
  37. package/src/fonts/210003.jpg +0 -0
  38. package/src/fonts/210004.jpg +0 -0
  39. package/src/fonts/210006.jpg +0 -0
  40. package/src/fonts/210018.jpg +0 -0
  41. package/src/fonts/210019.jpg +0 -0
  42. package/src/fonts/210020.jpg +0 -0
  43. package/src/fonts/210279.jpg +0 -0
  44. package/src/fonts/210280.jpg +0 -0
  45. package/src/fonts/Gilroy-Black.ttf +0 -0
  46. package/src/fonts/Gilroy-BlackItalic.ttf +0 -0
  47. package/src/fonts/Gilroy-Bold.ttf +0 -0
  48. package/src/fonts/Gilroy-BoldItalic.ttf +0 -0
  49. package/src/fonts/Gilroy-ExtraBold.ttf +0 -0
  50. package/src/fonts/Gilroy-ExtraBoldItalic.ttf +0 -0
  51. package/src/fonts/Gilroy-Heavy.ttf +0 -0
  52. package/src/fonts/Gilroy-HeavyItalic.ttf +0 -0
  53. package/src/fonts/Gilroy-Light.ttf +0 -0
  54. package/src/fonts/Gilroy-LightItalic.ttf +0 -0
  55. package/src/fonts/Gilroy-Medium.ttf +0 -0
  56. package/src/fonts/Gilroy-MediumItalic.ttf +0 -0
  57. package/src/fonts/Gilroy-Regular.ttf +0 -0
  58. package/src/fonts/Gilroy-RegularItalic.ttf +0 -0
  59. package/src/fonts/Gilroy-SemiBold.ttf +0 -0
  60. package/src/fonts/Gilroy-SemiBoldItalic.ttf +0 -0
  61. package/src/fonts/Gilroy-Thin.ttf +0 -0
  62. package/src/fonts/Gilroy-ThinItalic.ttf +0 -0
  63. package/src/fonts/Gilroy-UltraLight.ttf +0 -0
  64. package/src/fonts/Gilroy-UltraLightItalic.ttf +0 -0
  65. package/src/fonts/Help - Guide Document.pdf +0 -0
  66. package/src/fonts/License.txt +144 -0
  67. package/src/fonts/More Free Fonts on fontshmonts.com.url +2 -0
  68. package/src/fonts/cover.jpg +0 -0
  69. package/src/index.css +13 -0
  70. package/src/index.js +21 -0
  71. package/src/login.css +809 -0
  72. package/src/logo.svg +1 -0
  73. package/src/package.js +10 -0
  74. package/src/reportWebVitals.js +13 -0
  75. package/src/setupTests.js +5 -0
  76. package/src/styles.css +2026 -0
  77. package/src/theme.css +107 -0
@@ -0,0 +1,554 @@
1
+ import React, { useState, useEffect, useRef } from 'react';
2
+ import axios from 'axios';
3
+ import { Autocomplete, FormControl, Button, Grid, Box, Typography, Paper, Snackbar, Alert, CircularProgress, TextField, Checkbox, InputAdornment } from '@mui/material';
4
+ import { useNavigate } from 'react-router-dom';
5
+ import Header from '../components/Header';
6
+ import Sidebar from '../components/sidebar';
7
+ import '../App.css';
8
+ import Backdrop from '@mui/material/Backdrop';
9
+ import { DataGrid } from '@mui/x-data-grid';
10
+ import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
11
+ import SearchOutlinedIcon from '@mui/icons-material/SearchOutlined';
12
+
13
+ function ExamPackReport() {
14
+ const [token] = useState(localStorage.getItem("token"));
15
+ const navigate = useNavigate();
16
+ const [subjects, setSubjects] = useState([]);
17
+ const [courses, setCourses] = useState([]);
18
+ const [selectedSubject, setSelectedSubject] = useState(null);
19
+ const [selectedCourse, setSelectedCourse] = useState(null);
20
+ const [alertMessage, setAlertMessage] = useState('');
21
+ const [loading, setLoading] = useState(false);
22
+ const [alertOpen, setAlertOpen] = useState(false);
23
+
24
+ const reportType = [
25
+ {
26
+ value: "Batch Wise",
27
+ label: "Batch Wise",
28
+ },
29
+ {
30
+ value: "User Wise",
31
+ label: "User Wise"
32
+ }
33
+ ]
34
+
35
+ const [selectedReportType, setSelectedReportType] = useState(null)
36
+ const [columns, setColumns] = useState(null)
37
+ const [rows, setRows] = useState([])
38
+ const [tableShow, setTableShow] = useState(false);
39
+
40
+ const [multiLoading, setMultiLoading] = useState(false)
41
+ const [userSelect, setUserSelect] = useState(false)
42
+
43
+ const [selectedUserList, setSelectedUserList] = useState([])
44
+ const baseURL = process.env.REACT_APP_BASE_URL
45
+
46
+ const [tableKey, setTableKey] = useState(0);
47
+ const [searchQuery, setSearchQuery] = useState("");
48
+
49
+ const filteredRows = userSelect
50
+ ? rows.filter((row) => {
51
+ const searchTerms = searchQuery
52
+ .split(',')
53
+ .map(term => term.trim().toLowerCase())
54
+ .filter(term => term.length > 0);
55
+
56
+ if (searchTerms.length === 0) return true;
57
+
58
+ const email = row.userName.toLowerCase();
59
+ const localPart = email.split('@')[0];
60
+
61
+ return searchTerms.some(term => {
62
+ if (term.includes('@')) {
63
+ // Exact full email match
64
+ return email === term;
65
+ } else {
66
+ // Match with the part before @
67
+ return localPart.includes(term);
68
+ }
69
+ });
70
+ })
71
+ : rows;
72
+
73
+ const batchListColumns = [
74
+ {
75
+ field: 'sno',
76
+ headerName: 'S.No',
77
+ width: 60,
78
+ sortable: false,
79
+ renderCell: (params) => params.api.getAllRowIds().indexOf(params.id) + 1,
80
+ },
81
+ { field: 'batchName', headerName: 'Batch Name', width: 230 },
82
+ {
83
+ field: 'batchStatus',
84
+ headerName: 'Status',
85
+ width: 170,
86
+ renderCell: (params) => {
87
+ const isActive = params.value; // Assuming it's a boolean (true/false)
88
+ return (
89
+ <span style={{
90
+ color: isActive ? 'green' : 'red',
91
+ fontWeight: 'bold'
92
+ }}>
93
+ {isActive ? 'Active' : 'Inactive'}
94
+ </span>
95
+ );
96
+ },
97
+ },
98
+ {
99
+ field: 'dailyProgressMail',
100
+ headerName: 'Daily Progress Mail',
101
+ width: 130,
102
+ renderCell: (params) => {
103
+ const isActive = params.value;
104
+ return (
105
+ <span style={{
106
+ color: isActive ? 'green' : 'red',
107
+ fontWeight: 'bold'
108
+ }}>
109
+ {isActive ? 'Active' : 'Inactive'}
110
+ </span>
111
+ );
112
+ },
113
+ },
114
+ {
115
+ field: "report",
116
+ headerName: "Report",
117
+ sortable: false,
118
+ width: 70,
119
+ renderCell: (params) =>
120
+ <FileDownloadOutlinedIcon
121
+ onClick={() => handleDownloadBatchReport(params.row)}
122
+ fontSize="small"
123
+ style={{ cursor: "pointer" }}
124
+ />
125
+ },
126
+ ];
127
+
128
+ const userListColumns = [
129
+ {
130
+ field: 'sno',
131
+ headerName: 'S.No',
132
+ width: 60,
133
+ sortable: false,
134
+ renderCell: (params) => params.api.getAllRowIds().indexOf(params.id) + 1,
135
+ },
136
+ { field: 'userName', headerName: 'User Name', width: 280 },
137
+ {
138
+ field: 'userRoleType',
139
+ headerName: 'Role Type',
140
+ width: 200,
141
+ renderCell: (params) => {
142
+ const isActive = params.value;
143
+ return (
144
+ <span style={{
145
+ color: isActive ? 'green' : 'red',
146
+ fontWeight: 'bold'
147
+ }}>
148
+ {isActive ? 'User' : ''}
149
+ </span>
150
+ );
151
+ },
152
+ },
153
+ {
154
+ field: "report",
155
+ headerName: "Report",
156
+ sortable: false,
157
+ width: 70,
158
+ renderCell: (params) =>
159
+ <FileDownloadOutlinedIcon
160
+ onClick={() => handleDownloadSingleUserReport(params.row)}
161
+ fontSize="small"
162
+ style={{ cursor: "pointer" }}
163
+ />
164
+ },
165
+ ];
166
+
167
+ useEffect(() => {
168
+ if (token == null) {
169
+ navigate("/login");
170
+ } else {
171
+ fetchAllSubjects();
172
+ }
173
+ }, []);
174
+
175
+ const fetchAllSubjects = async () => {
176
+ try {
177
+ const response = await axios({
178
+ method: "get",
179
+ url: `${baseURL}api/showAllSubjects`,
180
+ headers: { Authorization: `Bearer ${token}` },
181
+ });
182
+ setSubjects(response.data);
183
+ } catch (error) {
184
+ console.error('Error fetching subjects:', error);
185
+ }
186
+ };
187
+
188
+ const fetchCourses = async (subjectId) => {
189
+ try {
190
+ const response = await axios({
191
+ method: "get",
192
+ params: { subjectId },
193
+ url: `${baseURL}api/getCoursesBySubject`,
194
+ headers: { Authorization: `Bearer ${token}` },
195
+ });
196
+ setCourses(response.data);
197
+ } catch (error) {
198
+ console.error('Error fetching courses:', error);
199
+ }
200
+ };
201
+
202
+ const handleSubjectChange = (event, newValue) => {
203
+ setSelectedSubject(newValue);
204
+ setSelectedCourse(null);
205
+ setCourses([]);
206
+ setSelectedReportType(null)
207
+ setTableShow(false)
208
+ setRows([])
209
+ setColumns([])
210
+
211
+ if (newValue?.subjectId) {
212
+ fetchCourses(newValue.subjectId);
213
+
214
+ }
215
+ };
216
+
217
+ const handleCourseChange = (event, newValue) => {
218
+ setSelectedCourse(newValue);
219
+ setTableKey((prevKey) => prevKey + 1);
220
+ fetchReportData(newValue?.id, selectedReportType?.value);
221
+ };
222
+
223
+ const handleReportTypeChange = (event, newValue) => {
224
+ setSelectedReportType(newValue);
225
+ setTableKey((prevKey) => prevKey + 1);
226
+ fetchReportData(selectedCourse?.id, newValue?.value);
227
+ };
228
+
229
+ const fetchReportData = (courseId, reportType) => {
230
+ if (!selectedSubject?.subjectId || !courseId) return;
231
+
232
+ const apiMap = {
233
+ "Batch Wise": getBatchWiseList,
234
+ "User Wise": getUserWiseList
235
+ };
236
+
237
+ apiMap[reportType]?.(courseId);
238
+ };
239
+
240
+ const getBatchWiseList = async () => {
241
+ setLoading(true)
242
+ try {
243
+ const response = await axios.get(
244
+ `${baseURL}api/getCorpCourseBatches?subjectId=${selectedSubject.subjectId}&corpCourseId=${selectedCourse.id}`,
245
+ { headers: { Authorization: `Bearer ${token}` } }
246
+ );
247
+
248
+ if (response.status === 200) {
249
+ setUserSelect(false)
250
+ setRows(response.data.corpCourseBatches)
251
+ setColumns(batchListColumns)
252
+ setTableShow(true)
253
+ }
254
+ console.log(response.data, "Batch Wise");
255
+ } catch (error) {
256
+ setAlertOpen(true)
257
+ setAlertMessage(error.response.data.error)
258
+ setTableShow(false)
259
+ setRows([])
260
+ setColumns([])
261
+ console.error("Error fetching batch-wise data:", error);
262
+ } finally {
263
+ setLoading(false)
264
+ }
265
+ };
266
+
267
+ const getUserWiseList = async () => {
268
+ setLoading(true)
269
+ try {
270
+ const response = await axios.get(
271
+ `${baseURL}api/getCorpCourseUsers?subjectId=${selectedSubject.subjectId}&corpCourseId=${selectedCourse.id}`,
272
+ { headers: { Authorization: `Bearer ${token}` } }
273
+ );
274
+
275
+ if (response.status === 200) {
276
+ setRows(response.data.users)
277
+ setColumns(userListColumns)
278
+ setTableShow(true)
279
+ setUserSelect(true)
280
+ }
281
+ console.log(response.data, "User Wise");
282
+ } catch (error) {
283
+ setAlertOpen(true)
284
+ setAlertMessage(error.response.data.error)
285
+ setTableShow(false)
286
+ setRows([])
287
+ setColumns([])
288
+ console.error("Error fetching batch-wise data:", error);
289
+ } finally {
290
+ setLoading(false)
291
+ }
292
+ };
293
+
294
+ const handleRowSelection = (newSelection) => {
295
+ setSelectedUserList(newSelection);
296
+ };
297
+
298
+ const handleDownloadBatchReport = async (item) => {
299
+ setLoading(true)
300
+ try {
301
+ let url;
302
+ url = `${baseURL}api/downloadCourseAndBatchWiseReport?subjectId=${selectedSubject.subjectId}&corpCourseId=${selectedCourse.id}&batchId=${item.id}`
303
+
304
+ const response = await axios.post(url, {}, {
305
+ responseType: 'blob',
306
+ headers: {
307
+ Authorization: `Bearer ${token}`
308
+ }
309
+ });
310
+ const text = await response.data.text();
311
+ if (text.includes('Course not configured')) {
312
+ setAlertMessage('Course not configured.');
313
+ setAlertOpen(true);
314
+ } else {
315
+ const filename = `${item.batchName}_Batch_Report.xlsx`;
316
+ const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
317
+ const blobUrl = URL.createObjectURL(blob);
318
+ const link = document.createElement('a');
319
+ link.href = blobUrl;
320
+ link.setAttribute('download', filename);
321
+ document.body.appendChild(link);
322
+ link.click();
323
+ link.parentNode.removeChild(link);
324
+ URL.revokeObjectURL(blobUrl);
325
+ }
326
+ } catch (error) {
327
+ setAlertOpen(true)
328
+ setAlertMessage(error.response.data.error)
329
+ console.error('Error downloading course report:', error);
330
+ } finally {
331
+ setLoading(false)
332
+ }
333
+ }
334
+
335
+ const handleDownloadSingleUserReport = async (item) => {
336
+ setLoading(true)
337
+ try {
338
+ let url;
339
+ url = `${baseURL}api/downloadExamPackReportByCourse?userId=${item.id}&corpCourseId=${selectedCourse.id}&subjectId=${selectedSubject.subjectId}`
340
+
341
+ const response = await axios.get(url, {
342
+ responseType: 'blob',
343
+ headers: {
344
+ Authorization: `Bearer ${token}`
345
+ }
346
+ });
347
+ const text = await response.data.text();
348
+ if (text.includes('Course not configured')) {
349
+ setAlertMessage('Course not configured.');
350
+ setAlertOpen(true);
351
+ } else {
352
+ const filename = `${item.userName}_Pack_Report.xlsx`;
353
+ const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
354
+ const blobUrl = URL.createObjectURL(blob);
355
+ const link = document.createElement('a');
356
+ link.href = blobUrl;
357
+ link.setAttribute('download', filename);
358
+ document.body.appendChild(link);
359
+ link.click();
360
+ link.parentNode.removeChild(link);
361
+ URL.revokeObjectURL(blobUrl);
362
+ }
363
+ } catch (error) {
364
+ setAlertOpen(true)
365
+ setAlertMessage(error.response.data.error)
366
+ console.error('Error downloading course report:', error);
367
+ } finally {
368
+ setLoading(false)
369
+ }
370
+ }
371
+
372
+ const handleSummeryDownload = async () => {
373
+ setMultiLoading(true)
374
+ const selectedRowIds = rows
375
+ .filter(row => selectedUserList.includes(row.id))
376
+ .map(row => row.id);
377
+ try {
378
+ let url;
379
+ url = `${baseURL}api/downloadExamPacksAndUserIdsReport?subjectId=${selectedSubject.subjectId}&corpCourseId=${selectedCourse.id}`
380
+ const response = await axios.post(url, {
381
+ userIds: selectedRowIds,
382
+ }, {
383
+ responseType: 'blob',
384
+ headers: {
385
+ Authorization: `Bearer ${token}`
386
+ }
387
+ });
388
+ const text = await response.data.text();
389
+ if (text.includes('Course not configured')) {
390
+ setAlertMessage('Course not configured.');
391
+ setAlertOpen(true);
392
+ } else {
393
+ const filename = 'User_Exam_Pack_Report.xlsx';
394
+ const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
395
+ const blobUrl = URL.createObjectURL(blob);
396
+ const link = document.createElement('a');
397
+ link.href = blobUrl;
398
+ link.setAttribute('download', filename);
399
+ document.body.appendChild(link);
400
+ link.click();
401
+ link.parentNode.removeChild(link);
402
+ URL.revokeObjectURL(blobUrl);
403
+ }
404
+ } catch (error) {
405
+ setAlertOpen(true)
406
+ setAlertMessage(error.response.data.error)
407
+ console.error('Error downloading course report:', error);
408
+ } finally {
409
+ setMultiLoading(false)
410
+ }
411
+ }
412
+
413
+ const paginationModel = { page: 0, pageSize: 5 };
414
+
415
+ return (
416
+ <div>
417
+ <Header />
418
+ <Sidebar />
419
+ <div style={{ padding: "100px 0px 0px 100px", marginLeft: "140px" }}>
420
+ <Box sx={{ maxWidth: 800, mx: 'auto', my: 4, p: 3, backgroundColor: '#f5f5f5', borderRadius: 2 }}>
421
+ <Paper elevation={6} sx={{ p: 5, textAlign: 'center', backgroundColor: '#fff' }}>
422
+ <Typography variant="h4" component="h1" gutterBottom sx={{ color: '#3f51b5', marginBottom: "20px" }}>
423
+ Exam Pack Report
424
+ </Typography>
425
+ <Grid container spacing={2} justifyContent="center">
426
+ <Grid item xs={12} sm={4}>
427
+ <FormControl fullWidth>
428
+ <Autocomplete
429
+ id="ddlSubject"
430
+ options={subjects}
431
+ getOptionLabel={(option) => option.subjectName}
432
+ value={selectedSubject}
433
+ onChange={handleSubjectChange}
434
+ renderInput={(params) => <TextField {...params} label="Subject" />}
435
+ />
436
+ </FormControl>
437
+ </Grid>
438
+
439
+ <Grid item xs={12} sm={4}>
440
+ <FormControl fullWidth>
441
+ <Autocomplete
442
+ id="ddlCourse"
443
+ options={courses}
444
+ getOptionLabel={(option) => option.courseName}
445
+ value={selectedCourse}
446
+ onChange={handleCourseChange}
447
+ renderInput={(params) => <TextField {...params} label="Course" />}
448
+ />
449
+ </FormControl>
450
+ </Grid>
451
+
452
+ <Grid item xs={12} sm={4}>
453
+ <FormControl fullWidth>
454
+ <Autocomplete
455
+ id="checkboxes-tags-demo"
456
+ options={reportType}
457
+ getOptionLabel={(option) => option.label}
458
+ value={selectedReportType}
459
+ onChange={handleReportTypeChange}
460
+ renderInput={(params) => (
461
+ <TextField {...params} label="Report Type" />
462
+ )}
463
+ />
464
+ </FormControl>
465
+ </Grid>
466
+
467
+
468
+ </Grid>
469
+
470
+ {tableShow &&
471
+ <>
472
+ <Box sx={{ display: "flex", justifyContent: "flex-end", width: "100%", margin: "20px 0px" }}>
473
+ {userSelect &&
474
+ <Button
475
+ variant="contained"
476
+ color="primary"
477
+ onClick={handleSummeryDownload}
478
+ disabled={selectedUserList.length <= 1}
479
+ sx={{ width: 'auto', backgroundColor: '#3f51b5', '&:hover': { backgroundColor: '#303f9f' } }}
480
+ >
481
+ {multiLoading ? 'Downloading...' : 'Download Report'}
482
+ </Button>
483
+ }
484
+ </Box>
485
+
486
+ <Paper sx={{ width: '100%' }}>
487
+ <div style={{ display: "flex", justifyContent: "end", alignItems: "center", height: "100%", padding: "10px 5px" }}>
488
+ {userSelect && (
489
+ <TextField
490
+ label="Search"
491
+ variant="outlined"
492
+ size="small"
493
+ sx={{ mb: 2 }}
494
+ value={searchQuery}
495
+ onChange={(e) => setSearchQuery(e.target.value)}
496
+ InputProps={{
497
+ endAdornment: (
498
+ <InputAdornment position="start">
499
+ <SearchOutlinedIcon />
500
+ </InputAdornment>
501
+ ),
502
+ }}
503
+ />
504
+ )}
505
+ </div>
506
+ <DataGrid
507
+ key={tableKey}
508
+ rows={userSelect ? filteredRows : rows}
509
+ columns={columns}
510
+ initialState={{ pagination: { paginationModel } }}
511
+ pageSizeOptions={[5, 10, 50, 100]}
512
+ checkboxSelection={userSelect}
513
+ disableRowSelectionOnClick
514
+ onRowSelectionModelChange={handleRowSelection}
515
+ sx={{
516
+ border: 0,
517
+ "& .MuiTablePagination-selectLabel": {
518
+ margin: "unset"
519
+ },
520
+ "& .MuiTablePagination-displayedRows": {
521
+ margin: "unset"
522
+ }
523
+ }}
524
+ />
525
+ </Paper>
526
+
527
+ </>
528
+ }
529
+ </Paper>
530
+ </Box>
531
+
532
+ <Snackbar
533
+ open={alertOpen}
534
+ autoHideDuration={6000}
535
+ onClose={() => setAlertOpen(false)}
536
+ anchorOrigin={{ vertical: 'top', horizontal: 'center' }}
537
+ >
538
+ <Alert onClose={() => setAlertOpen(false)} severity="info" sx={{ width: '100%' }}>
539
+ {alertMessage}
540
+ </Alert>
541
+ </Snackbar>
542
+
543
+ <Backdrop
544
+ sx={(theme) => ({ color: '#fff', zIndex: theme.zIndex.drawer + 1 })}
545
+ open={loading || multiLoading}
546
+ >
547
+ <CircularProgress color="inherit" />
548
+ </Backdrop>
549
+ </div>
550
+ </div>
551
+ );
552
+ }
553
+
554
+ export default ExamPackReport;