@nualang/nualang-ui-components 0.1.1357 → 0.1.1360

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.
@@ -11,6 +11,7 @@ function SelectExercise({
11
11
  courses,
12
12
  getCourseSections,
13
13
  getRoleplays,
14
+ getPhraseLists,
14
15
  handleCreateGame,
15
16
  username,
16
17
  courseSectionTopicId = ""
@@ -29,6 +30,7 @@ function SelectExercise({
29
30
  courses: courses,
30
31
  getCourseSections: getCourseSections,
31
32
  getRoleplays: getRoleplays,
33
+ getPhraseLists: getPhraseLists,
32
34
  username: username,
33
35
  handleCreateGame: handleCreateGame,
34
36
  t: t,
@@ -222,7 +222,7 @@ function Meaning({
222
222
  const correctPhrase = question.phrase;
223
223
  const otherPhrases = phrases.filter(p => p.phrase !== correctPhrase).map(p => p.phrase);
224
224
  shuffle(otherPhrases);
225
- const distractors = otherPhrases.slice(0, 3);
225
+ const distractors = otherPhrases.slice(0, 4);
226
226
  const options = [correctPhrase, ...distractors].map((text, id) => ({
227
227
  text,
228
228
  id,
@@ -10,6 +10,7 @@ function Course({
10
10
  index,
11
11
  getCourseSections,
12
12
  getRoleplays,
13
+ getPhraseLists,
13
14
  username,
14
15
  handleCreateGame,
15
16
  t,
@@ -99,6 +100,7 @@ function Course({
99
100
  isExerciseSelected: isExerciseSelected,
100
101
  setIsExerciseSelected: setIsExerciseSelected,
101
102
  getRoleplays: getRoleplays,
103
+ getPhraseLists: getPhraseLists,
102
104
  handleSelectExercise: handleSelectExercise,
103
105
  selectedExercises: selectedExercises,
104
106
  useCase: useCase,
@@ -116,6 +118,7 @@ function CourseSelection({
116
118
  courses,
117
119
  getCourseSections,
118
120
  getRoleplays,
121
+ getPhraseLists,
119
122
  username,
120
123
  handleCreateGame,
121
124
  t,
@@ -137,6 +140,7 @@ function CourseSelection({
137
140
  index: index,
138
141
  getCourseSections: getCourseSections,
139
142
  getRoleplays: getRoleplays,
143
+ getPhraseLists: getPhraseLists,
140
144
  username: username,
141
145
  handleCreateGame: handleCreateGame,
142
146
  t: t,
@@ -3,15 +3,13 @@ import { ListSubheader, List, ListItemIcon, ListItemText, ListItemAvatar, Collap
3
3
  import { useTheme } from "@mui/material/styles";
4
4
  import useMediaQuery from "@mui/material/useMediaQuery";
5
5
  import ImageSearchIcon from "@mui/icons-material/ImageSearch";
6
- import PlayCircleOutlineIcon from "@mui/icons-material/PlayCircleOutline";
7
- import HearingIcon from "@mui/icons-material/Hearing";
8
- import SwapHorizIcon from "@mui/icons-material/SwapHoriz";
9
6
  import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
10
7
  import ExpandLessIcon from "@mui/icons-material/ExpandLess";
11
- import RecordVoiceOverIcon from "@mui/icons-material/RecordVoiceOver";
12
8
  import ForumIcon from "@mui/icons-material/Forum";
13
9
  import OndemandVideoIcon from "@mui/icons-material/OndemandVideo";
14
10
  import RoleplaySelection from "../RoleplaySelection/RoleplaySelection";
11
+ import PhraseListSelection from "../PhraseListSelection/PhraseListSelection";
12
+ import MenuBookIcon from '@mui/icons-material/MenuBook';
15
13
  import { CardInfoSectionItem } from "../../Cards/CardElements";
16
14
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
17
15
  function Exercise({
@@ -19,71 +17,51 @@ function Exercise({
19
17
  description,
20
18
  courseSectionTopicId,
21
19
  handleCreateGame,
22
- section,
23
20
  t,
24
21
  isExerciseSelected,
25
- setIsExerciseSelected,
26
22
  roleplays,
27
- phrases,
28
23
  handleSelectExercise = null,
29
24
  useCase,
30
- isCreator
25
+ getPhraseLists,
26
+ courseId,
27
+ sectionId,
28
+ topicId,
29
+ setIsExerciseSelected,
30
+ phrases
31
31
  }) {
32
- const [listeningHidden, setListeningHidden] = useState(false);
33
- const [translationHidden, setTranslationHidden] = useState(false);
34
- const [pronunciationHidden, setPronunciationHidden] = useState(false);
35
32
  const noRoleplays = name.toLowerCase() === "roleplays" && roleplays.length === 0;
33
+ const noPhrases = name.toLowerCase() === "phrases" && !phrases?.length && !getPhraseLists;
36
34
  const [roleplaysOpen, setRoleplaysOpen] = useState(noRoleplays ? false : true);
37
- const handleToggleRoleplays = () => {
38
- setRoleplaysOpen(!roleplaysOpen);
39
- };
40
- const handleExerciseSelected = () => {
41
- setIsExerciseSelected(true);
42
- handleCreateGame({
43
- courseSectionTopicId: courseSectionTopicId,
44
- exercise: name,
45
- timer: 30,
46
- answerOption: 0
47
- });
35
+ const [phraseListsOpen, setPhraseListsOpen] = useState(noPhrases ? false : true);
36
+ const [hasPhraseListsAvailable, setHasPhraseListsAvailable] = useState(true);
37
+ const handlePhraseListsLoaded = count => {
38
+ if (count === 0) {
39
+ setHasPhraseListsAvailable(false);
40
+ setPhraseListsOpen(false);
41
+ }
48
42
  };
49
- useEffect(() => {
50
- section.topics.forEach(topic => {
51
- if (topic.hiddenExercises && topic.hiddenExercises.includes("listening")) {
52
- setListeningHidden(true);
53
- }
54
- if (topic.hiddenExercises && topic.hiddenExercises.includes("translation")) {
55
- setTranslationHidden(true);
56
- }
57
- if (topic.hiddenExercises && topic.hiddenExercises.includes("pronunciation")) {
58
- setPronunciationHidden(true);
59
- }
60
- });
61
- }, [section]);
62
- const isValidExercise = phrases.length >= 2 || name.toLowerCase() === "roleplays";
63
- if (name.toLowerCase() === "listening" && listeningHidden || name.toLowerCase() === "translation" && translationHidden || name.toLowerCase() === "pronunciation" && pronunciationHidden) {
64
- return null;
65
- }
43
+ const noPhraseListsAvailable = name.toLowerCase() === "phrases" && !hasPhraseListsAvailable;
66
44
  return /*#__PURE__*/_jsxs(Tooltip, {
67
- title: name === "roleplays" && noRoleplays ? t("topic_no_roleplays") : !isValidExercise ? t("minimum_two_phrases") : "",
45
+ title: (name === "roleplays" && noRoleplays ? t("topic_no_roleplays") : "") || (name === "phrases" && noPhrases ? t("topic_no_phrases") : "") || (noPhraseListsAvailable ? t("no_phrase_lists") : ""),
68
46
  children: [/*#__PURE__*/_jsxs(ListItemButton, {
69
47
  onClick: () => {
70
- name.toLowerCase() === "roleplays" ? handleToggleRoleplays() : handleExerciseSelected();
48
+ if (name.toLowerCase() === "roleplays") {
49
+ setRoleplaysOpen(!roleplaysOpen);
50
+ } else if (name.toLowerCase() === "phrases") {
51
+ setPhraseListsOpen(!phraseListsOpen);
52
+ }
71
53
  },
72
54
  display: "flex",
73
55
  alignItems: "center",
74
56
  justifyContent: "space-between",
75
- disabled: isExerciseSelected || !isValidExercise || noRoleplays,
57
+ disabled: isExerciseSelected || noRoleplays || noPhrases || noPhraseListsAvailable,
76
58
  sx: theme => ({
77
59
  [theme.breakpoints.up("sm")]: {
78
60
  ml: 3.5
79
61
  }
80
62
  }),
81
- children: [name.toLowerCase() === "listening" && /*#__PURE__*/_jsx(ListItemIcon, {
82
- children: /*#__PURE__*/_jsx(HearingIcon, {})
83
- }), name.toLowerCase() === "translation" && /*#__PURE__*/_jsx(ListItemIcon, {
84
- children: /*#__PURE__*/_jsx(SwapHorizIcon, {})
85
- }), name.toLowerCase() === "pronunciation" && /*#__PURE__*/_jsx(ListItemIcon, {
86
- children: /*#__PURE__*/_jsx(RecordVoiceOverIcon, {})
63
+ children: [name.toLowerCase() === "phrases" && /*#__PURE__*/_jsx(ListItemIcon, {
64
+ children: /*#__PURE__*/_jsx(MenuBookIcon, {})
87
65
  }), name.toLowerCase() === "roleplays" && /*#__PURE__*/_jsx(ListItemIcon, {
88
66
  children: /*#__PURE__*/_jsx(ForumIcon, {})
89
67
  }), /*#__PURE__*/_jsx(Box, {
@@ -92,13 +70,31 @@ function Exercise({
92
70
  primary: t(name),
93
71
  secondary: t(description)
94
72
  })
73
+ }), name.toLowerCase() === "phrases" && /*#__PURE__*/_jsx(ListItemIcon, {
74
+ children: phraseListsOpen ? /*#__PURE__*/_jsx(ExpandLessIcon, {}) : /*#__PURE__*/_jsx(ExpandMoreIcon, {})
95
75
  }), name.toLowerCase() === "roleplays" && /*#__PURE__*/_jsx(ListItemIcon, {
96
76
  children: roleplaysOpen ? /*#__PURE__*/_jsx(ExpandLessIcon, {}) : /*#__PURE__*/_jsx(ExpandMoreIcon, {})
97
- }), (name.toLowerCase() === "listening" || name.toLowerCase() === "translation" || name.toLowerCase() === "pronunciation") && /*#__PURE__*/_jsx(ListItemIcon, {
98
- children: /*#__PURE__*/_jsx(PlayCircleOutlineIcon, {
99
- color: "primary"
100
- })
101
77
  })]
78
+ }), name.toLowerCase() === "phrases" && getPhraseLists && /*#__PURE__*/_jsx(Collapse, {
79
+ in: phraseListsOpen,
80
+ timeout: "auto",
81
+ unmountOnExit: true,
82
+ children: /*#__PURE__*/_jsx(Box, {
83
+ ml: 7,
84
+ children: /*#__PURE__*/_jsx(PhraseListSelection, {
85
+ getPhraseLists: getPhraseLists,
86
+ courseId: courseId,
87
+ sectionId: sectionId,
88
+ topicId: topicId,
89
+ handleCreateGame: handleCreateGame,
90
+ courseSectionTopicId: courseSectionTopicId,
91
+ t: t,
92
+ isExerciseSelected: isExerciseSelected,
93
+ setIsExerciseSelected: setIsExerciseSelected,
94
+ isExpanded: phraseListsOpen,
95
+ onPhraseListsLoaded: handlePhraseListsLoaded
96
+ })
97
+ })
102
98
  }), name.toLowerCase() === "roleplays" && /*#__PURE__*/_jsx(Collapse, {
103
99
  in: roleplaysOpen,
104
100
  timeout: "auto",
@@ -135,6 +131,7 @@ function ExerciseList({
135
131
  useCase,
136
132
  handleStartExercise,
137
133
  isCreator,
134
+ getPhraseLists,
138
135
  ...otherProps
139
136
  }) {
140
137
  const courseSectionTopicId = `${courseId}|${sectionId}|${topicId}`;
@@ -161,6 +158,7 @@ function ExerciseList({
161
158
  useCase: useCase,
162
159
  handleStartExercise: handleStartExercise,
163
160
  isCreator: isCreator,
161
+ getPhraseLists: getPhraseLists,
164
162
  ...otherProps
165
163
  }, keyId);
166
164
  })
@@ -199,6 +197,7 @@ function Topic({
199
197
  handleStartExercise,
200
198
  isCreator,
201
199
  courseSectionTopicId,
200
+ getPhraseLists,
202
201
  ...otherProps
203
202
  }) {
204
203
  const theme = useTheme();
@@ -226,11 +225,8 @@ function Topic({
226
225
  }
227
226
  }, [courseSectionTopicId, courseId, sectionId, topicId]);
228
227
  const exercises = [{
229
- name: "translation",
230
- description: `translation_exercise_desc`
231
- }, {
232
- name: "listening",
233
- description: `listening_exercise_desc`
228
+ name: "phrases",
229
+ description: `phrases_exercise_desc`
234
230
  }, {
235
231
  name: "roleplays",
236
232
  description: `roleplay_exercise_desc`
@@ -241,7 +237,7 @@ function Topic({
241
237
  const hasNonEmptyRoleplay = roleplays.some(roleplay => roleplay?.script?.length !== 0 && roleplay?.script?.some(line => line.text !== undefined));
242
238
  return /*#__PURE__*/_jsx(_Fragment, {
243
239
  children: /*#__PURE__*/_jsxs(Tooltip, {
244
- title: phrases.length === 0 ? t("topic_no_phrases") : "",
240
+ title: !phrases?.length && !getPhraseLists ? t("topic_no_phrases") : "",
245
241
  placement: "top",
246
242
  children: [!isTopicHidden && /*#__PURE__*/_jsxs(ListItemButton, {
247
243
  ref: topicRef,
@@ -251,7 +247,7 @@ function Topic({
251
247
  justifyContent: "space-between",
252
248
  onClick: toggleExerciseList,
253
249
  "data-cy": "topic-list-item",
254
- disabled: phrases.length === 0 && !hasNonEmptyRoleplay || isExerciseSelected,
250
+ disabled: !phrases?.length && !getPhraseLists && !hasNonEmptyRoleplay || isExerciseSelected,
255
251
  children: [isLargeScreen && /*#__PURE__*/_jsx(ListItemAvatar, {
256
252
  children: picture ? /*#__PURE__*/_jsx(Avatar, {
257
253
  src: picture,
@@ -329,6 +325,7 @@ function Topic({
329
325
  useCase: useCase,
330
326
  handleStartExercise: handleStartExercise,
331
327
  isCreator: isCreator,
328
+ getPhraseLists: getPhraseLists,
332
329
  ...otherProps
333
330
  })
334
331
  })]
@@ -352,6 +349,7 @@ function Section({
352
349
  handleStartExercise,
353
350
  isCreator,
354
351
  courseSectionTopicId,
352
+ getPhraseLists,
355
353
  ...otherProps
356
354
  }) {
357
355
  const root = {
@@ -390,7 +388,8 @@ function Section({
390
388
  useCase: useCase,
391
389
  handleStartExercise: handleStartExercise,
392
390
  isCreator: isCreator,
393
- courseSectionTopicId: courseSectionTopicId
391
+ courseSectionTopicId: courseSectionTopicId,
392
+ getPhraseLists: getPhraseLists
394
393
  }, `${sectionId}-${topic.topicId}`))
395
394
  })
396
395
  });
@@ -410,6 +409,7 @@ function SectionList({
410
409
  handleStartExercise,
411
410
  isCreator,
412
411
  courseSectionTopicId,
412
+ getPhraseLists,
413
413
  ...otherProps
414
414
  }) {
415
415
  return /*#__PURE__*/_jsx(_Fragment, {
@@ -431,6 +431,7 @@ function SectionList({
431
431
  handleStartExercise: handleStartExercise,
432
432
  isCreator: isCreator,
433
433
  courseSectionTopicId: courseSectionTopicId,
434
+ getPhraseLists: getPhraseLists,
434
435
  ...otherProps
435
436
  }, section.sectionId);
436
437
  }) : /*#__PURE__*/_jsx(Typography, {
@@ -457,6 +458,7 @@ function ExerciseSelection({
457
458
  useCase,
458
459
  handleStartExercise,
459
460
  courseSectionTopicId,
461
+ getPhraseLists,
460
462
  ...otherProps
461
463
  }) {
462
464
  if (isLoading) {
@@ -477,6 +479,7 @@ function ExerciseSelection({
477
479
  getRoleplays: getRoleplays,
478
480
  handleStartExercise: handleStartExercise,
479
481
  courseSectionTopicId: courseSectionTopicId,
482
+ getPhraseLists: getPhraseLists,
480
483
  ...otherProps
481
484
  })
482
485
  });
@@ -498,6 +501,7 @@ function ExerciseSelection({
498
501
  handleSelectExercise: handleSelectExercise,
499
502
  useCase: useCase,
500
503
  courseSectionTopicId: courseSectionTopicId,
504
+ getPhraseLists: getPhraseLists,
501
505
  ...otherProps
502
506
  })
503
507
  });
@@ -1027,7 +1027,7 @@ function Exercises(props) {
1027
1027
  };
1028
1028
  const numberOfPhraseListCompletions = (completions, phraseListId) => {
1029
1029
  if (isEmptyArray(completions)) return 0;
1030
- return completions.filter(c => ["translation", "listening", "pronunciation"].includes(c.exercise) && c.phraseListId === phraseListId).length;
1030
+ return completions.filter(c => ["translation", "listening", "pronunciation"].includes(c.exercise) && (c.phraseListId === phraseListId || !c.phraseListId && phraseListId?.startsWith('leg-'))).length;
1031
1031
  };
1032
1032
 
1033
1033
  // ── phraseLists drag state ──
@@ -0,0 +1,213 @@
1
+ import { useState, useEffect } from "react";
2
+ import { Box, Button, ListItemText, ListItemButton, Avatar, ListItemIcon, Collapse, CircularProgress, Typography } from "@mui/material";
3
+ import PlayCircleOutlineIcon from "@mui/icons-material/PlayCircleOutline";
4
+ import HearingIcon from "@mui/icons-material/Hearing";
5
+ import SwapHorizIcon from "@mui/icons-material/SwapHoriz";
6
+ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
7
+ import ExpandLessIcon from "@mui/icons-material/ExpandLess";
8
+ import ImageSearchIcon from "@mui/icons-material/ImageSearch";
9
+ import ExtensionIcon from "@mui/icons-material/Extension";
10
+ import { courses as courseQuerys } from "@nualang/nualang-api-and-queries/Queries";
11
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
+ function PhraseList({
13
+ phraseList,
14
+ handleCreateGame,
15
+ courseSectionTopicId,
16
+ isPhraseListSelected,
17
+ setIsPhraseListSelected,
18
+ t,
19
+ isExerciseSelected,
20
+ setIsExerciseSelected
21
+ }) {
22
+ const [open, setOpen] = useState(false);
23
+ const isTranslationHidden = phraseList?.hiddenPhraseGroupGames?.translation;
24
+ const isListeningHidden = phraseList?.hiddenPhraseGroupGames?.listening;
25
+ const isMeaningHidden = phraseList?.hiddenPhraseGroupGames?.meaning;
26
+ const hasDefinitions = phraseList?.phrases?.some(p => p.definitions?.length > 0);
27
+ const hasSelectableGame = !isTranslationHidden || !isListeningHidden || !isMeaningHidden;
28
+ if (!hasSelectableGame) return null;
29
+ const handleGameSelected = exercise => () => {
30
+ setIsPhraseListSelected(true);
31
+ setIsExerciseSelected(true);
32
+ handleCreateGame({
33
+ courseSectionTopicId,
34
+ exercise,
35
+ timer: 30,
36
+ answerOption: 0,
37
+ phraseListId: phraseList.phraseListId,
38
+ phrases: phraseList.phrases || []
39
+ });
40
+ };
41
+ return /*#__PURE__*/_jsxs("div", {
42
+ children: [/*#__PURE__*/_jsxs(ListItemButton, {
43
+ onClick: () => setOpen(!open),
44
+ disabled: isPhraseListSelected || isExerciseSelected,
45
+ sx: {
46
+ display: "flex",
47
+ alignItems: "center",
48
+ justifyContent: "space-between",
49
+ flexDirection: {
50
+ xs: "column",
51
+ sm: "row"
52
+ }
53
+ },
54
+ children: [/*#__PURE__*/_jsxs(Box, {
55
+ display: "flex",
56
+ alignItems: "center",
57
+ width: {
58
+ xs: "100%",
59
+ sm: "85%"
60
+ },
61
+ children: [phraseList.picture ? /*#__PURE__*/_jsx(Avatar, {
62
+ src: phraseList.picture,
63
+ alt: phraseList.phraseListName
64
+ }) : /*#__PURE__*/_jsx(Avatar, {
65
+ alt: phraseList.phraseListName,
66
+ children: /*#__PURE__*/_jsx(ImageSearchIcon, {})
67
+ }), /*#__PURE__*/_jsx(ListItemText, {
68
+ primary: phraseList.phraseListName,
69
+ secondary: phraseList.description,
70
+ sx: {
71
+ marginLeft: 2
72
+ }
73
+ })]
74
+ }), /*#__PURE__*/_jsx(ListItemIcon, {
75
+ children: open ? /*#__PURE__*/_jsx(ExpandLessIcon, {
76
+ sx: {
77
+ color: "#757575"
78
+ }
79
+ }) : /*#__PURE__*/_jsx(ExpandMoreIcon, {
80
+ sx: {
81
+ color: "#757575"
82
+ }
83
+ })
84
+ })]
85
+ }), /*#__PURE__*/_jsx(Collapse, {
86
+ in: open,
87
+ timeout: "auto",
88
+ unmountOnExit: true,
89
+ children: /*#__PURE__*/_jsxs(Box, {
90
+ ml: 3.5,
91
+ children: [!isTranslationHidden && /*#__PURE__*/_jsxs(ListItemButton, {
92
+ onClick: handleGameSelected("translation"),
93
+ disabled: isPhraseListSelected || isExerciseSelected,
94
+ children: [/*#__PURE__*/_jsx(ListItemIcon, {
95
+ children: /*#__PURE__*/_jsx(SwapHorizIcon, {})
96
+ }), /*#__PURE__*/_jsx(ListItemText, {
97
+ primary: t("translation"),
98
+ secondary: t("translation_exercise_desc")
99
+ }), /*#__PURE__*/_jsx(ListItemIcon, {
100
+ children: /*#__PURE__*/_jsx(PlayCircleOutlineIcon, {
101
+ color: "primary"
102
+ })
103
+ })]
104
+ }), !isListeningHidden && /*#__PURE__*/_jsxs(ListItemButton, {
105
+ onClick: handleGameSelected("listening"),
106
+ disabled: isPhraseListSelected || isExerciseSelected,
107
+ children: [/*#__PURE__*/_jsx(ListItemIcon, {
108
+ children: /*#__PURE__*/_jsx(HearingIcon, {})
109
+ }), /*#__PURE__*/_jsx(ListItemText, {
110
+ primary: t("listening"),
111
+ secondary: t("listening_exercise_desc")
112
+ }), /*#__PURE__*/_jsx(ListItemIcon, {
113
+ children: /*#__PURE__*/_jsx(PlayCircleOutlineIcon, {
114
+ color: "primary"
115
+ })
116
+ })]
117
+ }), !isMeaningHidden && /*#__PURE__*/_jsxs(ListItemButton, {
118
+ onClick: handleGameSelected("meaning"),
119
+ disabled: isPhraseListSelected || isExerciseSelected || !hasDefinitions,
120
+ children: [/*#__PURE__*/_jsx(ListItemIcon, {
121
+ children: /*#__PURE__*/_jsx(ExtensionIcon, {})
122
+ }), /*#__PURE__*/_jsx(ListItemText, {
123
+ primary: t("meaning"),
124
+ secondary: t("meaning_exercise_desc")
125
+ }), /*#__PURE__*/_jsx(ListItemIcon, {
126
+ children: /*#__PURE__*/_jsx(PlayCircleOutlineIcon, {
127
+ color: "primary"
128
+ })
129
+ })]
130
+ })]
131
+ })
132
+ })]
133
+ });
134
+ }
135
+ function PhraseListSelection({
136
+ getPhraseLists,
137
+ courseId,
138
+ sectionId,
139
+ topicId,
140
+ handleCreateGame,
141
+ courseSectionTopicId,
142
+ t,
143
+ isExerciseSelected,
144
+ isExpanded,
145
+ setIsExerciseSelected,
146
+ onPhraseListsLoaded
147
+ }) {
148
+ const [isPhraseListSelected, setIsPhraseListSelected] = useState(false);
149
+ const phraseListsQuery = courseQuerys.useCourseSectionTopicPhraseLists(async (cId, sId, tId, filters) => getPhraseLists(cId, sId, tId, filters), {
150
+ courseId,
151
+ sectionId,
152
+ topicId,
153
+ filters: {
154
+ limit: 1000,
155
+ returnvalues: "phrases"
156
+ }
157
+ }, {
158
+ enabled: !!courseId && !!sectionId && !!topicId && isExpanded
159
+ });
160
+ const phraseLists = phraseListsQuery.isSuccess && Array.isArray(phraseListsQuery.data?.Items) ? phraseListsQuery.data.Items : [];
161
+ useEffect(() => {
162
+ if (phraseListsQuery.isSuccess && onPhraseListsLoaded) {
163
+ onPhraseListsLoaded(phraseLists.length);
164
+ }
165
+ }, [phraseListsQuery.isSuccess, phraseLists.length]);
166
+ if (phraseListsQuery.isError) {
167
+ return /*#__PURE__*/_jsxs(Box, {
168
+ px: 2,
169
+ py: 1,
170
+ children: [/*#__PURE__*/_jsx(Typography, {
171
+ variant: "body2",
172
+ color: "error",
173
+ children: t("phrase_lists_error")
174
+ }), /*#__PURE__*/_jsx(Button, {
175
+ size: "small",
176
+ onClick: () => phraseListsQuery.refetch(),
177
+ children: t("retry")
178
+ })]
179
+ });
180
+ }
181
+ if (phraseListsQuery.isLoading) {
182
+ return /*#__PURE__*/_jsx(Box, {
183
+ display: "flex",
184
+ justifyContent: "center",
185
+ py: 1,
186
+ children: /*#__PURE__*/_jsx(CircularProgress, {
187
+ size: 24
188
+ })
189
+ });
190
+ }
191
+ if (phraseListsQuery.isSuccess && phraseLists.length === 0) {
192
+ return /*#__PURE__*/_jsx(Box, {
193
+ px: 2,
194
+ py: 1,
195
+ children: /*#__PURE__*/_jsx(Typography, {
196
+ variant: "body2",
197
+ color: "textSecondary",
198
+ children: t("no_phrase_lists")
199
+ })
200
+ });
201
+ }
202
+ return phraseLists.map((phraseList, index) => /*#__PURE__*/_jsx(PhraseList, {
203
+ phraseList: phraseList,
204
+ handleCreateGame: handleCreateGame,
205
+ courseSectionTopicId: courseSectionTopicId,
206
+ isPhraseListSelected: isPhraseListSelected,
207
+ setIsPhraseListSelected: setIsPhraseListSelected,
208
+ t: t,
209
+ isExerciseSelected: isExerciseSelected,
210
+ setIsExerciseSelected: setIsExerciseSelected
211
+ }, phraseList.phraseListId || index));
212
+ }
213
+ export default PhraseListSelection;
@@ -16,6 +16,7 @@ import WaitingRoom from "../WaitingRoom/WaitingRoom";
16
16
  import LiveListener from "../LiveListener/LiveListener";
17
17
  import LiveTranslator from "../LiveTranslator/LiveTranslator";
18
18
  import LiveStory from "../LiveRoleplay/LiveStory/LiveStory";
19
+ import LiveMeaning from "../LiveMeaning/LiveMeaning";
19
20
  import HowToJoin from "../HowToJoin/HowToJoin";
20
21
  import GameMembers from "../GameMembers/GameMembers";
21
22
  import GameSettings from "../GameSettings/GameSettings";
@@ -510,6 +511,23 @@ export default function Game({
510
511
  handleShowLeaderboard: handleShowLeaderboard,
511
512
  ...otherProps
512
513
  });
514
+ } else if (exercise === "meaning") {
515
+ return /*#__PURE__*/_jsx(LiveMeaning, {
516
+ t: t,
517
+ currentQuestion: currentQuestion,
518
+ isCreator: isCreator,
519
+ languageInformation: languageInformation,
520
+ time: time,
521
+ setCurrentAnswer: setCurrentAnswer,
522
+ gameMembersTotal: membersInRoom?.length,
523
+ exercise: exercise,
524
+ voiceLanguageCode: voiceLanguageCode,
525
+ voice: voice,
526
+ voices: voices,
527
+ answerAttempt: answerAttempt,
528
+ handleShowLeaderboard: handleShowLeaderboard,
529
+ ...otherProps
530
+ });
513
531
  } else if (exercise === "roleplay-story") {
514
532
  return /*#__PURE__*/_jsx(LiveStory, {
515
533
  t: t,
@@ -0,0 +1,377 @@
1
+ import { useState, useEffect } from "react";
2
+ import { Grid, Button, Typography, Box } from "@mui/material";
3
+ import { makeStyles } from "tss-react/mui";
4
+ import PauseIcon from "@mui/icons-material/Pause";
5
+ import PlayIcon from "@mui/icons-material/PlayArrow";
6
+ import { CountdownCircleTimer } from "react-countdown-circle-timer";
7
+ import { useTheme } from "@mui/material/styles";
8
+ import AnswerResult from "../../Misc/AnswerResult/AnswerResult";
9
+ import ExerciseBottomBar from "../../Misc/ExerciseBottomBar/ExerciseBottomBar";
10
+ import DefaultButton from "../../Misc/DefaultColourButton/DefaultColourButton";
11
+ import ChatBubble from "../../Misc/ChatBubble/ChatBubble";
12
+ import WaveFormLite from "../../Misc/WaveFormLite/WaveFormLite";
13
+ import GameControls from "../GameControls/GameControls";
14
+ import GameQuestionResults from "../GameQuestionResults/GameQuestionResults";
15
+ import getVoiceImages from "../../utils/voiceImages";
16
+ import { removeExtraWhiteSpaces } from "../../utils/index";
17
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
18
+ const useStyles = makeStyles()(theme => ({
19
+ flex: {
20
+ [theme.breakpoints.down("md")]: {
21
+ display: "none"
22
+ },
23
+ flex: 1
24
+ },
25
+ root: {
26
+ [theme.breakpoints.down("md")]: {
27
+ padding: theme.spacing(2)
28
+ },
29
+ flexGrow: 1,
30
+ width: "100%",
31
+ padding: theme.spacing(4)
32
+ },
33
+ image: {
34
+ [theme.breakpoints.down("md")]: {
35
+ maxWidth: "200px",
36
+ maxHeight: "150px"
37
+ },
38
+ maxWidth: "250px",
39
+ height: "auto",
40
+ maxHeight: "200px",
41
+ borderRadius: "20px"
42
+ },
43
+ nualaSpeaking: {
44
+ [theme.breakpoints.down("md")]: {
45
+ height: "80px"
46
+ },
47
+ height: "100px",
48
+ minHeight: "80px",
49
+ minWidth: "80px",
50
+ borderRadius: "20px"
51
+ },
52
+ wordButton: {
53
+ textTransform: "none"
54
+ }
55
+ }));
56
+ function LiveMeaning({
57
+ t = text => text,
58
+ languageInformation,
59
+ isCreator,
60
+ currentQuestion,
61
+ isTimeUp,
62
+ time,
63
+ handleTimeUp,
64
+ handleShowLeaderboard,
65
+ wordOptions,
66
+ isAnswered,
67
+ setIsAnswered,
68
+ answerAttempt,
69
+ isCorrectResultOpen,
70
+ isIncorrectResultOpen,
71
+ handleContinue,
72
+ handleNextQuestion,
73
+ setCurrentAnswer,
74
+ gameMembersAnsweredTotal,
75
+ gameMembersTotal,
76
+ gameMembersCorrectAnswerTotal,
77
+ exercise,
78
+ voice,
79
+ voicePitch,
80
+ voiceLanguageCode,
81
+ voices,
82
+ pollyResult,
83
+ isSpeaking,
84
+ speak,
85
+ learnLang = "",
86
+ forLang = "",
87
+ languageTag
88
+ }) {
89
+ const {
90
+ classes
91
+ } = useStyles();
92
+ const theme = useTheme();
93
+ const [isPaused, setIsPaused] = useState(false);
94
+ const [selectedOption, setSelectedOption] = useState(null);
95
+ const [isFinishedInitialSpeaking, setIsFinishedInitialSpeaking] = useState(false);
96
+ useEffect(() => {
97
+ setSelectedOption(null);
98
+ setIsFinishedInitialSpeaking(false);
99
+ }, [currentQuestion]);
100
+ const definitionText = (() => {
101
+ if (!currentQuestion) return "";
102
+ if (currentQuestion.definitions?.length > 0) {
103
+ const raw = currentQuestion.definitions[0];
104
+ return typeof raw === "string" ? raw : "";
105
+ }
106
+ if (currentQuestion.translations?.length > 0) {
107
+ const raw = currentQuestion.translations[0];
108
+ return typeof raw === "string" ? raw : "";
109
+ }
110
+ return "";
111
+ })();
112
+ useEffect(() => {
113
+ if (isCreator && currentQuestion && definitionText !== "") {
114
+ speak(definitionText, voice, false, "#currentDefinition", voiceLanguageCode, false, voicePitch, () => setIsFinishedInitialSpeaking(true));
115
+ }
116
+ }, [currentQuestion?.phrase, isCreator]);
117
+ const voiceMap = getVoiceImages(voices || []);
118
+ const getVoiceImage = () => {
119
+ try {
120
+ const exerciseVoice = voiceMap.get(voice) || {};
121
+ return exerciseVoice.speakingImage || null;
122
+ } catch (error) {
123
+ console.error("Error retrieving voice image:", error);
124
+ return null;
125
+ }
126
+ };
127
+ const handleOptionSelect = option => {
128
+ if (isAnswered) return;
129
+ setSelectedOption(option);
130
+ setCurrentAnswer(option.text);
131
+ };
132
+ return /*#__PURE__*/_jsxs(_Fragment, {
133
+ children: [/*#__PURE__*/_jsxs(Grid, {
134
+ container: true,
135
+ className: classes.root,
136
+ direction: "column",
137
+ justifyContent: "center",
138
+ alignItems: "center",
139
+ spacing: 2,
140
+ sx: {
141
+ maxWidth: "lg",
142
+ mx: "auto"
143
+ },
144
+ component: "main",
145
+ children: [/*#__PURE__*/_jsx(Grid, {
146
+ children: /*#__PURE__*/_jsx(Typography, {
147
+ lang: languageTag,
148
+ variant: "h6",
149
+ component: "h2",
150
+ color: "inherit",
151
+ children: t("select_the_correct_phrase", {
152
+ lng: languageTag
153
+ })
154
+ })
155
+ }), currentQuestion?.image && /*#__PURE__*/_jsx(Grid, {
156
+ children: /*#__PURE__*/_jsx("img", {
157
+ src: currentQuestion.image,
158
+ alt: "",
159
+ className: classes.image
160
+ })
161
+ }), isCreator && /*#__PURE__*/_jsx(Grid, {
162
+ children: /*#__PURE__*/_jsxs(Box, {
163
+ sx: {
164
+ display: "flex",
165
+ justifyContent: "center",
166
+ flexDirection: "row"
167
+ },
168
+ children: [/*#__PURE__*/_jsx(Box, {
169
+ sx: {
170
+ display: "flex",
171
+ justifyContent: "center"
172
+ },
173
+ children: /*#__PURE__*/_jsx("img", {
174
+ src: getVoiceImage(),
175
+ className: classes.nualaSpeaking,
176
+ alt: ""
177
+ })
178
+ }), /*#__PURE__*/_jsxs(Box, {
179
+ sx: {
180
+ display: "grid",
181
+ justifyContent: "flex-end",
182
+ marginLeft: 1
183
+ },
184
+ children: [/*#__PURE__*/_jsx(ChatBubble, {
185
+ id: "currentDefinition",
186
+ handleSpeak: () => speak(definitionText, voice, true, null, voiceLanguageCode, false, voicePitch),
187
+ learnLang: learnLang,
188
+ voice: voice || null,
189
+ voicePitch: voicePitch || null,
190
+ forLang: forLang,
191
+ wordList: definitionText ? removeExtraWhiteSpaces(definitionText).split(" ") : [],
192
+ disableTranslation: false,
193
+ isMessage: false,
194
+ t: t
195
+ }), learnLang !== "irish" && /*#__PURE__*/_jsx(Box, {
196
+ children: /*#__PURE__*/_jsx(WaveFormLite, {
197
+ src: pollyResult ? pollyResult : "",
198
+ controls: false,
199
+ isSpeaking: isSpeaking,
200
+ muteAudio: true,
201
+ handleSpeak: () => speak(definitionText, voice, true, null, voiceLanguageCode, false, voicePitch),
202
+ theme: theme
203
+ })
204
+ })]
205
+ })]
206
+ })
207
+ }), isCreator ? /*#__PURE__*/_jsxs(Grid, {
208
+ sx: {
209
+ marginTop: 2,
210
+ textAlign: "center"
211
+ },
212
+ children: [!isAnswered && /*#__PURE__*/_jsxs(_Fragment, {
213
+ children: [!isTimeUp && /*#__PURE__*/_jsxs(Box, {
214
+ children: [/*#__PURE__*/_jsx(CountdownCircleTimer, {
215
+ isPlaying: !isPaused && isFinishedInitialSpeaking,
216
+ duration: time,
217
+ colors: theme.palette.primary.main,
218
+ size: 160,
219
+ onComplete: handleTimeUp,
220
+ children: ({
221
+ remainingTime
222
+ }) => /*#__PURE__*/_jsx(Typography, {
223
+ variant: "h2",
224
+ fontWeight: "fontWeightBold",
225
+ children: remainingTime
226
+ })
227
+ }), /*#__PURE__*/_jsx(Button, {
228
+ variant: "contained",
229
+ color: "primary",
230
+ sx: {
231
+ marginTop: 2,
232
+ paddingLeft: 1.5,
233
+ paddingRight: 1.5
234
+ },
235
+ onClick: () => setIsPaused(!isPaused),
236
+ startIcon: isPaused ? /*#__PURE__*/_jsx(PlayIcon, {}) : /*#__PURE__*/_jsx(PauseIcon, {}),
237
+ size: "small",
238
+ disabled: !isFinishedInitialSpeaking,
239
+ children: isPaused ? t("resume") : t("pause")
240
+ })]
241
+ }), isTimeUp && /*#__PURE__*/_jsxs(Box, {
242
+ sx: {
243
+ textAlign: "center"
244
+ },
245
+ children: [/*#__PURE__*/_jsx(Typography, {
246
+ variant: "h5",
247
+ fontWeight: "fontWeightBold",
248
+ textAlign: "center",
249
+ color: "success.main",
250
+ children: currentQuestion?.phrase
251
+ }), /*#__PURE__*/_jsx(Button, {
252
+ variant: "contained",
253
+ color: "primary",
254
+ sx: {
255
+ marginTop: 5
256
+ },
257
+ onClick: () => handleShowLeaderboard(),
258
+ children: t("next")
259
+ }), /*#__PURE__*/_jsx(GameQuestionResults, {
260
+ t: t,
261
+ gameMembersCorrectAnswerTotal: gameMembersCorrectAnswerTotal,
262
+ gameMembersTotal: gameMembersTotal
263
+ })]
264
+ }), !isTimeUp && /*#__PURE__*/_jsx(GameControls, {
265
+ t: t,
266
+ handleTimeUp: handleTimeUp,
267
+ gameMembersAnsweredTotal: gameMembersAnsweredTotal,
268
+ gameMembersTotal: gameMembersTotal
269
+ })]
270
+ }), isAnswered && /*#__PURE__*/_jsxs(Box, {
271
+ sx: {
272
+ marginTop: 2
273
+ },
274
+ children: [/*#__PURE__*/_jsx(Typography, {
275
+ variant: "h5",
276
+ fontWeight: "fontWeightBold",
277
+ textAlign: "center",
278
+ children: currentQuestion?.phrase || ""
279
+ }), /*#__PURE__*/_jsx(Button, {
280
+ variant: "contained",
281
+ color: "primary",
282
+ sx: {
283
+ marginTop: 5
284
+ },
285
+ onClick: () => handleShowLeaderboard(),
286
+ children: t("next")
287
+ }), /*#__PURE__*/_jsx(GameQuestionResults, {
288
+ t: t,
289
+ gameMembersCorrectAnswerTotal: gameMembersCorrectAnswerTotal,
290
+ gameMembersTotal: gameMembersTotal
291
+ })]
292
+ })]
293
+ }) : /*#__PURE__*/_jsx(Grid, {
294
+ sx: {
295
+ marginTop: 2,
296
+ width: "100%"
297
+ },
298
+ children: !isAnswered ? /*#__PURE__*/_jsxs(Box, {
299
+ component: "fieldset",
300
+ sx: {
301
+ borderWidth: 0
302
+ },
303
+ children: [/*#__PURE__*/_jsx(Typography, {
304
+ variant: "subtitle1",
305
+ color: "inherit",
306
+ gutterBottom: true,
307
+ component: "legend",
308
+ sx: {
309
+ textAlign: "center",
310
+ paddingBottom: 1
311
+ },
312
+ children: t("select_the_correct_answer")
313
+ }), /*#__PURE__*/_jsx(Grid, {
314
+ children: /*#__PURE__*/_jsx(Grid, {
315
+ container: true,
316
+ sx: {
317
+ width: "100%"
318
+ },
319
+ justifyContent: "center",
320
+ spacing: 1,
321
+ children: (wordOptions || []).map(option => /*#__PURE__*/_jsx(Grid, {
322
+ children: /*#__PURE__*/_jsx(DefaultButton, {
323
+ variant: selectedOption?.id === option.id ? "contained" : "outlined",
324
+ onClick: () => handleOptionSelect(option),
325
+ className: classes.wordButton,
326
+ children: option.text
327
+ })
328
+ }, option.id))
329
+ })
330
+ })]
331
+ }) : /*#__PURE__*/_jsx(Box, {
332
+ sx: {
333
+ marginTop: 2,
334
+ textAlign: "center"
335
+ },
336
+ children: /*#__PURE__*/_jsx(Typography, {
337
+ variant: "h5",
338
+ fontWeight: "fontWeightBold",
339
+ textAlign: "center",
340
+ children: selectedOption?.text || ""
341
+ })
342
+ })
343
+ })]
344
+ }), /*#__PURE__*/_jsx(AnswerResult, {
345
+ t: t,
346
+ open: isCorrectResultOpen,
347
+ handleNext: handleNextQuestion,
348
+ ...answerAttempt,
349
+ result: "correct",
350
+ languageInformation: languageInformation,
351
+ isLive: true,
352
+ exercise: exercise
353
+ }), /*#__PURE__*/_jsx(AnswerResult, {
354
+ t: t,
355
+ open: isIncorrectResultOpen,
356
+ handleNext: handleNextQuestion,
357
+ ...answerAttempt,
358
+ result: "incorrect",
359
+ languageInformation: languageInformation,
360
+ isLive: true,
361
+ exercise: exercise
362
+ }), !isCreator && /*#__PURE__*/_jsx(ExerciseBottomBar, {
363
+ t: t,
364
+ handleSubmit: () => handleContinue(false),
365
+ handleSkip: () => handleContinue(true),
366
+ isSubmitAnswerAllowed: true,
367
+ isSubmitAnswerDisabled: !selectedOption,
368
+ isLive: true,
369
+ isAnswered: isAnswered,
370
+ setIsAnswered: setIsAnswered,
371
+ isTimeUp: isTimeUp,
372
+ setCurrentAnswer: setCurrentAnswer,
373
+ exercise: exercise
374
+ })]
375
+ });
376
+ }
377
+ export default LiveMeaning;
@@ -48,6 +48,8 @@ function ExerciseBottomBar({
48
48
  } else if (isLive && !isAnswered && exercise === "translation") {
49
49
  setCurrentAnswer(translationText ? translationText : translationWords);
50
50
  setIsAnswered(true);
51
+ } else if (isLive && !isAnswered && exercise === "meaning") {
52
+ setIsAnswered(true);
51
53
  } else {
52
54
  handleSubmit();
53
55
  }
@@ -1637,6 +1637,7 @@ function Classroom({
1637
1637
  handleClose: handleCloseSelectExercise,
1638
1638
  getCourseSections: getCourseSections,
1639
1639
  getRoleplays: getRoleplays,
1640
+ getPhraseLists: getCourseSectionTopicPhraseLists,
1640
1641
  username: username,
1641
1642
  handleCreateGame: handleCreateLiveGame,
1642
1643
  t: t,
@@ -1724,7 +1725,8 @@ export default function ViewClassroom({
1724
1725
  makeChatGptApiRequest,
1725
1726
  siteLanguage
1726
1727
  }) {
1727
- console.log("ViewClassroom render");
1728
+ // console.log("ViewClassroom render");
1729
+
1728
1730
  const {
1729
1731
  classes
1730
1732
  } = useStyles();
@@ -306,7 +306,7 @@ function ExerciseListItem({
306
306
  return /*#__PURE__*/_jsx(ListItemIcon, {});
307
307
  }
308
308
  let icon = null;
309
- let completions = exercise === "roleplays" ? data?.completions.map(completion => completion.exercise.startsWith("roleplay") && completion.roleplayId === roleplay.roleplayId) : phraseListId ? data?.completions.map(completion => completion.exercise === exercise && completion.phraseListId === phraseListId) : data?.completions.map(completion => completion.exercise === exercise);
309
+ let completions = exercise === "roleplays" ? data?.completions.map(completion => completion.exercise.startsWith("roleplay") && completion.roleplayId === roleplay.roleplayId) : phraseListId ? data?.completions.map(completion => completion.exercise === exercise && (completion.phraseListId === phraseListId || !completion.phraseListId && phraseListId?.startsWith('leg-'))) : data?.completions.map(completion => completion.exercise === exercise);
310
310
  if (exercise === "roleplays" && roleplay?.roleplayId && completions.length > 0) {
311
311
  icon = /*#__PURE__*/_jsx(Badge, {
312
312
  badgeContent: completions.length,
@@ -564,7 +564,7 @@ function MemberListItem({
564
564
  }), currentView === "topic" && phraseLists.map((phraseList, i) => {
565
565
  const topicData = getTopicData(data, selectedTopic, selectedCourse, selectedSection);
566
566
  const phraseListExercises = ["translation", "listening", "pronunciation"];
567
- const completedCount = phraseListExercises.filter(exercise => topicData?.completions?.some(c => c.exercise === exercise && c.phraseListId === phraseList.phraseListId)).length;
567
+ const completedCount = phraseListExercises.filter(exercise => topicData?.completions?.some(c => c.exercise === exercise && (c.phraseListId === phraseList.phraseListId || !c.phraseListId && phraseList.phraseListId?.startsWith('leg-')))).length;
568
568
  return /*#__PURE__*/_jsx(MenuItem, {
569
569
  children: /*#__PURE__*/_jsx(ListItem, {
570
570
  sx: {
@@ -356,7 +356,7 @@ export function ExerciseCellData(props) {
356
356
  } else if (exercise === "bots") {
357
357
  completions = data?.completions.filter(completion => completion.exercise === "bot" && completion.botId === botId);
358
358
  } else if (phraseListId) {
359
- completions = data?.completions.filter(completion => completion.exercise === exercise && completion.phraseListId === phraseListId);
359
+ completions = data?.completions.filter(completion => completion.exercise === exercise && (completion.phraseListId === phraseListId || !completion.phraseListId && phraseListId?.startsWith('leg-')));
360
360
  } else {
361
361
  completions = data?.completions.filter(completion => completion.exercise === exercise);
362
362
  }
@@ -474,7 +474,7 @@ export function PhraseListCellData({
474
474
  index: index
475
475
  });
476
476
  const phraseListExercises = ["translation", "listening", "pronunciation"];
477
- const completedCount = phraseListExercises.filter(exercise => data?.completions?.some(c => c.exercise === exercise && c.phraseListId === phraseListId)).length;
477
+ const completedCount = phraseListExercises.filter(exercise => data?.completions?.some(c => c.exercise === exercise && (c.phraseListId === phraseListId || !c.phraseListId && phraseListId?.startsWith('leg-')))).length;
478
478
  const total = phraseListExercises.length;
479
479
  let color;
480
480
  if (completedCount === total) color = green[400];else if (completedCount > 0) color = yellow[400];else color = red[400];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nualang/nualang-ui-components",
3
- "version": "0.1.1357",
3
+ "version": "0.1.1360",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "files": [