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.
- package/.env +7 -0
- package/README.md +70 -0
- package/package.json +55 -0
- package/public/favicon.ico +0 -0
- package/public/images/EResourcesImg.png +0 -0
- package/public/images/courseCardImg.png +0 -0
- package/public/images/courseInfo.png +0 -0
- package/public/images/exam-options.png +0 -0
- package/public/images/icleaf-11.png +0 -0
- package/public/images/icleaf_logo.png +0 -0
- package/public/images/template.png +0 -0
- package/public/images/unnamed.png +0 -0
- package/public/images/user.jpg +0 -0
- package/public/images/young-man-studying-library-using-laptop-1.png +0 -0
- package/public/index.html +45 -0
- package/public/logo192.png +0 -0
- package/public/logo512.png +0 -0
- package/public/manifest.json +25 -0
- package/public/robots.txt +3 -0
- package/src/App.css +24 -0
- package/src/App.js +37 -0
- package/src/Login.js +159 -0
- package/src/Reports/CourseReport.js +209 -0
- package/src/Reports/ExamPackReport.js +554 -0
- package/src/Reports/ExamReport.js +269 -0
- package/src/Reports/Report.js +271 -0
- package/src/api/client.jsx +42 -0
- package/src/components/Header.css +301 -0
- package/src/components/Header.jsx +192 -0
- package/src/components/Loader.jsx +23 -0
- package/src/components/imagePathUrl.jsx +11 -0
- package/src/components/sidebar.css +947 -0
- package/src/components/sidebar.jsx +81 -0
- package/src/context/TenantProvider.jsx +22 -0
- package/src/fonts/210000.jpg +0 -0
- package/src/fonts/210001.jpg +0 -0
- package/src/fonts/210003.jpg +0 -0
- package/src/fonts/210004.jpg +0 -0
- package/src/fonts/210006.jpg +0 -0
- package/src/fonts/210018.jpg +0 -0
- package/src/fonts/210019.jpg +0 -0
- package/src/fonts/210020.jpg +0 -0
- package/src/fonts/210279.jpg +0 -0
- package/src/fonts/210280.jpg +0 -0
- package/src/fonts/Gilroy-Black.ttf +0 -0
- package/src/fonts/Gilroy-BlackItalic.ttf +0 -0
- package/src/fonts/Gilroy-Bold.ttf +0 -0
- package/src/fonts/Gilroy-BoldItalic.ttf +0 -0
- package/src/fonts/Gilroy-ExtraBold.ttf +0 -0
- package/src/fonts/Gilroy-ExtraBoldItalic.ttf +0 -0
- package/src/fonts/Gilroy-Heavy.ttf +0 -0
- package/src/fonts/Gilroy-HeavyItalic.ttf +0 -0
- package/src/fonts/Gilroy-Light.ttf +0 -0
- package/src/fonts/Gilroy-LightItalic.ttf +0 -0
- package/src/fonts/Gilroy-Medium.ttf +0 -0
- package/src/fonts/Gilroy-MediumItalic.ttf +0 -0
- package/src/fonts/Gilroy-Regular.ttf +0 -0
- package/src/fonts/Gilroy-RegularItalic.ttf +0 -0
- package/src/fonts/Gilroy-SemiBold.ttf +0 -0
- package/src/fonts/Gilroy-SemiBoldItalic.ttf +0 -0
- package/src/fonts/Gilroy-Thin.ttf +0 -0
- package/src/fonts/Gilroy-ThinItalic.ttf +0 -0
- package/src/fonts/Gilroy-UltraLight.ttf +0 -0
- package/src/fonts/Gilroy-UltraLightItalic.ttf +0 -0
- package/src/fonts/Help - Guide Document.pdf +0 -0
- package/src/fonts/License.txt +144 -0
- package/src/fonts/More Free Fonts on fontshmonts.com.url +2 -0
- package/src/fonts/cover.jpg +0 -0
- package/src/index.css +13 -0
- package/src/index.js +21 -0
- package/src/login.css +809 -0
- package/src/logo.svg +1 -0
- package/src/package.js +10 -0
- package/src/reportWebVitals.js +13 -0
- package/src/setupTests.js +5 -0
- package/src/styles.css +2026 -0
- 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;
|