icleafreportui 0.2.11 → 0.2.12
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/dist/App.js +4 -3
- package/dist/Login.js +54 -14
- package/dist/Reports/CourseReport.js +3 -2
- package/dist/Reports/ExamPackReport.js +175 -76
- package/dist/Reports/ExamReport.js +119 -51
- package/dist/Reports/ImportContent/ReportViewer.js +1 -0
- package/package.json +1 -1
package/dist/App.js
CHANGED
|
@@ -8,7 +8,8 @@ import ExamPackReport from './Reports/ExamPackReport';
|
|
|
8
8
|
import { TenantProvider } from './context/TenantProvider'; // ADD THIS
|
|
9
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
10
|
const App = () => {
|
|
11
|
-
const userToken = localStorage.getItem("token")
|
|
11
|
+
// const userToken = localStorage.getItem("token")
|
|
12
|
+
const tenantToken = localStorage.getItem("tenantToken");
|
|
12
13
|
|
|
13
14
|
// Configuration for API calls
|
|
14
15
|
const config = {
|
|
@@ -23,10 +24,10 @@ const App = () => {
|
|
|
23
24
|
children: /*#__PURE__*/_jsxs(Routes, {
|
|
24
25
|
children: [/*#__PURE__*/_jsx(Route, {
|
|
25
26
|
path: "/",
|
|
26
|
-
element:
|
|
27
|
+
element: tenantToken != null ? /*#__PURE__*/_jsx(Report, {}) : /*#__PURE__*/_jsx(Login, {})
|
|
27
28
|
}), /*#__PURE__*/_jsx(Route, {
|
|
28
29
|
path: "/login",
|
|
29
|
-
element:
|
|
30
|
+
element: tenantToken != null ? /*#__PURE__*/_jsx(Report, {}) : /*#__PURE__*/_jsx(Login, {})
|
|
30
31
|
}), /*#__PURE__*/_jsx(Route, {
|
|
31
32
|
path: "/report",
|
|
32
33
|
element: /*#__PURE__*/_jsx(Report, {})
|
package/dist/Login.js
CHANGED
|
@@ -41,29 +41,69 @@ const Login = () => {
|
|
|
41
41
|
return "icleaf";
|
|
42
42
|
};
|
|
43
43
|
const baseURL = process.env.REACT_APP_BASE_URL;
|
|
44
|
+
|
|
45
|
+
// const handleLogin = async (e) => {
|
|
46
|
+
// e.preventDefault();
|
|
47
|
+
|
|
48
|
+
// const subdomain = getSubDomain();
|
|
49
|
+
// try {
|
|
50
|
+
// // const response = await axios.get(`${baseURL}` + "api/login?userName=" + username + "&password=" + password);
|
|
51
|
+
// const response = await axios.get(
|
|
52
|
+
// `${baseURL}api/login?userName=${username}&password=${password}&subdomain=${subdomain}`
|
|
53
|
+
// );
|
|
54
|
+
// console.log(response, "response")
|
|
55
|
+
// if (response.data) {
|
|
56
|
+
// var res = response.data;
|
|
57
|
+
// if (res == "User Not Found") {
|
|
58
|
+
// setError(res);
|
|
59
|
+
// setOpenAlert(true);
|
|
60
|
+
// } else if (res == "UserName or Password cannot be empty") {
|
|
61
|
+
// setError(res);
|
|
62
|
+
// setOpenAlert(true);
|
|
63
|
+
// } else {
|
|
64
|
+
// localStorage.setItem("token", response.data);
|
|
65
|
+
// localStorage.setItem("subdomain", subdomain);
|
|
66
|
+
// navigate('/Report');
|
|
67
|
+
|
|
68
|
+
// }
|
|
69
|
+
// }
|
|
70
|
+
|
|
71
|
+
// } catch (error) {
|
|
72
|
+
// setError('User Not Found');
|
|
73
|
+
// setOpenAlert(true);
|
|
74
|
+
// }
|
|
75
|
+
// };
|
|
76
|
+
|
|
44
77
|
const handleLogin = async e => {
|
|
45
78
|
e.preventDefault();
|
|
46
79
|
const subdomain = getSubDomain();
|
|
47
80
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
console.log(response,
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
} else if (res == "UserName or Password cannot be empty") {
|
|
81
|
+
const response = await axios.get(`${baseURL}/api/login?userName=${username}&password=${password}&subdomain=${subdomain}`);
|
|
82
|
+
console.log("Response data:", response.data);
|
|
83
|
+
console.log("Type of response:", typeof response.data);
|
|
84
|
+
const res = response.data;
|
|
85
|
+
|
|
86
|
+
// Check if response is an error message (string)
|
|
87
|
+
if (typeof res === 'string') {
|
|
88
|
+
if (res === "User Not Found" || res === "UserName or Password cannot be empty" || res === "Invalid Company" || res === "User not allowed") {
|
|
57
89
|
setError(res);
|
|
58
90
|
setOpenAlert(true);
|
|
59
|
-
|
|
60
|
-
localStorage.setItem("token", response.data);
|
|
61
|
-
localStorage.setItem("subdomain", subdomain);
|
|
62
|
-
navigate('/Report');
|
|
91
|
+
return;
|
|
63
92
|
}
|
|
64
93
|
}
|
|
94
|
+
|
|
95
|
+
// If we reach here, it's a success (tenant token)
|
|
96
|
+
if (res) {
|
|
97
|
+
localStorage.setItem("tenantToken", res);
|
|
98
|
+
localStorage.setItem("subdomain", subdomain);
|
|
99
|
+
navigate('/Report');
|
|
100
|
+
} else {
|
|
101
|
+
setError("Login failed");
|
|
102
|
+
setOpenAlert(true);
|
|
103
|
+
}
|
|
65
104
|
} catch (error) {
|
|
66
|
-
|
|
105
|
+
console.error("Login error:", error);
|
|
106
|
+
setError(error.response?.data || "User Not Found");
|
|
67
107
|
setOpenAlert(true);
|
|
68
108
|
}
|
|
69
109
|
};
|
|
@@ -8,7 +8,8 @@ import '../App.css';
|
|
|
8
8
|
import { useTenant } from '../context/TenantProvider';
|
|
9
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
10
|
function CourseReport() {
|
|
11
|
-
const [token, setToken] = useState(localStorage.getItem("token"));
|
|
11
|
+
// const [token, setToken] = useState(localStorage.getItem("token"));
|
|
12
|
+
const tenantToken = localStorage.getItem("tenantToken");
|
|
12
13
|
const navigate = useNavigate();
|
|
13
14
|
const [subjects, setSubjects] = useState([]);
|
|
14
15
|
const [courses, setCourses] = useState([]);
|
|
@@ -24,7 +25,7 @@ function CourseReport() {
|
|
|
24
25
|
apiClient
|
|
25
26
|
} = useTenant();
|
|
26
27
|
useEffect(() => {
|
|
27
|
-
if (
|
|
28
|
+
if (tenantToken == null) {
|
|
28
29
|
navigate("/login");
|
|
29
30
|
} else {
|
|
30
31
|
fetchAllSubjects();
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState, useEffect, useRef } from 'react';
|
|
2
|
-
import axios from 'axios';
|
|
2
|
+
// import axios from 'axios'; // ❌ OLD - Commented
|
|
3
3
|
import { Autocomplete, FormControl, Button, Grid, Box, Typography, Paper, Snackbar, Alert, CircularProgress, TextField, Checkbox, InputAdornment } from '@mui/material';
|
|
4
4
|
import { useNavigate } from 'react-router-dom';
|
|
5
5
|
import Header from '../components/Header';
|
|
@@ -9,10 +9,16 @@ import Backdrop from '@mui/material/Backdrop';
|
|
|
9
9
|
import { DataGrid } from '@mui/x-data-grid';
|
|
10
10
|
import FileDownloadOutlinedIcon from '@mui/icons-material/FileDownloadOutlined';
|
|
11
11
|
import SearchOutlinedIcon from '@mui/icons-material/SearchOutlined';
|
|
12
|
+
import { useTenant } from '../context/TenantProvider'; // ✅ NEW - Add this
|
|
12
13
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
13
14
|
function ExamPackReport() {
|
|
14
|
-
const [token] = useState(localStorage.getItem("token"));
|
|
15
|
+
// const [token] = useState(localStorage.getItem("token")); // ❌ OLD - Commented
|
|
16
|
+
const tenantToken = localStorage.getItem("tenantToken"); // ✅ NEW
|
|
15
17
|
const navigate = useNavigate();
|
|
18
|
+
const {
|
|
19
|
+
apiClient
|
|
20
|
+
} = useTenant(); // ✅ NEW - Add this
|
|
21
|
+
|
|
16
22
|
const [subjects, setSubjects] = useState([]);
|
|
17
23
|
const [courses, setCourses] = useState([]);
|
|
18
24
|
const [selectedSubject, setSelectedSubject] = useState(null);
|
|
@@ -44,10 +50,8 @@ function ExamPackReport() {
|
|
|
44
50
|
const localPart = email.split('@')[0];
|
|
45
51
|
return searchTerms.some(term => {
|
|
46
52
|
if (term.includes('@')) {
|
|
47
|
-
// Exact full email match
|
|
48
53
|
return email === term;
|
|
49
54
|
} else {
|
|
50
|
-
// Match with the part before @
|
|
51
55
|
return localPart.includes(term);
|
|
52
56
|
}
|
|
53
57
|
});
|
|
@@ -67,7 +71,7 @@ function ExamPackReport() {
|
|
|
67
71
|
headerName: 'Status',
|
|
68
72
|
width: 170,
|
|
69
73
|
renderCell: params => {
|
|
70
|
-
const isActive = params.value;
|
|
74
|
+
const isActive = params.value;
|
|
71
75
|
return /*#__PURE__*/_jsx("span", {
|
|
72
76
|
style: {
|
|
73
77
|
color: isActive ? 'green' : 'red',
|
|
@@ -141,39 +145,59 @@ function ExamPackReport() {
|
|
|
141
145
|
})
|
|
142
146
|
}];
|
|
143
147
|
useEffect(() => {
|
|
144
|
-
if (token == null) {
|
|
148
|
+
// if (token == null) { // ❌ OLD - Commented
|
|
149
|
+
if (tenantToken == null) {
|
|
150
|
+
// ✅ NEW
|
|
145
151
|
navigate("/login");
|
|
146
152
|
} else {
|
|
147
153
|
fetchAllSubjects();
|
|
148
154
|
}
|
|
149
155
|
}, []);
|
|
156
|
+
|
|
157
|
+
// ❌ OLD - Commented
|
|
158
|
+
// const fetchAllSubjects = async () => {
|
|
159
|
+
// try {
|
|
160
|
+
// const response = await axios({
|
|
161
|
+
// method: "get",
|
|
162
|
+
// url: `${baseURL}api/showAllSubjects`,
|
|
163
|
+
// headers: { Authorization: `Bearer ${token}` },
|
|
164
|
+
// });
|
|
165
|
+
// setSubjects(response.data);
|
|
166
|
+
// } catch (error) {
|
|
167
|
+
// console.error('Error fetching subjects:', error);
|
|
168
|
+
// }
|
|
169
|
+
// };
|
|
170
|
+
|
|
171
|
+
// ✅ NEW - Using apiClient
|
|
150
172
|
const fetchAllSubjects = async () => {
|
|
151
173
|
try {
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
url: `${baseURL}api/showAllSubjects`,
|
|
155
|
-
headers: {
|
|
156
|
-
Authorization: `Bearer ${token}`
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
setSubjects(response.data);
|
|
174
|
+
const data = await apiClient.get('/api/showAllSubjects');
|
|
175
|
+
setSubjects(data);
|
|
160
176
|
} catch (error) {
|
|
161
177
|
console.error('Error fetching subjects:', error);
|
|
162
178
|
}
|
|
163
179
|
};
|
|
180
|
+
|
|
181
|
+
// ❌ OLD - Commented
|
|
182
|
+
// const fetchCourses = async (subjectId) => {
|
|
183
|
+
// try {
|
|
184
|
+
// const response = await axios({
|
|
185
|
+
// method: "get",
|
|
186
|
+
// params: { subjectId },
|
|
187
|
+
// url: `${baseURL}api/getCoursesBySubject`,
|
|
188
|
+
// headers: { Authorization: `Bearer ${token}` },
|
|
189
|
+
// });
|
|
190
|
+
// setCourses(response.data);
|
|
191
|
+
// } catch (error) {
|
|
192
|
+
// console.error('Error fetching courses:', error);
|
|
193
|
+
// }
|
|
194
|
+
// };
|
|
195
|
+
|
|
196
|
+
// ✅ NEW - Using apiClient
|
|
164
197
|
const fetchCourses = async subjectId => {
|
|
165
198
|
try {
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
params: {
|
|
169
|
-
subjectId
|
|
170
|
-
},
|
|
171
|
-
url: `${baseURL}api/getCoursesBySubject`,
|
|
172
|
-
headers: {
|
|
173
|
-
Authorization: `Bearer ${token}`
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
setCourses(response.data);
|
|
199
|
+
const data = await apiClient.get(`/api/getCoursesBySubject?subjectId=${subjectId}`);
|
|
200
|
+
setCourses(data);
|
|
177
201
|
} catch (error) {
|
|
178
202
|
console.error('Error fetching courses:', error);
|
|
179
203
|
}
|
|
@@ -208,24 +232,47 @@ function ExamPackReport() {
|
|
|
208
232
|
};
|
|
209
233
|
apiMap[reportType]?.(courseId);
|
|
210
234
|
};
|
|
235
|
+
|
|
236
|
+
// ❌ OLD - Commented
|
|
237
|
+
// const getBatchWiseList = async () => {
|
|
238
|
+
// setLoading(true)
|
|
239
|
+
// try {
|
|
240
|
+
// const response = await axios.get(
|
|
241
|
+
// `${baseURL}api/getCorpCourseBatches?subjectId=${selectedSubject.subjectId}&corpCourseId=${selectedCourse.id}`,
|
|
242
|
+
// { headers: { Authorization: `Bearer ${token}` } }
|
|
243
|
+
// );
|
|
244
|
+
// if (response.status === 200) {
|
|
245
|
+
// setUserSelect(false)
|
|
246
|
+
// setRows(response.data.corpCourseBatches)
|
|
247
|
+
// setColumns(batchListColumns)
|
|
248
|
+
// setTableShow(true)
|
|
249
|
+
// }
|
|
250
|
+
// } catch (error) {
|
|
251
|
+
// setAlertOpen(true)
|
|
252
|
+
// setAlertMessage(error.response.data.error)
|
|
253
|
+
// setTableShow(false)
|
|
254
|
+
// setRows([])
|
|
255
|
+
// setColumns([])
|
|
256
|
+
// console.error("Error fetching batch-wise data:", error);
|
|
257
|
+
// } finally {
|
|
258
|
+
// setLoading(false)
|
|
259
|
+
// }
|
|
260
|
+
// };
|
|
261
|
+
|
|
262
|
+
// ✅ NEW - Using apiClient
|
|
211
263
|
const getBatchWiseList = async () => {
|
|
212
264
|
setLoading(true);
|
|
213
265
|
try {
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
Authorization: `Bearer ${token}`
|
|
217
|
-
}
|
|
218
|
-
});
|
|
219
|
-
if (response.status === 200) {
|
|
266
|
+
const data = await apiClient.get(`/api/getCorpCourseBatches?subjectId=${selectedSubject.subjectId}&corpCourseId=${selectedCourse.id}`);
|
|
267
|
+
if (data) {
|
|
220
268
|
setUserSelect(false);
|
|
221
|
-
setRows(
|
|
269
|
+
setRows(data.corpCourseBatches || []);
|
|
222
270
|
setColumns(batchListColumns);
|
|
223
271
|
setTableShow(true);
|
|
224
272
|
}
|
|
225
|
-
console.log(response.data, "Batch Wise");
|
|
226
273
|
} catch (error) {
|
|
227
274
|
setAlertOpen(true);
|
|
228
|
-
setAlertMessage(error.response
|
|
275
|
+
setAlertMessage(error.response?.data?.error || 'Error fetching batch data');
|
|
229
276
|
setTableShow(false);
|
|
230
277
|
setRows([]);
|
|
231
278
|
setColumns([]);
|
|
@@ -234,28 +281,51 @@ function ExamPackReport() {
|
|
|
234
281
|
setLoading(false);
|
|
235
282
|
}
|
|
236
283
|
};
|
|
284
|
+
|
|
285
|
+
// ❌ OLD - Commented
|
|
286
|
+
// const getUserWiseList = async () => {
|
|
287
|
+
// setLoading(true)
|
|
288
|
+
// try {
|
|
289
|
+
// const response = await axios.get(
|
|
290
|
+
// `${baseURL}api/getCorpCourseUsers?subjectId=${selectedSubject.subjectId}&corpCourseId=${selectedCourse.id}`,
|
|
291
|
+
// { headers: { Authorization: `Bearer ${token}` } }
|
|
292
|
+
// );
|
|
293
|
+
// if (response.status === 200) {
|
|
294
|
+
// setRows(response.data.users)
|
|
295
|
+
// setColumns(userListColumns)
|
|
296
|
+
// setTableShow(true)
|
|
297
|
+
// setUserSelect(true)
|
|
298
|
+
// }
|
|
299
|
+
// } catch (error) {
|
|
300
|
+
// setAlertOpen(true)
|
|
301
|
+
// setAlertMessage(error.response.data.error)
|
|
302
|
+
// setTableShow(false)
|
|
303
|
+
// setRows([])
|
|
304
|
+
// setColumns([])
|
|
305
|
+
// console.error("Error fetching batch-wise data:", error);
|
|
306
|
+
// } finally {
|
|
307
|
+
// setLoading(false)
|
|
308
|
+
// }
|
|
309
|
+
// };
|
|
310
|
+
|
|
311
|
+
// ✅ NEW - Using apiClient
|
|
237
312
|
const getUserWiseList = async () => {
|
|
238
313
|
setLoading(true);
|
|
239
314
|
try {
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
if (response.status === 200) {
|
|
246
|
-
setRows(response.data.users);
|
|
315
|
+
const data = await apiClient.get(`/api/getCorpCourseUsers?subjectId=${selectedSubject.subjectId}&corpCourseId=${selectedCourse.id}`);
|
|
316
|
+
if (data) {
|
|
317
|
+
setRows(data.users || []);
|
|
247
318
|
setColumns(userListColumns);
|
|
248
319
|
setTableShow(true);
|
|
249
320
|
setUserSelect(true);
|
|
250
321
|
}
|
|
251
|
-
console.log(response.data, "User Wise");
|
|
252
322
|
} catch (error) {
|
|
253
323
|
setAlertOpen(true);
|
|
254
|
-
setAlertMessage(error.response
|
|
324
|
+
setAlertMessage(error.response?.data?.error || 'Error fetching user data');
|
|
255
325
|
setTableShow(false);
|
|
256
326
|
setRows([]);
|
|
257
327
|
setColumns([]);
|
|
258
|
-
console.error("Error fetching
|
|
328
|
+
console.error("Error fetching user-wise data:", error);
|
|
259
329
|
} finally {
|
|
260
330
|
setLoading(false);
|
|
261
331
|
}
|
|
@@ -263,27 +333,47 @@ function ExamPackReport() {
|
|
|
263
333
|
const handleRowSelection = newSelection => {
|
|
264
334
|
setSelectedUserList(newSelection);
|
|
265
335
|
};
|
|
336
|
+
|
|
337
|
+
// ❌ OLD - Commented
|
|
338
|
+
// const handleDownloadBatchReport = async (item) => {
|
|
339
|
+
// setLoading(true)
|
|
340
|
+
// try {
|
|
341
|
+
// let url;
|
|
342
|
+
// url = `${baseURL}api/downloadCourseAndBatchWiseReport?subjectId=${selectedSubject.subjectId}&corpCourseId=${selectedCourse.id}&batchId=${item.id}`
|
|
343
|
+
// const response = await axios.post(url, {}, {
|
|
344
|
+
// responseType: 'blob',
|
|
345
|
+
// headers: { Authorization: `Bearer ${token}` }
|
|
346
|
+
// });
|
|
347
|
+
// // ... rest of download logic
|
|
348
|
+
// } catch (error) {
|
|
349
|
+
// // ... error handling
|
|
350
|
+
// } finally {
|
|
351
|
+
// setLoading(false)
|
|
352
|
+
// }
|
|
353
|
+
// }
|
|
354
|
+
|
|
355
|
+
// ✅ NEW - Using fetch with tenant token
|
|
266
356
|
const handleDownloadBatchReport = async item => {
|
|
267
357
|
setLoading(true);
|
|
268
358
|
try {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
responseType: 'blob',
|
|
359
|
+
const url = `${baseURL}/api/downloadCourseAndBatchWiseReport?subjectId=${selectedSubject.subjectId}&corpCourseId=${selectedCourse.id}&batchId=${item.id}`;
|
|
360
|
+
const response = await fetch(url, {
|
|
361
|
+
method: 'POST',
|
|
273
362
|
headers: {
|
|
274
|
-
|
|
363
|
+
'X-Tenant-Token': apiClient.tenantToken
|
|
275
364
|
}
|
|
276
365
|
});
|
|
277
|
-
const
|
|
366
|
+
const blob = await response.blob();
|
|
367
|
+
const text = await blob.text();
|
|
278
368
|
if (text.includes('Course not configured')) {
|
|
279
369
|
setAlertMessage('Course not configured.');
|
|
280
370
|
setAlertOpen(true);
|
|
281
371
|
} else {
|
|
282
372
|
const filename = `${item.batchName}_Batch_Report.xlsx`;
|
|
283
|
-
const
|
|
373
|
+
const downloadBlob = new Blob([blob], {
|
|
284
374
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
285
375
|
});
|
|
286
|
-
const blobUrl = URL.createObjectURL(
|
|
376
|
+
const blobUrl = URL.createObjectURL(downloadBlob);
|
|
287
377
|
const link = document.createElement('a');
|
|
288
378
|
link.href = blobUrl;
|
|
289
379
|
link.setAttribute('download', filename);
|
|
@@ -294,33 +384,38 @@ function ExamPackReport() {
|
|
|
294
384
|
}
|
|
295
385
|
} catch (error) {
|
|
296
386
|
setAlertOpen(true);
|
|
297
|
-
setAlertMessage(error.
|
|
387
|
+
setAlertMessage(error.message || 'Error downloading report');
|
|
298
388
|
console.error('Error downloading course report:', error);
|
|
299
389
|
} finally {
|
|
300
390
|
setLoading(false);
|
|
301
391
|
}
|
|
302
392
|
};
|
|
393
|
+
|
|
394
|
+
// ❌ OLD - Commented (similar pattern for other download functions)
|
|
395
|
+
// const handleDownloadSingleUserReport = async (item) => { ... }
|
|
396
|
+
// const handleSummeryDownload = async () => { ... }
|
|
397
|
+
|
|
398
|
+
// ✅ NEW - handleDownloadSingleUserReport
|
|
303
399
|
const handleDownloadSingleUserReport = async item => {
|
|
304
400
|
setLoading(true);
|
|
305
401
|
try {
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
const response = await axios.get(url, {
|
|
309
|
-
responseType: 'blob',
|
|
402
|
+
const url = `${baseURL}/api/downloadExamPackReportByCourse?userId=${item.id}&corpCourseId=${selectedCourse.id}&subjectId=${selectedSubject.subjectId}`;
|
|
403
|
+
const response = await fetch(url, {
|
|
310
404
|
headers: {
|
|
311
|
-
|
|
405
|
+
'X-Tenant-Token': apiClient.tenantToken
|
|
312
406
|
}
|
|
313
407
|
});
|
|
314
|
-
const
|
|
408
|
+
const blob = await response.blob();
|
|
409
|
+
const text = await blob.text();
|
|
315
410
|
if (text.includes('Course not configured')) {
|
|
316
411
|
setAlertMessage('Course not configured.');
|
|
317
412
|
setAlertOpen(true);
|
|
318
413
|
} else {
|
|
319
414
|
const filename = `${item.userName}_Pack_Report.xlsx`;
|
|
320
|
-
const
|
|
415
|
+
const downloadBlob = new Blob([blob], {
|
|
321
416
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
322
417
|
});
|
|
323
|
-
const blobUrl = URL.createObjectURL(
|
|
418
|
+
const blobUrl = URL.createObjectURL(downloadBlob);
|
|
324
419
|
const link = document.createElement('a');
|
|
325
420
|
link.href = blobUrl;
|
|
326
421
|
link.setAttribute('download', filename);
|
|
@@ -331,36 +426,40 @@ function ExamPackReport() {
|
|
|
331
426
|
}
|
|
332
427
|
} catch (error) {
|
|
333
428
|
setAlertOpen(true);
|
|
334
|
-
setAlertMessage(error.
|
|
335
|
-
console.error('Error downloading
|
|
429
|
+
setAlertMessage(error.message || 'Error downloading report');
|
|
430
|
+
console.error('Error downloading user report:', error);
|
|
336
431
|
} finally {
|
|
337
432
|
setLoading(false);
|
|
338
433
|
}
|
|
339
434
|
};
|
|
435
|
+
|
|
436
|
+
// ✅ NEW - handleSummeryDownload
|
|
340
437
|
const handleSummeryDownload = async () => {
|
|
341
438
|
setMultiLoading(true);
|
|
342
439
|
const selectedRowIds = rows.filter(row => selectedUserList.includes(row.id)).map(row => row.id);
|
|
343
440
|
try {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
userIds: selectedRowIds
|
|
348
|
-
}, {
|
|
349
|
-
responseType: 'blob',
|
|
441
|
+
const url = `${baseURL}/api/downloadExamPacksAndUserIdsReport?subjectId=${selectedSubject.subjectId}&corpCourseId=${selectedCourse.id}`;
|
|
442
|
+
const response = await fetch(url, {
|
|
443
|
+
method: 'POST',
|
|
350
444
|
headers: {
|
|
351
|
-
|
|
352
|
-
|
|
445
|
+
'Content-Type': 'application/json',
|
|
446
|
+
'X-Tenant-Token': apiClient.tenantToken
|
|
447
|
+
},
|
|
448
|
+
body: JSON.stringify({
|
|
449
|
+
userIds: selectedRowIds
|
|
450
|
+
})
|
|
353
451
|
});
|
|
354
|
-
const
|
|
452
|
+
const blob = await response.blob();
|
|
453
|
+
const text = await blob.text();
|
|
355
454
|
if (text.includes('Course not configured')) {
|
|
356
455
|
setAlertMessage('Course not configured.');
|
|
357
456
|
setAlertOpen(true);
|
|
358
457
|
} else {
|
|
359
458
|
const filename = 'User_Exam_Pack_Report.xlsx';
|
|
360
|
-
const
|
|
459
|
+
const downloadBlob = new Blob([blob], {
|
|
361
460
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
362
461
|
});
|
|
363
|
-
const blobUrl = URL.createObjectURL(
|
|
462
|
+
const blobUrl = URL.createObjectURL(downloadBlob);
|
|
364
463
|
const link = document.createElement('a');
|
|
365
464
|
link.href = blobUrl;
|
|
366
465
|
link.setAttribute('download', filename);
|
|
@@ -371,8 +470,8 @@ function ExamPackReport() {
|
|
|
371
470
|
}
|
|
372
471
|
} catch (error) {
|
|
373
472
|
setAlertOpen(true);
|
|
374
|
-
setAlertMessage(error.
|
|
375
|
-
console.error('Error downloading
|
|
473
|
+
setAlertMessage(error.message || 'Error downloading report');
|
|
474
|
+
console.error('Error downloading summary report:', error);
|
|
376
475
|
} finally {
|
|
377
476
|
setMultiLoading(false);
|
|
378
477
|
}
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import axios from 'axios';
|
|
2
|
+
// import axios from 'axios'; // ❌ OLD - Commented
|
|
3
3
|
import { Autocomplete, FormControl, Button, Grid, Box, Typography, Paper, Snackbar, Alert, CircularProgress, TextField, Checkbox, FormControlLabel } from '@mui/material';
|
|
4
4
|
import { useNavigate } from 'react-router-dom';
|
|
5
5
|
import Header from '../components/Header';
|
|
6
6
|
import Sidebar from '../components/sidebar';
|
|
7
7
|
import '../App.css';
|
|
8
|
+
import { useTenant } from '../context/TenantProvider'; // ✅ NEW - Add this
|
|
8
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
10
|
function ExamReport() {
|
|
10
|
-
const [token, setToken] = useState(localStorage.getItem("token"));
|
|
11
|
+
// const [token, setToken] = useState(localStorage.getItem("token")); // ❌ OLD - Commented
|
|
12
|
+
const tenantToken = localStorage.getItem("tenantToken"); // ✅ NEW
|
|
11
13
|
const navigate = useNavigate();
|
|
14
|
+
const {
|
|
15
|
+
apiClient
|
|
16
|
+
} = useTenant(); // ✅ NEW - Add this
|
|
17
|
+
|
|
12
18
|
const [subjects, setSubjects] = useState([]);
|
|
13
19
|
const [examPacks, setExamPacks] = useState([]);
|
|
14
20
|
const [exams, setExams] = useState([]);
|
|
@@ -22,73 +28,109 @@ function ExamReport() {
|
|
|
22
28
|
const [loading, setLoading] = useState(false);
|
|
23
29
|
const baseURL = process.env.REACT_APP_BASE_URL;
|
|
24
30
|
useEffect(() => {
|
|
25
|
-
if (token == null) {
|
|
31
|
+
// if (token == null) { // ❌ OLD - Commented
|
|
32
|
+
if (tenantToken == null) {
|
|
33
|
+
// ✅ NEW
|
|
26
34
|
navigate("/login");
|
|
27
35
|
} else {
|
|
28
36
|
fetchAllSubjects();
|
|
29
37
|
}
|
|
30
38
|
}, []);
|
|
39
|
+
|
|
40
|
+
// ❌ OLD - Commented
|
|
41
|
+
// const fetchAllSubjects = async () => {
|
|
42
|
+
// try {
|
|
43
|
+
// const response = await axios({
|
|
44
|
+
// method: "get",
|
|
45
|
+
// url: `${baseURL}api/showAllSubjects`,
|
|
46
|
+
// headers: { Authorization: `Bearer ${token}` },
|
|
47
|
+
// });
|
|
48
|
+
// setSubjects(response.data);
|
|
49
|
+
// } catch (error) {
|
|
50
|
+
// console.error('Error fetching subjects:', error);
|
|
51
|
+
// }
|
|
52
|
+
// };
|
|
53
|
+
|
|
54
|
+
// ✅ NEW - Using apiClient
|
|
31
55
|
const fetchAllSubjects = async () => {
|
|
32
56
|
try {
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
url: `${baseURL}api/showAllSubjects`,
|
|
36
|
-
headers: {
|
|
37
|
-
Authorization: `Bearer ${token}`
|
|
38
|
-
}
|
|
39
|
-
});
|
|
40
|
-
setSubjects(response.data);
|
|
57
|
+
const data = await apiClient.get('/api/showAllSubjects');
|
|
58
|
+
setSubjects(data);
|
|
41
59
|
} catch (error) {
|
|
42
60
|
console.error('Error fetching subjects:', error);
|
|
43
61
|
}
|
|
44
62
|
};
|
|
63
|
+
|
|
64
|
+
// ❌ OLD - Commented
|
|
65
|
+
// const fetchExamPacks = async (subjectId) => {
|
|
66
|
+
// try {
|
|
67
|
+
// const response = await axios({
|
|
68
|
+
// method: "get",
|
|
69
|
+
// params: { subjectId },
|
|
70
|
+
// url: `${baseURL}api/getAllExamPacks`,
|
|
71
|
+
// headers: { Authorization: `Bearer ${token}` },
|
|
72
|
+
// });
|
|
73
|
+
// setExamPacks(response.data);
|
|
74
|
+
// } catch (error) {
|
|
75
|
+
// console.error('Error fetching exam packs:', error);
|
|
76
|
+
// }
|
|
77
|
+
// };
|
|
78
|
+
|
|
79
|
+
// ✅ NEW - Using apiClient
|
|
45
80
|
const fetchExamPacks = async subjectId => {
|
|
46
81
|
try {
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
params: {
|
|
50
|
-
subjectId
|
|
51
|
-
},
|
|
52
|
-
url: `${baseURL}api/getAllExamPacks`,
|
|
53
|
-
headers: {
|
|
54
|
-
Authorization: `Bearer ${token}`
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
setExamPacks(response.data);
|
|
82
|
+
const data = await apiClient.get(`/api/getAllExamPacks?subjectId=${subjectId}`);
|
|
83
|
+
setExamPacks(data);
|
|
58
84
|
} catch (error) {
|
|
59
85
|
console.error('Error fetching exam packs:', error);
|
|
60
86
|
}
|
|
61
87
|
};
|
|
88
|
+
|
|
89
|
+
// ❌ OLD - Commented
|
|
90
|
+
// const fetchExams = async (examPackId) => {
|
|
91
|
+
// try {
|
|
92
|
+
// const response = await axios({
|
|
93
|
+
// method: "get",
|
|
94
|
+
// params: { examPackId },
|
|
95
|
+
// url: `${baseURL}api/getAllExams`,
|
|
96
|
+
// headers: { Authorization: `Bearer ${token}` },
|
|
97
|
+
// });
|
|
98
|
+
// setExams(response.data);
|
|
99
|
+
// } catch (error) {
|
|
100
|
+
// console.error('Error fetching exams:', error);
|
|
101
|
+
// }
|
|
102
|
+
// };
|
|
103
|
+
|
|
104
|
+
// ✅ NEW - Using apiClient
|
|
62
105
|
const fetchExams = async examPackId => {
|
|
63
106
|
try {
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
params: {
|
|
67
|
-
examPackId
|
|
68
|
-
},
|
|
69
|
-
url: `${baseURL}api/getAllExams`,
|
|
70
|
-
headers: {
|
|
71
|
-
Authorization: `Bearer ${token}`
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
setExams(response.data);
|
|
107
|
+
const data = await apiClient.get(`/api/getAllExams?examPackId=${examPackId}`);
|
|
108
|
+
setExams(data);
|
|
75
109
|
} catch (error) {
|
|
76
110
|
console.error('Error fetching exams:', error);
|
|
77
111
|
}
|
|
78
112
|
};
|
|
113
|
+
|
|
114
|
+
// ❌ OLD - Commented
|
|
115
|
+
// const fetchUsers = async (examId) => {
|
|
116
|
+
// try {
|
|
117
|
+
// const response = await axios({
|
|
118
|
+
// method: "get",
|
|
119
|
+
// params: { examId },
|
|
120
|
+
// url: `${baseURL}api/getAllUsers`,
|
|
121
|
+
// headers: { Authorization: `Bearer ${token}` },
|
|
122
|
+
// });
|
|
123
|
+
// setUsers(response.data);
|
|
124
|
+
// } catch (error) {
|
|
125
|
+
// console.error('Error fetching users:', error);
|
|
126
|
+
// }
|
|
127
|
+
// };
|
|
128
|
+
|
|
129
|
+
// ✅ NEW - Using apiClient
|
|
79
130
|
const fetchUsers = async examId => {
|
|
80
131
|
try {
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
params: {
|
|
84
|
-
examId
|
|
85
|
-
},
|
|
86
|
-
url: `${baseURL}api/getAllUsers`,
|
|
87
|
-
headers: {
|
|
88
|
-
Authorization: `Bearer ${token}`
|
|
89
|
-
}
|
|
90
|
-
});
|
|
91
|
-
setUsers(response.data);
|
|
132
|
+
const data = await apiClient.get(`/api/getAllUsers?examId=${examId}`);
|
|
133
|
+
setUsers(data);
|
|
92
134
|
} catch (error) {
|
|
93
135
|
console.error('Error fetching users:', error);
|
|
94
136
|
}
|
|
@@ -126,19 +168,45 @@ function ExamReport() {
|
|
|
126
168
|
const handleUserChange = (event, newValue) => {
|
|
127
169
|
setSelectedUser(newValue);
|
|
128
170
|
};
|
|
171
|
+
|
|
172
|
+
// ❌ OLD - Commented
|
|
173
|
+
// const handleDownload = async () => {
|
|
174
|
+
// setLoading(true);
|
|
175
|
+
// try {
|
|
176
|
+
// const url = `${baseURL}api/generateReportForExam?subjectId=${selectedSubject.subjectId}&examPackId=${selectedExamPack.id}&examId=${selectedExam.id}`;
|
|
177
|
+
// const response = await axios.get(url, {
|
|
178
|
+
// responseType: 'blob',
|
|
179
|
+
// headers: {
|
|
180
|
+
// Authorization: `Bearer ${token}`
|
|
181
|
+
// }
|
|
182
|
+
// });
|
|
183
|
+
// const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
|
|
184
|
+
// const blobUrl = URL.createObjectURL(blob);
|
|
185
|
+
// const link = document.createElement('a');
|
|
186
|
+
// link.href = blobUrl;
|
|
187
|
+
// link.setAttribute('download', 'QuestionewiseExamReport.xlsx');
|
|
188
|
+
// document.body.appendChild(link);
|
|
189
|
+
// link.click();
|
|
190
|
+
// link.parentNode.removeChild(link);
|
|
191
|
+
// URL.revokeObjectURL(blobUrl);
|
|
192
|
+
// } catch (error) {
|
|
193
|
+
// console.error('Error downloading report:', error);
|
|
194
|
+
// } finally {
|
|
195
|
+
// setLoading(false);
|
|
196
|
+
// }
|
|
197
|
+
// };
|
|
198
|
+
|
|
199
|
+
// ✅ NEW - Using fetch with tenant token
|
|
129
200
|
const handleDownload = async () => {
|
|
130
201
|
setLoading(true);
|
|
131
202
|
try {
|
|
132
|
-
const url = `${baseURL}api/generateReportForExam?subjectId=${selectedSubject.subjectId}&examPackId=${selectedExamPack.id}&examId=${selectedExam.id}`;
|
|
133
|
-
const response = await
|
|
134
|
-
responseType: 'blob',
|
|
203
|
+
const url = `${baseURL}/api/generateReportForExam?subjectId=${selectedSubject.subjectId}&examPackId=${selectedExamPack.id}&examId=${selectedExam.id}`;
|
|
204
|
+
const response = await fetch(url, {
|
|
135
205
|
headers: {
|
|
136
|
-
|
|
206
|
+
'X-Tenant-Token': apiClient.tenantToken
|
|
137
207
|
}
|
|
138
208
|
});
|
|
139
|
-
const blob =
|
|
140
|
-
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
141
|
-
});
|
|
209
|
+
const blob = await response.blob();
|
|
142
210
|
const blobUrl = URL.createObjectURL(blob);
|
|
143
211
|
const link = document.createElement('a');
|
|
144
212
|
link.href = blobUrl;
|