@nualang/nualang-ui-components 0.1.1380 → 0.1.1382
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/Assignments/AssignmentCourseSelection/AssignmentCourseSelection.js +10 -16
- package/dist/Assignments/AssignmentExerciseSelection/AssignmentExerciseSelection.js +3 -14
- package/dist/Lists/Exercises/Exercises.js +5 -5
- package/dist/Screens/Courses/ViewCourse/ViewTopic/ViewTopic.js +0 -2
- package/dist/Tables/Progress/Progress.js +11 -31
- package/dist/Tables/Progress/ProgressList.js +13 -13
- package/dist/Tables/Progress/ProgressTable.js +10 -4
- package/dist/Tables/Progress/ProgressTableRow.js +6 -3
- package/dist/Tables/Progress/cells/index.js +3 -2
- package/dist/Tables/Progress/useProgressUrlParams.js +3 -4
- package/dist/hooks/useExerciseState.js +1 -0
- package/dist/utils/index.js +52 -7
- package/package.json +3 -3
|
@@ -36,6 +36,8 @@ function Course({
|
|
|
36
36
|
isReadWriteModeStudent
|
|
37
37
|
}) {
|
|
38
38
|
const numOfIds = lastClickedExerciseId ? lastClickedExerciseId.split("|") : [];
|
|
39
|
+
const assignmentExercises = assignment?.exercises;
|
|
40
|
+
const filteredSelectedExercises = useMemo(() => (assignmentExercises ?? selectedExercises)?.filter(exercise => exercise?.roleplayId || exercise?.courseSectionTopicId?.split("|")[0] === course?.courseId), [assignmentExercises, selectedExercises, course?.courseId]);
|
|
39
41
|
const [open, setOpen] = useState(false);
|
|
40
42
|
useEffect(() => {
|
|
41
43
|
if (lastClickedExerciseId && (numOfIds.length === 4 && lastClickedExerciseId.endsWith(`|${assignment?.assignmentId}`) || numOfIds.length === 5 && lastClickedExerciseId.split("|")[3] === assignment?.assignmentId)) {
|
|
@@ -58,21 +60,13 @@ function Course({
|
|
|
58
60
|
enabled: !!course.courseId && isDialogOpen
|
|
59
61
|
});
|
|
60
62
|
useEffect(() => {
|
|
61
|
-
|
|
63
|
+
filteredSelectedExercises?.forEach(exercise => {
|
|
62
64
|
if (exercise.courseSectionTopicId) {
|
|
63
65
|
const sectionId = exercise.courseSectionTopicId.split("|")[1];
|
|
64
66
|
setSelectedSectionIds(prev => [...prev, sectionId]);
|
|
65
67
|
}
|
|
66
68
|
});
|
|
67
|
-
}, [
|
|
68
|
-
useEffect(() => {
|
|
69
|
-
selectedExercises?.forEach(exercise => {
|
|
70
|
-
if (exercise.courseSectionTopicId) {
|
|
71
|
-
const sectionId = exercise.courseSectionTopicId.split("|")[1];
|
|
72
|
-
setSelectedSectionIds(prev => [...prev, sectionId]);
|
|
73
|
-
}
|
|
74
|
-
});
|
|
75
|
-
}, [selectedExercises]);
|
|
69
|
+
}, [filteredSelectedExercises]);
|
|
76
70
|
const courseSections = useMemo(() => sectionsQuery.isSuccess && Array.isArray(sectionsQuery.data.Items) && sectionsQuery.data.Items.length ? sectionsQuery.data.Items : [], [sectionsQuery.data, sectionsQuery.isSuccess]);
|
|
77
71
|
const isCourseEmpty = courseSections.length === 0 && !sectionsQuery.isLoading ? true : false;
|
|
78
72
|
useEffect(() => {
|
|
@@ -82,18 +76,18 @@ function Course({
|
|
|
82
76
|
}, [courseSections, selectedSectionIds, viewOnly]);
|
|
83
77
|
useEffect(() => {
|
|
84
78
|
const assignedSectionIds = new Set();
|
|
85
|
-
|
|
79
|
+
assignmentExercises?.forEach(exercise => {
|
|
86
80
|
if (exercise.courseSectionTopicId) {
|
|
87
81
|
const sectionId = exercise.courseSectionTopicId.split("|")[1];
|
|
88
82
|
assignedSectionIds.add(sectionId);
|
|
89
83
|
}
|
|
90
84
|
});
|
|
91
|
-
if (courseSections && memberCourseCompletions &&
|
|
92
|
-
const sectionsWithProgress = calcCompletions(courseSections.filter(section => assignedSectionIds.has(section.sectionId)), memberCourseCompletions,
|
|
85
|
+
if (courseSections && memberCourseCompletions && filteredSelectedExercises) {
|
|
86
|
+
const sectionsWithProgress = calcCompletions(courseSections.filter(section => assignedSectionIds.has(section.sectionId)), memberCourseCompletions, filteredSelectedExercises, isChallengeModeStudent, isReadWriteModeStudent);
|
|
93
87
|
const percentCompletion = calcPercentageCompletion(sectionsWithProgress);
|
|
94
88
|
setCourseSectionCompletion(percentCompletion);
|
|
95
89
|
}
|
|
96
|
-
}, [courseSections, memberCourseCompletions,
|
|
90
|
+
}, [courseSections, memberCourseCompletions, filteredSelectedExercises, isChallengeModeStudent, isReadWriteModeStudent, assignmentExercises]);
|
|
97
91
|
return /*#__PURE__*/_jsx(_Fragment, {
|
|
98
92
|
children: useCase === "assignment-select" ? /*#__PURE__*/_jsxs(List, {
|
|
99
93
|
disablePadding: true,
|
|
@@ -167,7 +161,7 @@ function Course({
|
|
|
167
161
|
setIsExerciseSelected: setIsExerciseSelected,
|
|
168
162
|
getRoleplays: getRoleplays,
|
|
169
163
|
handleSelectExercise: handleSelectExercise,
|
|
170
|
-
selectedExercises:
|
|
164
|
+
selectedExercises: filteredSelectedExercises,
|
|
171
165
|
useCase: useCase,
|
|
172
166
|
assignment: assignment,
|
|
173
167
|
handleStartExercise: handleStartExercise,
|
|
@@ -255,7 +249,7 @@ function AssignmentCourseSelection({
|
|
|
255
249
|
isExerciseSelected: isExerciseSelected,
|
|
256
250
|
setIsExerciseSelected: setIsExerciseSelected,
|
|
257
251
|
handleSelectExercise: handleSelectExercise,
|
|
258
|
-
selectedExercises:
|
|
252
|
+
selectedExercises: selectedExercises,
|
|
259
253
|
useCase: useCase,
|
|
260
254
|
assignment: assignment,
|
|
261
255
|
handleStartExercise: handleStartExercise,
|
|
@@ -13,7 +13,7 @@ import OndemandVideoIcon from "@mui/icons-material/OndemandVideo";
|
|
|
13
13
|
import AssignmentRoleplaySelection from "../AssignmentRoleplaySelection/AssignmentRoleplaySelection";
|
|
14
14
|
import { CardInfoSectionItem } from "../../Cards/CardElements";
|
|
15
15
|
import { getCourseSectionTopicProgress } from "@nualang/nualang-api-and-queries/APIs/Progress";
|
|
16
|
-
import { progress as progressQuerys
|
|
16
|
+
import { progress as progressQuerys } from "@nualang/nualang-api-and-queries/Queries";
|
|
17
17
|
import { useNavigate, useParams } from "react-router";
|
|
18
18
|
import { ProgressBadge, InProgressBadge } from "../../Lists/Exercises/Exercises";
|
|
19
19
|
import ColorLinearProgress from "../../Misc/ColorLinearProgress/ColorLinearProgress";
|
|
@@ -384,25 +384,14 @@ function Topic({
|
|
|
384
384
|
handleRemoveExercise,
|
|
385
385
|
isPreview,
|
|
386
386
|
isChallengeModeStudent,
|
|
387
|
+
phraseLists: rawPhraseLists = [],
|
|
387
388
|
...otherProps
|
|
388
389
|
}) {
|
|
389
390
|
const theme = useTheme();
|
|
390
391
|
const isLargeScreen = useMediaQuery(theme.breakpoints.up("sm"));
|
|
391
392
|
const topicRef = useRef(null);
|
|
392
393
|
const numOfIds = lastClickedExerciseId ? lastClickedExerciseId.split("|") : [];
|
|
393
|
-
const
|
|
394
|
-
courseId,
|
|
395
|
-
sectionId,
|
|
396
|
-
topicId,
|
|
397
|
-
filters: {
|
|
398
|
-
includePhrases: "true"
|
|
399
|
-
}
|
|
400
|
-
}, {
|
|
401
|
-
enabled: !!courseId && !!sectionId && !!topicId
|
|
402
|
-
});
|
|
403
|
-
const phraseLists = phraseListsQuery.isSuccess && phraseListsQuery.data && Array.isArray(phraseListsQuery.data.Items) ? phraseListsQuery.data.Items.filter(pl => pl.phrases && pl.phrases.length > 0) : [];
|
|
404
|
-
|
|
405
|
-
// Define a single variable for clarity
|
|
394
|
+
const phraseLists = rawPhraseLists.filter(pl => pl.phrases && pl.phrases.length > 0);
|
|
406
395
|
const matchesLastClickedExercise = lastClickedExerciseId && (numOfIds.length === 4 && lastClickedExerciseId.endsWith(`|${assignment.assignmentId}`) && numOfIds[2] === topicId || numOfIds.length === 5 && numOfIds[3] === assignment.assignmentId && numOfIds[2] === topicId);
|
|
407
396
|
const [isExerciseListOpen, setExerciseListOpen] = useState(false);
|
|
408
397
|
useEffect(() => {
|
|
@@ -934,7 +934,6 @@ function Exercises(props) {
|
|
|
934
934
|
t = text => text,
|
|
935
935
|
isLoading,
|
|
936
936
|
phrases = [],
|
|
937
|
-
phraseLists = [],
|
|
938
937
|
completions = [],
|
|
939
938
|
roleplays = [],
|
|
940
939
|
bots = [],
|
|
@@ -974,7 +973,8 @@ function Exercises(props) {
|
|
|
974
973
|
updatePhraseLists,
|
|
975
974
|
courseSettings,
|
|
976
975
|
parentClassroom,
|
|
977
|
-
isChallengeModeStudent
|
|
976
|
+
isChallengeModeStudent,
|
|
977
|
+
topicInfo
|
|
978
978
|
} = props;
|
|
979
979
|
const params = useParams();
|
|
980
980
|
const {
|
|
@@ -1033,10 +1033,10 @@ function Exercises(props) {
|
|
|
1033
1033
|
// ── phraseLists drag state ──
|
|
1034
1034
|
const [phraseListsArray, setPhraseListsArray] = useState([]);
|
|
1035
1035
|
useEffect(() => {
|
|
1036
|
-
if (phraseLists && Array.isArray(phraseLists)) {
|
|
1037
|
-
setPhraseListsArray([...phraseLists].sort((a, b) => (a.idx || 0) - (b.idx || 0)));
|
|
1036
|
+
if (topicInfo?.phraseLists && Array.isArray(topicInfo.phraseLists)) {
|
|
1037
|
+
setPhraseListsArray([...topicInfo.phraseLists].sort((a, b) => (a.idx || 0) - (b.idx || 0)));
|
|
1038
1038
|
}
|
|
1039
|
-
}, [JSON.stringify(phraseLists)]);
|
|
1039
|
+
}, [JSON.stringify(topicInfo?.phraseLists)]);
|
|
1040
1040
|
const onDropPhraseList = () => {
|
|
1041
1041
|
if (updatePhraseLists) updatePhraseLists(phraseListsArray);
|
|
1042
1042
|
};
|
|
@@ -430,7 +430,6 @@ function Topic({
|
|
|
430
430
|
isClassroomCreator,
|
|
431
431
|
isVideoChatEnabled,
|
|
432
432
|
isVideoChatEnabledInSettings,
|
|
433
|
-
phraseLists = [],
|
|
434
433
|
handleCreatePhraseList,
|
|
435
434
|
handleUpdatePhraseList,
|
|
436
435
|
handleDeletePhraseList,
|
|
@@ -718,7 +717,6 @@ function Topic({
|
|
|
718
717
|
isMember: isMember,
|
|
719
718
|
isChallengeModeStudent: isChallengeModeStudent,
|
|
720
719
|
phrases: phrases,
|
|
721
|
-
phraseLists: phraseLists,
|
|
722
720
|
completions: completions,
|
|
723
721
|
roleplays: roleplays,
|
|
724
722
|
bots: bots,
|
|
@@ -28,7 +28,6 @@ function Progress({
|
|
|
28
28
|
memberId,
|
|
29
29
|
username,
|
|
30
30
|
getCourseSections = () => {},
|
|
31
|
-
getCourseSectionTopicPhraseLists = () => {},
|
|
32
31
|
submissions,
|
|
33
32
|
courseIds,
|
|
34
33
|
featureFlags,
|
|
@@ -43,8 +42,7 @@ function Progress({
|
|
|
43
42
|
const isSmallScreen = useMediaQuery(theme.breakpoints.down("sm"));
|
|
44
43
|
const {
|
|
45
44
|
useQueryClient,
|
|
46
|
-
useQueries
|
|
47
|
-
useQuery
|
|
45
|
+
useQueries
|
|
48
46
|
} = ReactQuery;
|
|
49
47
|
const queryClient = useQueryClient();
|
|
50
48
|
const [selectedAssignment, setSelectedAssignment] = useState(null);
|
|
@@ -55,35 +53,11 @@ function Progress({
|
|
|
55
53
|
const [selectedPhraseList, setSelectedPhraseList] = useState(null);
|
|
56
54
|
const [roleplays, setRoleplays] = useState([]);
|
|
57
55
|
const [bots, setBots] = useState([]);
|
|
58
|
-
const [phraseLists, setPhraseLists] = useState([]);
|
|
59
56
|
const reportFilters = ["percentage_complete", "correct_answer_percentage", "avg_pronunciation_score"];
|
|
60
57
|
const [filter, setFilter] = useState(reportFilters[0]);
|
|
61
58
|
const reportTypes = useMemo(() => isVideoChatEnabled ? ["courses", "assignments", "discuss"] : ["courses", "assignments"], [isVideoChatEnabled]);
|
|
62
59
|
const availableReportTypes = useMemo(() => memberId ? ["courses", "assignments"] : reportTypes, [memberId, reportTypes]);
|
|
63
60
|
const [reportType, setReportType] = useState(availableReportTypes[0]);
|
|
64
|
-
useEffect(() => {
|
|
65
|
-
if (selectedTopic) {
|
|
66
|
-
setRoleplays(selectedTopic?.roleplays);
|
|
67
|
-
setBots(selectedTopic?.bots);
|
|
68
|
-
} else {
|
|
69
|
-
setSelectedPhraseList(null);
|
|
70
|
-
setPhraseLists([]);
|
|
71
|
-
}
|
|
72
|
-
}, [selectedTopic]);
|
|
73
|
-
const selectedTopicCoords = selectedTopic ? [...selectedTopic.courseSectionId.split("|"), selectedTopic.topicId] : [];
|
|
74
|
-
const topicPhraseListsQuery = useQuery({
|
|
75
|
-
queryKey: ["courseSectionTopicPhraseLists", ...selectedTopicCoords],
|
|
76
|
-
queryFn: () => {
|
|
77
|
-
const [courseId, sectionId] = selectedTopic.courseSectionId.split("|");
|
|
78
|
-
return getCourseSectionTopicPhraseLists(courseId, sectionId, selectedTopic.topicId);
|
|
79
|
-
},
|
|
80
|
-
enabled: !!selectedTopic
|
|
81
|
-
});
|
|
82
|
-
useEffect(() => {
|
|
83
|
-
if (topicPhraseListsQuery.isSuccess && topicPhraseListsQuery.data?.Items) {
|
|
84
|
-
setPhraseLists(topicPhraseListsQuery.data.Items);
|
|
85
|
-
}
|
|
86
|
-
}, [topicPhraseListsQuery.data, topicPhraseListsQuery.isSuccess]);
|
|
87
61
|
const currentCourses = reportType === "assignments" ? assignedCourses : courses;
|
|
88
62
|
const courseSectionsQueries = useQueries({
|
|
89
63
|
queries: (currentCourses || []).map(({
|
|
@@ -112,6 +86,15 @@ function Progress({
|
|
|
112
86
|
return [];
|
|
113
87
|
}
|
|
114
88
|
}, [currentCourses, courseSectionsQueriesIsLoading]);
|
|
89
|
+
useEffect(() => {
|
|
90
|
+
if (selectedTopic) {
|
|
91
|
+
setSelectedPhraseList(null);
|
|
92
|
+
setRoleplays(selectedTopic?.roleplays);
|
|
93
|
+
setBots(selectedTopic?.bots);
|
|
94
|
+
} else {
|
|
95
|
+
setSelectedPhraseList(null);
|
|
96
|
+
}
|
|
97
|
+
}, [selectedTopic, coursesWithSections]);
|
|
115
98
|
const deleteCourseId = () => {
|
|
116
99
|
if ("URLSearchParams" in window) {
|
|
117
100
|
const searchParams = searchStringToObj(window.location.search);
|
|
@@ -147,8 +130,7 @@ function Progress({
|
|
|
147
130
|
selectedRoleplay,
|
|
148
131
|
setSelectedRoleplay,
|
|
149
132
|
selectedPhraseList,
|
|
150
|
-
setSelectedPhraseList
|
|
151
|
-
phraseLists
|
|
133
|
+
setSelectedPhraseList
|
|
152
134
|
});
|
|
153
135
|
const [selectedMembers, setSelectedMembers] = useState([]);
|
|
154
136
|
const assignedStudents = assignmentMembersById && assignmentMembersById[selectedAssignment?.assignmentId] || [];
|
|
@@ -417,7 +399,6 @@ function Progress({
|
|
|
417
399
|
setSelectedRoleplay: setSelectedRoleplay,
|
|
418
400
|
selectedPhraseList: selectedPhraseList,
|
|
419
401
|
setSelectedPhraseList: setSelectedPhraseList,
|
|
420
|
-
phraseLists: phraseLists,
|
|
421
402
|
selectedAssignment: selectedAssignment,
|
|
422
403
|
setSelectedAssignment: setSelectedAssignment,
|
|
423
404
|
assignments: assignments,
|
|
@@ -497,7 +478,6 @@ function Progress({
|
|
|
497
478
|
setSelectedRoleplay: setSelectedRoleplay,
|
|
498
479
|
selectedPhraseList: selectedPhraseList,
|
|
499
480
|
setSelectedPhraseList: setSelectedPhraseList,
|
|
500
|
-
phraseLists: phraseLists,
|
|
501
481
|
selectedAssignment: selectedAssignment,
|
|
502
482
|
setSelectedAssignment: setSelectedAssignment,
|
|
503
483
|
assignments: assignments,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
1
|
+
import { useState, useMemo } from "react";
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
3
|
import { Link as RouterLink } from "react-router";
|
|
4
4
|
import { ReactQuery } from "@nualang/nualang-api-and-queries/Queries";
|
|
@@ -450,26 +450,26 @@ function MemberListItem({
|
|
|
450
450
|
bots,
|
|
451
451
|
selectedRoleplay,
|
|
452
452
|
setSelectedRoleplay,
|
|
453
|
-
phraseLists,
|
|
454
453
|
selectedPhraseList,
|
|
455
454
|
setSelectedPhraseList
|
|
456
455
|
}) {
|
|
456
|
+
const phraseLists = selectedTopic?.phraseLists ?? [];
|
|
457
457
|
const {
|
|
458
458
|
memberId
|
|
459
459
|
} = member;
|
|
460
460
|
const courseIds = courses.map(c => c.courseId).toString();
|
|
461
|
-
const {
|
|
462
|
-
data = {},
|
|
463
|
-
error,
|
|
464
|
-
isLoading
|
|
465
|
-
} = useQuery({
|
|
461
|
+
const memberCourseCompletionsQuery = useQuery({
|
|
466
462
|
queryKey: ["memberCourseProgress", memberId, courseIds],
|
|
467
463
|
queryFn: async () => {
|
|
468
|
-
|
|
469
|
-
return formatMemberCourseCompletions(courses, memberCourseCompletions.Items);
|
|
464
|
+
return fetchMemberCourseCompletions(memberId, courseIds);
|
|
470
465
|
},
|
|
471
466
|
staleTime: 500000
|
|
472
467
|
});
|
|
468
|
+
const {
|
|
469
|
+
error,
|
|
470
|
+
isLoading
|
|
471
|
+
} = memberCourseCompletionsQuery;
|
|
472
|
+
const data = useMemo(() => memberCourseCompletionsQuery.isSuccess && Array.isArray(memberCourseCompletionsQuery.data?.Items) ? formatMemberCourseCompletions(courses, memberCourseCompletionsQuery.data.Items, null, false, null, "course", false) : {}, [memberCourseCompletionsQuery.data, memberCourseCompletionsQuery.isSuccess, courses]);
|
|
473
473
|
let memberActivityLink = `/classrooms/${classroomId}/activity/member/${member.memberId}`;
|
|
474
474
|
if (currentView === "course") {
|
|
475
475
|
memberActivityLink = `${memberActivityLink}/${selectedCourse.courseId}`;
|
|
@@ -482,7 +482,8 @@ function MemberListItem({
|
|
|
482
482
|
const handleClick = () => {
|
|
483
483
|
setOpen(!open);
|
|
484
484
|
};
|
|
485
|
-
const
|
|
485
|
+
const hasMeaningForSelectedPhraseList = selectedPhraseList?.phrases?.some(p => p.definitions?.length > 0) && !selectedPhraseList?.hiddenPhraseGroupGames?.meaning;
|
|
486
|
+
const exercisesArray = [...exercises, ...(hasMeaningForSelectedPhraseList ? ["meaning"] : [])];
|
|
486
487
|
return /*#__PURE__*/_jsxs(_Fragment, {
|
|
487
488
|
children: [/*#__PURE__*/_jsxs(ListItem, {
|
|
488
489
|
secondaryAction: /*#__PURE__*/_jsx(IconButton, {
|
|
@@ -563,7 +564,8 @@ function MemberListItem({
|
|
|
563
564
|
}, `student-progress-data-cell-${i}`);
|
|
564
565
|
}), currentView === "topic" && phraseLists.map((phraseList, i) => {
|
|
565
566
|
const topicData = getTopicData(data, selectedTopic, selectedCourse, selectedSection);
|
|
566
|
-
const
|
|
567
|
+
const hasMeaning = phraseList?.phrases?.some(p => p.definitions?.length > 0) && !phraseList?.hiddenPhraseGroupGames?.meaning;
|
|
568
|
+
const phraseListExercises = ["translation", "listening", "pronunciation", ...(hasMeaning ? ["meaning"] : [])];
|
|
567
569
|
const completedCount = phraseListExercises.filter(exercise => topicData?.completions?.some(c => c.exercise === exercise && (c.phraseListId === phraseList.phraseListId || !c.phraseListId && phraseList.phraseListId?.startsWith('leg-')))).length;
|
|
568
570
|
return /*#__PURE__*/_jsx(MenuItem, {
|
|
569
571
|
children: /*#__PURE__*/_jsx(ListItem, {
|
|
@@ -655,7 +657,6 @@ function ProgressList({
|
|
|
655
657
|
setSelectedRoleplay,
|
|
656
658
|
selectedPhraseList,
|
|
657
659
|
setSelectedPhraseList,
|
|
658
|
-
phraseLists = [],
|
|
659
660
|
roleplays,
|
|
660
661
|
bots,
|
|
661
662
|
deleteCourseId
|
|
@@ -900,7 +901,6 @@ function ProgressList({
|
|
|
900
901
|
bots: bots,
|
|
901
902
|
selectedRoleplay: selectedRoleplay,
|
|
902
903
|
setSelectedRoleplay: setSelectedRoleplay,
|
|
903
|
-
phraseLists: phraseLists,
|
|
904
904
|
selectedPhraseList: selectedPhraseList,
|
|
905
905
|
setSelectedPhraseList: setSelectedPhraseList
|
|
906
906
|
}, `student-progress-list-item-${i}`))
|
|
@@ -48,10 +48,10 @@ function ProgressTable({
|
|
|
48
48
|
featureFlags,
|
|
49
49
|
reportType,
|
|
50
50
|
assignedCourseIds,
|
|
51
|
-
phraseLists = [],
|
|
52
51
|
selectedPhraseList,
|
|
53
52
|
setSelectedPhraseList
|
|
54
53
|
}) {
|
|
54
|
+
const phraseLists = selectedTopic?.phraseLists ?? [];
|
|
55
55
|
const selectedAssignmentTopics = (selectedAssignment?.exercises || [])?.map(e => e.courseSectionTopicId);
|
|
56
56
|
const currentCourseIds = reportType === "assignments" ? assignedCourseIds : courseIds;
|
|
57
57
|
const assignmentTopics = courses.flatMap(c => c.sections.flatMap(s => s.topics)).filter(t => selectedAssignmentTopics?.includes(`${t.courseSectionId}|${t.topicId}`));
|
|
@@ -62,6 +62,11 @@ function ProgressTable({
|
|
|
62
62
|
const tableRoleplays = assignmentExercises && assignmentExercises.length ? roleplays.filter(r => assignmentExercises.some(ae => ae.roleplayId === r.roleplayId)) : roleplays;
|
|
63
63
|
const tableRoleplayGames = assignmentExercises && assignmentExercises.length && selectedRoleplay ? roleplayGames.filter(rg => assignmentExercises.some(ae => ae.roleplayId === selectedRoleplay.roleplayId && ae.name === `roleplay-${rg}`)) : roleplayGames;
|
|
64
64
|
const tableBots = assignmentExercises && assignmentExercises.length ? bots.filter(b => assignmentExercises.some(ae => ae.botId === b.botId)) : bots;
|
|
65
|
+
const tablePhraseLists = assignmentExercises && assignmentExercises.length ? phraseLists.filter(pl => assignmentExercises.some(ae => ae.phraseListId === pl.phraseListId)) : phraseLists;
|
|
66
|
+
const hasMeaningForSelectedPhraseList = selectedPhraseList?.phrases?.some(p => p.definitions?.length > 0) && !selectedPhraseList?.hiddenPhraseGroupGames?.meaning;
|
|
67
|
+
const allPhraseListExercises = ["translation", "listening", "pronunciation", ...(hasMeaningForSelectedPhraseList ? ["meaning"] : [])];
|
|
68
|
+
const assignmentPhraseListExercises = reportType === "assignments" && selectedPhraseList ? (selectedAssignment?.exercises || []).filter(e => e.phraseListId === selectedPhraseList.phraseListId) : [];
|
|
69
|
+
const tablePhraseListExercises = assignmentPhraseListExercises.length ? allPhraseListExercises.filter(e => assignmentPhraseListExercises.some(ae => ae.name === e)) : allPhraseListExercises;
|
|
65
70
|
const [sortDirection, setSortDirection] = useState("asc");
|
|
66
71
|
const [sortColumn, setSortColumn] = useState("student");
|
|
67
72
|
const [membersState, setMembersState] = useState([]);
|
|
@@ -695,7 +700,7 @@ function ProgressTable({
|
|
|
695
700
|
},
|
|
696
701
|
children: t("view_topic")
|
|
697
702
|
})]
|
|
698
|
-
}, i)), (currentView === "topic" || currentView === "assignment-topic") &&
|
|
703
|
+
}, i)), (currentView === "topic" || currentView === "assignment-topic") && tablePhraseLists.map((phraseList, i) => /*#__PURE__*/_jsxs("th", {
|
|
699
704
|
scope: "col",
|
|
700
705
|
children: [/*#__PURE__*/_jsx(Typography, {
|
|
701
706
|
variant: "subtitle1",
|
|
@@ -716,7 +721,7 @@ function ProgressTable({
|
|
|
716
721
|
},
|
|
717
722
|
children: t("view_phrase_list")
|
|
718
723
|
})]
|
|
719
|
-
}, i)), (currentView === "phrase-list" || currentView === "assignment-phrase-list") &&
|
|
724
|
+
}, i)), (currentView === "phrase-list" || currentView === "assignment-phrase-list") && tablePhraseListExercises.map((exercise, i) => /*#__PURE__*/_jsx("th", {
|
|
720
725
|
scope: "col",
|
|
721
726
|
children: /*#__PURE__*/_jsx(Typography, {
|
|
722
727
|
variant: "subtitle1",
|
|
@@ -851,7 +856,7 @@ function ProgressTable({
|
|
|
851
856
|
selectedTopic: selectedTopic,
|
|
852
857
|
selectedRoleplay: selectedRoleplay,
|
|
853
858
|
selectedPhraseList: selectedPhraseList,
|
|
854
|
-
phraseLists:
|
|
859
|
+
phraseLists: tablePhraseLists,
|
|
855
860
|
selectedAssignment: selectedAssignment,
|
|
856
861
|
fetchMemberCourseCompletions: fetchMemberCourseCompletions,
|
|
857
862
|
currentView: currentView,
|
|
@@ -860,6 +865,7 @@ function ProgressTable({
|
|
|
860
865
|
featureFlags: featureFlags,
|
|
861
866
|
reportType: reportType,
|
|
862
867
|
tableExercises: tableExercises,
|
|
868
|
+
tablePhraseListExercises: tablePhraseListExercises,
|
|
863
869
|
tableRoleplays: tableRoleplays,
|
|
864
870
|
tableRoleplayGames: tableRoleplayGames,
|
|
865
871
|
tableBots: tableBots,
|
|
@@ -33,6 +33,7 @@ export default function ProgressTableRow(props) {
|
|
|
33
33
|
featureFlags,
|
|
34
34
|
reportType,
|
|
35
35
|
tableExercises = [],
|
|
36
|
+
tablePhraseListExercises = [],
|
|
36
37
|
tableRoleplays = [],
|
|
37
38
|
tableBots = [],
|
|
38
39
|
tableRoleplayGames = [],
|
|
@@ -49,7 +50,7 @@ export default function ProgressTableRow(props) {
|
|
|
49
50
|
} = member;
|
|
50
51
|
const isChallengeModeStudent = assignedLabels?.isChallengeModeStudent;
|
|
51
52
|
const isReadWriteModeStudent = assignedLabels?.isReadWriteModeStudent;
|
|
52
|
-
const assignmentDueDateMs = selectedAssignment?.dueDate ? new Date(selectedAssignment.dueDate).getTime() : null;
|
|
53
|
+
const assignmentDueDateMs = reportType === "assignments" && selectedAssignment?.dueDate ? new Date(selectedAssignment.dueDate).getTime() : null;
|
|
53
54
|
const memberCourseCompletionsQuery = useQuery({
|
|
54
55
|
queryKey: ["memberCourseProgress", memberId, courseIds],
|
|
55
56
|
queryFn: async () => {
|
|
@@ -201,6 +202,7 @@ export default function ProgressTableRow(props) {
|
|
|
201
202
|
index: i
|
|
202
203
|
}, `topic-cell-${i}`)), (currentView === "topic" || currentView === "assignment-topic") && phraseLists.map((phraseList, i) => {
|
|
203
204
|
const topicData = getTopicData(data, selectedTopic, selectedCourse, selectedSection);
|
|
205
|
+
const hasMeaning = phraseList?.phrases?.some(p => p.definitions?.length > 0) && !phraseList?.hiddenPhraseGroupGames?.meaning;
|
|
204
206
|
return /*#__PURE__*/_jsx(PhraseListCellData, {
|
|
205
207
|
t: t,
|
|
206
208
|
data: topicData,
|
|
@@ -208,9 +210,10 @@ export default function ProgressTableRow(props) {
|
|
|
208
210
|
isLoading: isLoading,
|
|
209
211
|
error: error,
|
|
210
212
|
index: i,
|
|
211
|
-
assignmentDueDateMs: assignmentDueDateMs
|
|
213
|
+
assignmentDueDateMs: assignmentDueDateMs,
|
|
214
|
+
hasMeaning: hasMeaning
|
|
212
215
|
}, `phrase-list-cell-${i}`);
|
|
213
|
-
}), (currentView === "phrase-list" || currentView === "assignment-phrase-list") &&
|
|
216
|
+
}), (currentView === "phrase-list" || currentView === "assignment-phrase-list") && tablePhraseListExercises.map((exercise, i) => {
|
|
214
217
|
const topicData = getTopicData(data, selectedTopic, selectedCourse, selectedSection);
|
|
215
218
|
return /*#__PURE__*/_jsx(ExerciseCellData, {
|
|
216
219
|
t: t,
|
|
@@ -485,7 +485,8 @@ export function PhraseListCellData({
|
|
|
485
485
|
isLoading,
|
|
486
486
|
error,
|
|
487
487
|
index,
|
|
488
|
-
assignmentDueDateMs = null
|
|
488
|
+
assignmentDueDateMs = null,
|
|
489
|
+
hasMeaning = false
|
|
489
490
|
}) {
|
|
490
491
|
if (isLoading) return /*#__PURE__*/_jsx(DataCell, {
|
|
491
492
|
t: t,
|
|
@@ -497,7 +498,7 @@ export function PhraseListCellData({
|
|
|
497
498
|
data: "A problem has occurred.",
|
|
498
499
|
index: index
|
|
499
500
|
});
|
|
500
|
-
const phraseListExercises = ["translation", "listening", "pronunciation"];
|
|
501
|
+
const phraseListExercises = ["translation", "listening", "pronunciation", ...(hasMeaning ? ["meaning"] : [])];
|
|
501
502
|
const phraseListCompletions = data?.completions?.filter(c => phraseListExercises.includes(c.exercise) && (c.phraseListId === phraseListId || !c.phraseListId && phraseListId?.startsWith('leg-'))) ?? [];
|
|
502
503
|
const completedCount = phraseListExercises.filter(exercise => phraseListCompletions.some(c => c.exercise === exercise)).length;
|
|
503
504
|
const total = phraseListExercises.length;
|
|
@@ -43,8 +43,7 @@ export default function useProgressUrlParams(opts) {
|
|
|
43
43
|
selectedRoleplay,
|
|
44
44
|
setSelectedRoleplay,
|
|
45
45
|
selectedPhraseList,
|
|
46
|
-
setSelectedPhraseList
|
|
47
|
-
phraseLists
|
|
46
|
+
setSelectedPhraseList
|
|
48
47
|
} = opts;
|
|
49
48
|
|
|
50
49
|
// Track if we've already initialized from URL to prevent re-initialization
|
|
@@ -97,8 +96,8 @@ export default function useProgressUrlParams(opts) {
|
|
|
97
96
|
}
|
|
98
97
|
}
|
|
99
98
|
const phraseListId = searchParams.phraseListId;
|
|
100
|
-
if (phraseListId && !selectedPhraseList
|
|
101
|
-
const phraseList = phraseLists
|
|
99
|
+
if (phraseListId && !selectedPhraseList) {
|
|
100
|
+
const phraseList = topic?.phraseLists?.find(p => p.phraseListId === phraseListId);
|
|
102
101
|
if (phraseList) setSelectedPhraseList(phraseList);
|
|
103
102
|
}
|
|
104
103
|
}
|
|
@@ -1718,6 +1718,7 @@ export default function useExerciseState({
|
|
|
1718
1718
|
exerciseCompletion.answerAttempts = newAnswerAttempts && Array.isArray(newAnswerAttempts) ? newAnswerAttempts : answerAttempts;
|
|
1719
1719
|
exerciseCompletion.unansweredPhrases = unansweredPhrases && Array.isArray(unansweredPhrases) ? unansweredPhrases : questions;
|
|
1720
1720
|
exerciseCompletion.remainingQuestions = remainingQuestions;
|
|
1721
|
+
if (phraseListId) exerciseCompletion.phraseListId = phraseListId;
|
|
1721
1722
|
setQuestions(phrases.slice());
|
|
1722
1723
|
}
|
|
1723
1724
|
handleComplete(exerciseCompletion);
|
package/dist/utils/index.js
CHANGED
|
@@ -135,7 +135,7 @@ export const calcTopicCompletions = (topic, completions = [], isSectionHidden, a
|
|
|
135
135
|
let isPhrases;
|
|
136
136
|
let completedExerciseCount = 0;
|
|
137
137
|
let totalExercises = 0;
|
|
138
|
-
if (topic.phrases && topic.phrases.length > 0) {
|
|
138
|
+
if (topic.phrases && topic.phrases.length > 0 && !topic.phraseLists?.length) {
|
|
139
139
|
isPhrases = true;
|
|
140
140
|
}
|
|
141
141
|
if (isSectionHidden || topic.isTopicHidden) {
|
|
@@ -149,6 +149,7 @@ export const calcTopicCompletions = (topic, completions = [], isSectionHidden, a
|
|
|
149
149
|
isListeningHidden = true;
|
|
150
150
|
isTranslationHidden = true;
|
|
151
151
|
assignmentExercises.forEach(e => {
|
|
152
|
+
if (e.phraseListId) return;
|
|
152
153
|
if (e.name === "pronunciation") {
|
|
153
154
|
isPronunciationHidden = false;
|
|
154
155
|
}
|
|
@@ -189,12 +190,14 @@ export const calcTopicCompletions = (topic, completions = [], isSectionHidden, a
|
|
|
189
190
|
totalExercises++;
|
|
190
191
|
}
|
|
191
192
|
}
|
|
192
|
-
const completedPronunciation = Array.isArray(completions) ? completions.filter(c => c.topicId === topic.topicId && c.exercise === "pronunciation").length : 0;
|
|
193
|
-
const completedTranslation = Array.isArray(completions) ? completions.filter(c => c.topicId === topic.topicId && c.exercise === "translation").length : 0;
|
|
194
|
-
const completedListening = Array.isArray(completions) ? completions.filter(c => c.topicId === topic.topicId && c.exercise === "listening").length : 0;
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
const completedPronunciation = Array.isArray(completions) ? completions.filter(c => c.topicId === topic.topicId && c.exercise === "pronunciation" && !c.phraseListId).length : 0;
|
|
194
|
+
const completedTranslation = Array.isArray(completions) ? completions.filter(c => c.topicId === topic.topicId && c.exercise === "translation" && !c.phraseListId).length : 0;
|
|
195
|
+
const completedListening = Array.isArray(completions) ? completions.filter(c => c.topicId === topic.topicId && c.exercise === "listening" && !c.phraseListId).length : 0;
|
|
196
|
+
if (isPhrases) {
|
|
197
|
+
completedExerciseCount += !isPronunciationHidden && completedPronunciation > 0 ? 1 : 0;
|
|
198
|
+
completedExerciseCount += !isTranslationHidden && completedTranslation > 0 ? 1 : 0;
|
|
199
|
+
completedExerciseCount += !isListeningHidden && completedListening > 0 ? 1 : 0;
|
|
200
|
+
}
|
|
198
201
|
let completedRoleplays = 0;
|
|
199
202
|
let completedGames = 0;
|
|
200
203
|
let totalNumberOfGames = 0;
|
|
@@ -295,6 +298,48 @@ export const calcTopicCompletions = (topic, completions = [], isSectionHidden, a
|
|
|
295
298
|
completedCount
|
|
296
299
|
};
|
|
297
300
|
});
|
|
301
|
+
if (!assignmentExercises && topic.phraseLists?.length > 0) {
|
|
302
|
+
topic.phraseLists.forEach(phraseList => {
|
|
303
|
+
if (phraseList.isPhraseListHidden) return;
|
|
304
|
+
const hasMeaning = phraseList?.phrases?.some(p => p.definitions?.length > 0) && !phraseList?.hiddenPhraseGroupGames?.meaning;
|
|
305
|
+
const exerciseNames = ["translation", "listening", "pronunciation", ...(hasMeaning ? ["meaning"] : [])];
|
|
306
|
+
exerciseNames.forEach(exerciseName => {
|
|
307
|
+
totalExercises++;
|
|
308
|
+
const isCompleted = Array.isArray(completions) && completions.some(c => c.phraseListId === phraseList.phraseListId && c.exercise === exerciseName && c.topicId === topic.topicId);
|
|
309
|
+
if (isCompleted) completedExerciseCount++;
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
if (assignmentExercises) {
|
|
314
|
+
const phraseListExercises = assignmentExercises.filter(e => e.phraseListId);
|
|
315
|
+
const phraseListMap = {};
|
|
316
|
+
phraseListExercises.forEach(e => {
|
|
317
|
+
if (!phraseListMap[e.phraseListId]) phraseListMap[e.phraseListId] = [];
|
|
318
|
+
if (e.name) phraseListMap[e.phraseListId].push(e.name);
|
|
319
|
+
});
|
|
320
|
+
if (topic.phraseLists?.length > 0) {
|
|
321
|
+
Object.keys(phraseListMap).forEach(phraseListId => {
|
|
322
|
+
if (!phraseListMap[phraseListId].includes("meaning")) {
|
|
323
|
+
const pl = topic.phraseLists?.find(p => p.phraseListId === phraseListId);
|
|
324
|
+
const hasMeaning = pl?.phrases?.some(p => p.definitions?.length > 0) && !pl?.hiddenPhraseGroupGames?.meaning;
|
|
325
|
+
if (hasMeaning) phraseListMap[phraseListId].push("meaning");
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
Object.entries(phraseListMap).forEach(([phraseListId, exerciseNames]) => {
|
|
330
|
+
if (exerciseNames.length === 0) {
|
|
331
|
+
totalExercises++;
|
|
332
|
+
const isCompleted = Array.isArray(completions) && completions.some(c => c.phraseListId === phraseListId && c.topicId === topic.topicId);
|
|
333
|
+
if (isCompleted) completedExerciseCount++;
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
exerciseNames.forEach(exerciseName => {
|
|
337
|
+
totalExercises++;
|
|
338
|
+
const isCompleted = Array.isArray(completions) && completions.some(c => c.phraseListId === phraseListId && c.exercise === exerciseName && c.topicId === topic.topicId);
|
|
339
|
+
if (isCompleted) completedExerciseCount++;
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
}
|
|
298
343
|
const scoreValues = getScoreValues("topic", topic.topicId, completions);
|
|
299
344
|
return {
|
|
300
345
|
...topic,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nualang/nualang-ui-components",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1382",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"files": [
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"@mui/system": "^7.0.2",
|
|
102
102
|
"@mui/x-date-pickers": "^8.27.2",
|
|
103
103
|
"@nualang/eslint-config-nualang": "0.2.0-alpha-5",
|
|
104
|
-
"@nualang/nualang-api-and-queries": "^1.1.
|
|
104
|
+
"@nualang/nualang-api-and-queries": "^1.1.42",
|
|
105
105
|
"@react-theming/storybook-addon": "^1.1.10",
|
|
106
106
|
"@storybook/addon-docs": "^10.0.2",
|
|
107
107
|
"@storybook/addon-links": "^10.0.2",
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
"react-dom": "^19.2.4",
|
|
135
135
|
"react-pdf": "^10.2.0",
|
|
136
136
|
"react-router": "^7.12.0",
|
|
137
|
-
"rimraf": "^6.1.
|
|
137
|
+
"rimraf": "^6.1.3",
|
|
138
138
|
"storybook": "^10.0.2",
|
|
139
139
|
"vitest": "^4.0.18",
|
|
140
140
|
"zx": "^8.8.5"
|