@nualang/nualang-ui-components 0.1.1334 → 0.1.1336
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.
|
@@ -8,7 +8,7 @@ import ArrowForwardIcon from "@mui/icons-material/ArrowForward";
|
|
|
8
8
|
import Avatar from "@mui/material/Avatar";
|
|
9
9
|
import Box from "@mui/material/Box";
|
|
10
10
|
import Typography from "@mui/material/Typography";
|
|
11
|
-
import { TableSortLabel, Chip } from "@mui/material";
|
|
11
|
+
import { TableSortLabel, Chip, Grid } from "@mui/material";
|
|
12
12
|
import IconButton from "@mui/material/IconButton";
|
|
13
13
|
import Tooltip, { tooltipClasses } from "@mui/material/Tooltip";
|
|
14
14
|
import { green, lightGreen, red, grey, deepOrange, orange, yellow } from "@mui/material/colors";
|
|
@@ -17,7 +17,7 @@ import DefaultButton from "../../Misc/DefaultColourButton/DefaultColourButton";
|
|
|
17
17
|
import { formatMemberCourseCompletions, formatMemberAssignmentCompletions } from "./utils";
|
|
18
18
|
import DoneIcon from "@mui/icons-material/Done";
|
|
19
19
|
import CloseIcon from "@mui/icons-material/Close";
|
|
20
|
-
import RemoveIcon from
|
|
20
|
+
import RemoveIcon from "@mui/icons-material/Remove";
|
|
21
21
|
import HtmlTooltipComponent from "../../Misc/HtmlTooltipComponent/HtmlTooltipComponent";
|
|
22
22
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
23
23
|
const {
|
|
@@ -123,7 +123,8 @@ function DataCell({
|
|
|
123
123
|
backgroundColor,
|
|
124
124
|
memberActivityLink,
|
|
125
125
|
type,
|
|
126
|
-
index
|
|
126
|
+
index,
|
|
127
|
+
submittedLate = false
|
|
127
128
|
}) {
|
|
128
129
|
const completedTotal = `${completed} of ${total}`;
|
|
129
130
|
const [tooltipOpen, setTooltipOpen] = useState(false);
|
|
@@ -147,7 +148,7 @@ function DataCell({
|
|
|
147
148
|
backgroundColor: backgroundColor || theme.palette.divider,
|
|
148
149
|
color: theme.palette.common.white
|
|
149
150
|
}),
|
|
150
|
-
children: /*#__PURE__*/
|
|
151
|
+
children: /*#__PURE__*/_jsxs(HtmlTooltip, {
|
|
151
152
|
tabIndex: `${index + 1}`,
|
|
152
153
|
open: tooltipOpen,
|
|
153
154
|
disableHoverListener: !type,
|
|
@@ -182,7 +183,7 @@ function DataCell({
|
|
|
182
183
|
})]
|
|
183
184
|
}),
|
|
184
185
|
enterDelay: 1000,
|
|
185
|
-
children: /*#__PURE__*/_jsx(Typography, {
|
|
186
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
186
187
|
variant: "subtitle1",
|
|
187
188
|
component: "div",
|
|
188
189
|
sx: theme => ({
|
|
@@ -192,21 +193,50 @@ function DataCell({
|
|
|
192
193
|
textDecoration: "none"
|
|
193
194
|
}),
|
|
194
195
|
children: data
|
|
195
|
-
})
|
|
196
|
+
}), submittedLate && /*#__PURE__*/_jsx(Chip, {
|
|
197
|
+
variant: "outlined",
|
|
198
|
+
sx: theme => ({
|
|
199
|
+
color: theme.palette.common.black,
|
|
200
|
+
fontWeight: "bold"
|
|
201
|
+
}),
|
|
202
|
+
size: "small",
|
|
203
|
+
label: t("late_submission")
|
|
204
|
+
})]
|
|
196
205
|
})
|
|
197
206
|
});
|
|
198
207
|
}
|
|
199
208
|
function ExerciseCell({
|
|
200
209
|
icon,
|
|
201
|
-
backgroundColor
|
|
210
|
+
backgroundColor,
|
|
211
|
+
submittedLate = false,
|
|
212
|
+
daysLate = null,
|
|
213
|
+
t
|
|
202
214
|
}) {
|
|
203
|
-
return /*#__PURE__*/
|
|
215
|
+
return /*#__PURE__*/_jsxs(Box, {
|
|
204
216
|
component: "td",
|
|
205
217
|
sx: theme => ({
|
|
206
218
|
backgroundColor: backgroundColor || theme.palette.divider,
|
|
207
219
|
color: theme.palette.common.black
|
|
208
220
|
}),
|
|
209
|
-
children:
|
|
221
|
+
children: [/*#__PURE__*/_jsx(Grid, {
|
|
222
|
+
container: true
|
|
223
|
+
}), /*#__PURE__*/_jsx(Grid, {
|
|
224
|
+
size: 12,
|
|
225
|
+
children: icon
|
|
226
|
+
}), submittedLate && /*#__PURE__*/_jsx(Grid, {
|
|
227
|
+
size: 12,
|
|
228
|
+
children: /*#__PURE__*/_jsx(Chip, {
|
|
229
|
+
variant: "outlined",
|
|
230
|
+
sx: theme => ({
|
|
231
|
+
color: theme.palette.common.black,
|
|
232
|
+
fontWeight: "bold"
|
|
233
|
+
}),
|
|
234
|
+
size: "small",
|
|
235
|
+
label: t("days_late", {
|
|
236
|
+
count: daysLate
|
|
237
|
+
})
|
|
238
|
+
})
|
|
239
|
+
})]
|
|
210
240
|
});
|
|
211
241
|
}
|
|
212
242
|
function DataCellContainer({
|
|
@@ -216,7 +246,9 @@ function DataCellContainer({
|
|
|
216
246
|
error,
|
|
217
247
|
data,
|
|
218
248
|
memberActivityLink,
|
|
219
|
-
index
|
|
249
|
+
index,
|
|
250
|
+
submittedLate = false,
|
|
251
|
+
daysLate = null
|
|
220
252
|
}) {
|
|
221
253
|
if (isLoading) {
|
|
222
254
|
return /*#__PURE__*/_jsx(DataCell, {
|
|
@@ -245,7 +277,9 @@ function DataCellContainer({
|
|
|
245
277
|
total: data && data.total ? data.total : 0,
|
|
246
278
|
type: data && data.type ? data.type : "percent",
|
|
247
279
|
memberActivityLink: memberActivityLink,
|
|
248
|
-
index: index
|
|
280
|
+
index: index,
|
|
281
|
+
submittedLate: submittedLate,
|
|
282
|
+
daysLate: daysLate
|
|
249
283
|
});
|
|
250
284
|
} else if (data.percentage === -1 || data.total === 0 && data.type === "correct") {
|
|
251
285
|
return /*#__PURE__*/_jsx(DataCell, {
|
|
@@ -256,7 +290,9 @@ function DataCellContainer({
|
|
|
256
290
|
total: 0,
|
|
257
291
|
type: data && data.type ? data.type : "percent",
|
|
258
292
|
memberActivityLink: memberActivityLink,
|
|
259
|
-
index: index
|
|
293
|
+
index: index,
|
|
294
|
+
submittedLate: submittedLate,
|
|
295
|
+
daysLate: daysLate
|
|
260
296
|
});
|
|
261
297
|
} else if (data.percentage >= 80) {
|
|
262
298
|
return /*#__PURE__*/_jsx(DataCell, {
|
|
@@ -267,7 +303,9 @@ function DataCellContainer({
|
|
|
267
303
|
total: data.total,
|
|
268
304
|
type: data && data.type ? data.type : "percent",
|
|
269
305
|
memberActivityLink: memberActivityLink,
|
|
270
|
-
index: index
|
|
306
|
+
index: index,
|
|
307
|
+
submittedLate: submittedLate,
|
|
308
|
+
daysLate: daysLate
|
|
271
309
|
});
|
|
272
310
|
} else if (data.percentage >= 60) {
|
|
273
311
|
return /*#__PURE__*/_jsx(DataCell, {
|
|
@@ -278,7 +316,9 @@ function DataCellContainer({
|
|
|
278
316
|
total: data.total,
|
|
279
317
|
type: data && data.type ? data.type : "percent",
|
|
280
318
|
memberActivityLink: memberActivityLink,
|
|
281
|
-
index: index
|
|
319
|
+
index: index,
|
|
320
|
+
submittedLate: submittedLate,
|
|
321
|
+
daysLate: daysLate
|
|
282
322
|
});
|
|
283
323
|
} else if (data.percentage >= 40) {
|
|
284
324
|
return /*#__PURE__*/_jsx(DataCell, {
|
|
@@ -289,7 +329,9 @@ function DataCellContainer({
|
|
|
289
329
|
total: data.total,
|
|
290
330
|
type: data && data.type ? data.type : "percent",
|
|
291
331
|
memberActivityLink: memberActivityLink,
|
|
292
|
-
index: index
|
|
332
|
+
index: index,
|
|
333
|
+
submittedLate: submittedLate,
|
|
334
|
+
daysLate: daysLate
|
|
293
335
|
});
|
|
294
336
|
} else if (data.percentage >= 20) {
|
|
295
337
|
return /*#__PURE__*/_jsx(DataCell, {
|
|
@@ -300,7 +342,9 @@ function DataCellContainer({
|
|
|
300
342
|
total: data.total,
|
|
301
343
|
type: data && data.type ? data.type : "percent",
|
|
302
344
|
memberActivityLink: memberActivityLink,
|
|
303
|
-
index: index
|
|
345
|
+
index: index,
|
|
346
|
+
submittedLate: submittedLate,
|
|
347
|
+
daysLate: daysLate
|
|
304
348
|
});
|
|
305
349
|
} else if (data.percentage >= 0) {
|
|
306
350
|
return /*#__PURE__*/_jsx(DataCell, {
|
|
@@ -311,7 +355,9 @@ function DataCellContainer({
|
|
|
311
355
|
total: data.total,
|
|
312
356
|
type: data && data.type ? data.type : "percent",
|
|
313
357
|
memberActivityLink: memberActivityLink,
|
|
314
|
-
index: index
|
|
358
|
+
index: index,
|
|
359
|
+
submittedLate: submittedLate,
|
|
360
|
+
daysLate: daysLate
|
|
315
361
|
});
|
|
316
362
|
}
|
|
317
363
|
return /*#__PURE__*/_jsx(DataCell, {
|
|
@@ -322,7 +368,9 @@ function DataCellContainer({
|
|
|
322
368
|
total: data.total,
|
|
323
369
|
type: data && data.type ? data.type : "percent",
|
|
324
370
|
memberActivityLink: memberActivityLink,
|
|
325
|
-
index: index
|
|
371
|
+
index: index,
|
|
372
|
+
submittedLate: submittedLate,
|
|
373
|
+
daysLate: daysLate
|
|
326
374
|
});
|
|
327
375
|
}
|
|
328
376
|
function AssignmentCellData({
|
|
@@ -336,6 +384,12 @@ function AssignmentCellData({
|
|
|
336
384
|
index
|
|
337
385
|
}) {
|
|
338
386
|
let cellData = null;
|
|
387
|
+
let submittedLate = false;
|
|
388
|
+
let daysLate = null;
|
|
389
|
+
if (data?.submittedLate.isLate === true) {
|
|
390
|
+
submittedLate = true;
|
|
391
|
+
daysLate = data.submittedLate.daysLate;
|
|
392
|
+
}
|
|
339
393
|
if (data) {
|
|
340
394
|
if (filter === "percentage_complete" && data.percentComplete) {
|
|
341
395
|
cellData = data.percentComplete;
|
|
@@ -355,7 +409,9 @@ function AssignmentCellData({
|
|
|
355
409
|
error: error,
|
|
356
410
|
memberActivityLink: memberActivityLink,
|
|
357
411
|
index: index,
|
|
358
|
-
isNotAssigned: isNotAssigned
|
|
412
|
+
isNotAssigned: isNotAssigned,
|
|
413
|
+
submittedLate: submittedLate,
|
|
414
|
+
daysLate: daysLate
|
|
359
415
|
});
|
|
360
416
|
}
|
|
361
417
|
function CourseCellData({
|
|
@@ -430,6 +486,12 @@ function TopicCellData({
|
|
|
430
486
|
index
|
|
431
487
|
}) {
|
|
432
488
|
let cellData = null;
|
|
489
|
+
let submittedLate = false;
|
|
490
|
+
let daysLate = null;
|
|
491
|
+
if (data?.submittedLate.isLate === true) {
|
|
492
|
+
submittedLate = true;
|
|
493
|
+
daysLate = data.submittedLate.daysLate;
|
|
494
|
+
}
|
|
433
495
|
if (data) {
|
|
434
496
|
if (filter === "percentage_complete" && data.percentComplete) {
|
|
435
497
|
cellData = data.percentComplete;
|
|
@@ -448,7 +510,9 @@ function TopicCellData({
|
|
|
448
510
|
isLoading: isLoading,
|
|
449
511
|
error: error,
|
|
450
512
|
memberActivityLink: memberActivityLink,
|
|
451
|
-
index: index
|
|
513
|
+
index: index,
|
|
514
|
+
submittedLate: submittedLate,
|
|
515
|
+
daysLate: daysLate
|
|
452
516
|
});
|
|
453
517
|
}
|
|
454
518
|
function ExerciseCellData({
|
|
@@ -471,6 +535,12 @@ function ExerciseCellData({
|
|
|
471
535
|
let type = null;
|
|
472
536
|
let color;
|
|
473
537
|
let memberActivityLink = null;
|
|
538
|
+
let submittedLate = false;
|
|
539
|
+
let daysLate = null;
|
|
540
|
+
if (data?.submittedLate.isLate === true) {
|
|
541
|
+
submittedLate = true;
|
|
542
|
+
daysLate = data.submittedLate.daysLate;
|
|
543
|
+
}
|
|
474
544
|
if (isLoading) {
|
|
475
545
|
return /*#__PURE__*/_jsx(DataCell, {
|
|
476
546
|
t: t,
|
|
@@ -544,7 +614,9 @@ function ExerciseCellData({
|
|
|
544
614
|
type: type,
|
|
545
615
|
backgroundColor: color ?? (type === "completed" ? green[400] : red[400]),
|
|
546
616
|
memberActivityLink: memberActivityLink,
|
|
547
|
-
index: index
|
|
617
|
+
index: index,
|
|
618
|
+
submittedLate: submittedLate,
|
|
619
|
+
daysLate: daysLate
|
|
548
620
|
});
|
|
549
621
|
}
|
|
550
622
|
function RoleplayCellData({
|
|
@@ -627,8 +699,8 @@ function TableRow({
|
|
|
627
699
|
error,
|
|
628
700
|
isLoading
|
|
629
701
|
} = memberCourseCompletionsQuery;
|
|
630
|
-
const tableData = useMemo(() => memberCourseCompletionsQuery.isSuccess && Array.isArray(memberCourseCompletionsQuery.data.Items) && memberCourseCompletionsQuery.data.Items.length ? formatMemberCourseCompletions(courses, memberCourseCompletionsQuery.data.Items, reportType === "assignments" && selectedAssignment ? selectedAssignment?.exercises : null, isChallengeModeStudent) : [], [memberCourseCompletionsQuery.data, memberCourseCompletionsQuery.isSuccess, reportType, selectedAssignment, isChallengeModeStudent]);
|
|
631
|
-
const assignmentsTableData = useMemo(() => memberCourseCompletionsQuery.isSuccess && Array.isArray(memberCourseCompletionsQuery.data.Items) && memberCourseCompletionsQuery.data.Items.length && assignments && assignments.length ? formatMemberAssignmentCompletions(assignments, courses, memberCourseCompletionsQuery.data.Items, isChallengeModeStudent) : [], [memberCourseCompletionsQuery.data, memberCourseCompletionsQuery.isSuccess, assignments, isChallengeModeStudent]);
|
|
702
|
+
const tableData = useMemo(() => memberCourseCompletionsQuery.isSuccess && Array.isArray(memberCourseCompletionsQuery.data.Items) && memberCourseCompletionsQuery.data.Items.length ? formatMemberCourseCompletions(courses, memberCourseCompletionsQuery.data.Items, reportType === "assignments" && selectedAssignment ? selectedAssignment?.exercises : null, isChallengeModeStudent, selectedAssignment, reportType) : [], [memberCourseCompletionsQuery.data, memberCourseCompletionsQuery.isSuccess, courses, reportType, selectedAssignment, isChallengeModeStudent]);
|
|
703
|
+
const assignmentsTableData = useMemo(() => memberCourseCompletionsQuery.isSuccess && Array.isArray(memberCourseCompletionsQuery.data.Items) && memberCourseCompletionsQuery.data.Items.length && assignments && assignments.length ? formatMemberAssignmentCompletions(assignments, courses, memberCourseCompletionsQuery.data.Items, isChallengeModeStudent) : [], [memberCourseCompletionsQuery.data, memberCourseCompletionsQuery.isSuccess, courses, assignments, isChallengeModeStudent]);
|
|
632
704
|
let memberActivityLink = `/classrooms/${classroomId}/activity/member/${member.memberId}`;
|
|
633
705
|
if (currentView === "course") {
|
|
634
706
|
memberActivityLink = `${memberActivityLink}/${selectedCourse.courseId}`;
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
import { calcCompletions, calcPercentageCompletion, getScoreValues } from "../../utils";
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
// Helper function to calculate how many days late a submission is
|
|
4
|
+
const calculateDaysLate = (submissionTimestamp, dueDateMs) => {
|
|
5
|
+
const daysDifference = (submissionTimestamp - dueDateMs) / (1000 * 60 * 60 * 24);
|
|
6
|
+
if (daysDifference <= 0) return 0; // Not actually late
|
|
7
|
+
return Math.max(1, Math.ceil(daysDifference)); // Round up, minimum 1 day
|
|
8
|
+
};
|
|
9
|
+
export const formatMemberCourseCompletions = (courses = [], completions = [], assignmentExercises, isChallengeModeStudent = false, selectedAssignment = null, reportType = "course") => {
|
|
10
|
+
const dueDateMs = selectedAssignment?.dueDate ? new Date(selectedAssignment.dueDate).getTime() : null;
|
|
3
11
|
const initialStatsObject = courses.reduce((obj, v) => {
|
|
4
12
|
const scoreValues = getScoreValues("course", v.courseId, completions);
|
|
5
13
|
obj[v.courseId] = {
|
|
@@ -60,11 +68,26 @@ export const formatMemberCourseCompletions = (courses = [], completions = [], as
|
|
|
60
68
|
return topicObj;
|
|
61
69
|
}, {});
|
|
62
70
|
const formattedTopics = sectionVal.topics.reduce((topicObj, topicVal) => {
|
|
71
|
+
const topicCompletions = completions.filter(completion => completion.topicId === topicVal.topicId);
|
|
63
72
|
topicObj[topicVal.topicId] = {
|
|
64
73
|
percentComplete: topicVal.completion,
|
|
65
74
|
percentCorrect: topicVal.percentCorrect,
|
|
66
75
|
avgPronunciationScore: topicVal.avgPronunciationScore,
|
|
67
|
-
completions:
|
|
76
|
+
completions: topicCompletions,
|
|
77
|
+
...(selectedAssignment && reportType === "assignments" && {
|
|
78
|
+
submittedLate: (() => {
|
|
79
|
+
if (!dueDateMs) return {
|
|
80
|
+
isLate: false
|
|
81
|
+
};
|
|
82
|
+
const earliestCompletion = topicCompletions.reduce((earliest, c) => !earliest || c.createdAt < earliest.createdAt ? c : earliest, null);
|
|
83
|
+
return earliestCompletion && earliestCompletion.createdAt > dueDateMs ? {
|
|
84
|
+
isLate: true,
|
|
85
|
+
daysLate: calculateDaysLate(earliestCompletion.createdAt, dueDateMs)
|
|
86
|
+
} : {
|
|
87
|
+
isLate: false
|
|
88
|
+
};
|
|
89
|
+
})()
|
|
90
|
+
})
|
|
68
91
|
};
|
|
69
92
|
return topicObj;
|
|
70
93
|
}, initialTopicsObject);
|
|
@@ -72,11 +95,38 @@ export const formatMemberCourseCompletions = (courses = [], completions = [], as
|
|
|
72
95
|
percentComplete: sectionVal.completion,
|
|
73
96
|
percentCorrect: sectionVal.percentCorrect,
|
|
74
97
|
avgPronunciationScore: sectionVal.avgPronunciationScore,
|
|
75
|
-
topics: formattedTopics
|
|
98
|
+
topics: formattedTopics,
|
|
99
|
+
...(selectedAssignment && reportType === "assignments" && {
|
|
100
|
+
submittedLate: (() => {
|
|
101
|
+
const lateTopics = Object.values(formattedTopics).filter(t => t.submittedLate?.isLate);
|
|
102
|
+
if (lateTopics.length === 0) return {
|
|
103
|
+
isLate: false
|
|
104
|
+
};
|
|
105
|
+
const maxDaysLate = Math.max(...lateTopics.map(topic => topic.submittedLate.daysLate));
|
|
106
|
+
return {
|
|
107
|
+
isLate: true,
|
|
108
|
+
daysLate: maxDaysLate
|
|
109
|
+
};
|
|
110
|
+
})()
|
|
111
|
+
})
|
|
76
112
|
};
|
|
77
113
|
return sectionObj;
|
|
78
114
|
}, initialSectionsObject);
|
|
79
115
|
previousValue[currentValue.courseId].sections = formattedSections;
|
|
116
|
+
if (selectedAssignment && reportType === "assignments") {
|
|
117
|
+
const lateSections = Object.values(formattedSections).filter(s => s.submittedLate?.isLate);
|
|
118
|
+
if (lateSections.length === 0) {
|
|
119
|
+
previousValue[currentValue.courseId].submittedLate = {
|
|
120
|
+
isLate: false
|
|
121
|
+
};
|
|
122
|
+
} else {
|
|
123
|
+
const maxDaysLate = Math.max(...lateSections.map(section => section.submittedLate.daysLate));
|
|
124
|
+
previousValue[currentValue.courseId].submittedLate = {
|
|
125
|
+
isLate: true,
|
|
126
|
+
daysLate: maxDaysLate
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
}
|
|
80
130
|
return previousValue;
|
|
81
131
|
}, initialStatsObject);
|
|
82
132
|
return stats;
|
|
@@ -106,8 +156,21 @@ export const formatMemberAssignmentCompletions = (assignments = [], courses = []
|
|
|
106
156
|
total: 0,
|
|
107
157
|
sum: 0,
|
|
108
158
|
percentage: 0
|
|
159
|
+
},
|
|
160
|
+
submittedLate: {
|
|
161
|
+
isLate: false
|
|
109
162
|
}
|
|
110
163
|
};
|
|
164
|
+
if (assignment.dueDate) {
|
|
165
|
+
const dueDateMs = new Date(assignment.dueDate).getTime();
|
|
166
|
+
const lateCompletion = assignmentCompletions.find(completion => completion.createdAt > dueDateMs);
|
|
167
|
+
if (lateCompletion) {
|
|
168
|
+
accumulator.submittedLate = {
|
|
169
|
+
isLate: true,
|
|
170
|
+
daysLate: calculateDaysLate(lateCompletion.createdAt, dueDateMs)
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
111
174
|
Object.values(assignmentCourseCompletions).forEach(course => {
|
|
112
175
|
if (course?.percentComplete) {
|
|
113
176
|
accumulator.percentComplete.completed += course.percentComplete.completed || 0;
|