@nualang/nualang-ui-components 0.1.1336 → 0.1.1337

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.
Files changed (40) hide show
  1. package/dist/Cards/Course/Course.js +1 -1
  2. package/dist/Cards/FeedbackCard/FeedbackCard.js +18 -18
  3. package/dist/Cards/RecordingCard/RecordingCard.js +4 -4
  4. package/dist/Chat/Chat.js +2 -2
  5. package/dist/Chat/ChatLoading/ChatLoading.js +2 -2
  6. package/dist/Chat/Messages/Message/Message.js +2 -2
  7. package/dist/Dialogs/AvatarDialog/AvatarDialog.js +5 -5
  8. package/dist/Dialogs/GenerateBot/GenerateBot.js +2 -2
  9. package/dist/Dialogs/GroupedFeedbackDialog/GroupedFeedbackDialog.js +2 -2
  10. package/dist/Dialogs/SelectClassroom/SelectClassroom.js +1 -1
  11. package/dist/Dialogs/UploadBotVars/UploadBotVars.js +4 -4
  12. package/dist/Dialogs/UploadPhrases/UploadPhrases.js +11 -11
  13. package/dist/Dialogs/UploadRoleplayScript/UploadRoleplayScript.js +15 -15
  14. package/dist/Editors/Phrases/Phrases.js +2 -2
  15. package/dist/Exercises/Bot/Bot.js +3 -3
  16. package/dist/Exercises/Roleplay/Games/ActItOutGame/package.json +6 -6
  17. package/dist/Exercises/Roleplay/Games/ActItOutGameListening/package.json +6 -6
  18. package/dist/Forms/CreateClassroom/CreateClassroom.js +1 -1
  19. package/dist/Forms/CreateClassroom/Steps/ClassroomSettings/ClassroomSettings.js +3 -3
  20. package/dist/Forms/CreateClassroom/Steps/CourseSettings/CourseSettings.js +2 -2
  21. package/dist/Forms/CreateMeetingMultiStepForm/GroupAssignment.js +14 -14
  22. package/dist/Forms/UpdateClassroom/UpdateClassroom.js +1 -1
  23. package/dist/Misc/AxiosRestExample/AxiosRestExample.js +3 -3
  24. package/dist/Misc/AxiosRestExample/AxiosRestPostExample.js +6 -6
  25. package/dist/Misc/DiscussionTopicSelector/package.json +6 -7
  26. package/dist/Misc/ExerciseBottomBar/ExerciseBottomBar.js +1 -1
  27. package/dist/Misc/HtmlTooltipComponent/package.json +6 -7
  28. package/dist/Screens/Activity/Exercise/Attempt/Attempt.js +1 -1
  29. package/dist/Screens/Classrooms/ViewClassroom/ViewClassroom.js +2 -2
  30. package/dist/Tables/MeetingPrompstList/MeetingPromptsList.js +2 -2
  31. package/dist/Tables/Progress/ProgressList.js +3 -3
  32. package/dist/Tables/Progress/ProgressTable.js +18 -8
  33. package/dist/Tables/Progress/utils.js +1 -0
  34. package/dist/Tables/RecordingListCards/RecordingListCards.js +1 -1
  35. package/dist/hooks/useExerciseState.js +1 -1
  36. package/dist/hooks/useRecognition.js +6 -6
  37. package/dist/hooks/useSpreadsheetState.js +7 -7
  38. package/dist/utils/canvasUtils.js +5 -5
  39. package/dist/utils/index.js +8 -8
  40. package/package.json +1 -1
@@ -16,7 +16,7 @@ import PictureAsPdfIcon from "@mui/icons-material/PictureAsPdf";
16
16
  import DifficultyIcon from "@mui/icons-material/School";
17
17
  import VisibilityIcon from "@mui/icons-material/Visibility";
18
18
  import VisibilityOff from "@mui/icons-material/VisibilityOff";
19
- import DeleteIcon from '@mui/icons-material/Delete';
19
+ import DeleteIcon from "@mui/icons-material/Delete";
20
20
 
21
21
  // components
22
22
  import MembersDialog from "../../Dialogs/Members/Members";
@@ -25,20 +25,20 @@ const getPerformanceLevel = (student, aiGrade) => {
25
25
  };
26
26
  const getPerformanceChipProps = (level, t) => {
27
27
  switch (level?.toLowerCase()) {
28
- case 'above':
28
+ case "above":
29
29
  return {
30
- label: t('above_level'),
31
- color: 'success',
30
+ label: t("above_level"),
31
+ color: "success",
32
32
  icon: /*#__PURE__*/_jsx(TrendingUpIcon, {
33
33
  sx: {
34
34
  fontSize: 16
35
35
  }
36
36
  })
37
37
  };
38
- case 'below':
38
+ case "below":
39
39
  return {
40
- label: t('below_level'),
41
- color: 'warning',
40
+ label: t("below_level"),
41
+ color: "warning",
42
42
  icon: /*#__PURE__*/_jsx(TrendingDownIcon, {
43
43
  sx: {
44
44
  fontSize: 16
@@ -448,21 +448,21 @@ export default function FeedbackCard({
448
448
  sx: {
449
449
  mb: 0.5
450
450
  },
451
- children: t('ai_performance_assessment')
451
+ children: t("ai_performance_assessment")
452
452
  }), /*#__PURE__*/_jsx(Typography, {
453
453
  variant: "caption",
454
454
  sx: {
455
- display: 'block',
455
+ display: "block",
456
456
  mb: 0.5
457
457
  },
458
- children: t('performance_chip_tooltip')
458
+ children: t("performance_chip_tooltip")
459
459
  }), /*#__PURE__*/_jsx(Typography, {
460
460
  variant: "caption",
461
461
  sx: {
462
- fontStyle: 'italic',
462
+ fontStyle: "italic",
463
463
  opacity: 0.9
464
464
  },
465
- children: t('performance_chip_note')
465
+ children: t("performance_chip_note")
466
466
  })]
467
467
  }),
468
468
  placement: "top",
@@ -472,15 +472,15 @@ export default function FeedbackCard({
472
472
  size: "small",
473
473
  icon: chipProps.icon,
474
474
  sx: {
475
- width: 'fit-content',
475
+ width: "fit-content",
476
476
  fontWeight: 600,
477
- fontSize: '0.7rem',
478
- height: '22px',
479
- '& .MuiChip-label': {
480
- paddingLeft: '8px',
481
- paddingRight: '8px'
477
+ fontSize: "0.7rem",
478
+ height: "22px",
479
+ "& .MuiChip-label": {
480
+ paddingLeft: "8px",
481
+ paddingRight: "8px"
482
482
  },
483
- '&:hover': {
483
+ "&:hover": {
484
484
  opacity: 0.9
485
485
  }
486
486
  }
@@ -8,10 +8,10 @@ import { Card, CardActionArea, Divider, Button, Tooltip, IconButton } from "@mui
8
8
  import PropTypes from "prop-types";
9
9
  import dayjs from "dayjs";
10
10
  import PDFViewer from "../../Dialogs/PDFViewer/PDFViewer";
11
- import level1Rubric from '../../Dialogs/PDFViewer/rubrics/level1Rubric.pdf';
12
- import level2Rubric from '../../Dialogs/PDFViewer/rubrics/level2Rubric.pdf';
13
- import level3Rubric from '../../Dialogs/PDFViewer/rubrics/level3Rubric.pdf';
14
- import level4Rubric from '../../Dialogs/PDFViewer/rubrics/level4Rubric.pdf';
11
+ import level1Rubric from "../../Dialogs/PDFViewer/rubrics/level1Rubric.pdf";
12
+ import level2Rubric from "../../Dialogs/PDFViewer/rubrics/level2Rubric.pdf";
13
+ import level3Rubric from "../../Dialogs/PDFViewer/rubrics/level3Rubric.pdf";
14
+ import level4Rubric from "../../Dialogs/PDFViewer/rubrics/level4Rubric.pdf";
15
15
  import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
16
16
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
17
17
  export default function RecordingCard({
package/dist/Chat/Chat.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { memo } from "react";
2
- import { useState, useEffect } from 'react';
2
+ import { useState, useEffect } from "react";
3
3
  import { makeStyles } from "tss-react/mui";
4
4
  import TopBar from "./TopBar/TopBar";
5
5
  import Messages from "./Messages/Messages";
@@ -106,7 +106,7 @@ function Chat({
106
106
  setIsMessageLoading(false);
107
107
  return;
108
108
  }
109
- const hasLoadingMessage = messages.some(message => typeof message === 'object' && message?.isLoading === true);
109
+ const hasLoadingMessage = messages.some(message => typeof message === "object" && message?.isLoading === true);
110
110
  setIsMessageLoading(hasLoadingMessage);
111
111
  }, [messages]);
112
112
  return /*#__PURE__*/_jsxs("div", {
@@ -151,7 +151,7 @@ export default function ChatLoading({
151
151
  children: /*#__PURE__*/_jsxs("div", {
152
152
  className: classes.message,
153
153
  children: [userImage ? /*#__PURE__*/_jsx(_Fragment, {
154
- children: !isMyMessage && isDynamicResponsesEnabled === 'enable' ? /*#__PURE__*/_jsxs("div", {
154
+ children: !isMyMessage && isDynamicResponsesEnabled === "enable" ? /*#__PURE__*/_jsxs("div", {
155
155
  style: {
156
156
  position: "relative",
157
157
  display: "inline-block"
@@ -161,7 +161,7 @@ export default function ChatLoading({
161
161
  src: userImage,
162
162
  alt: ""
163
163
  }), /*#__PURE__*/_jsx(AiExperienceIcon, {
164
- aiExperienceInfoText: t('ai_chatbot_message_generating_info'),
164
+ aiExperienceInfoText: t("ai_chatbot_message_generating_info"),
165
165
  sx: {
166
166
  bottom: isSmallScreen ? "23px" : "23px",
167
167
  right: isSmallScreen ? "-11px" : "-21px",
@@ -379,7 +379,7 @@ export default function Message({
379
379
  children: /*#__PURE__*/_jsxs("div", {
380
380
  className: classes.message,
381
381
  children: [userImage ? /*#__PURE__*/_jsx(_Fragment, {
382
- children: !isMyMessage && isDynamicResponsesEnabled === 'enable' ? /*#__PURE__*/_jsxs("div", {
382
+ children: !isMyMessage && isDynamicResponsesEnabled === "enable" ? /*#__PURE__*/_jsxs("div", {
383
383
  style: {
384
384
  position: "relative",
385
385
  display: "inline-block"
@@ -389,7 +389,7 @@ export default function Message({
389
389
  src: userImage,
390
390
  alt: ""
391
391
  }), /*#__PURE__*/_jsx(AiExperienceIcon, {
392
- aiExperienceInfoText: t('ai_chatbot_message_info'),
392
+ aiExperienceInfoText: t("ai_chatbot_message_info"),
393
393
  isCurrentMessagePlaying: isCurrentMessagePlaying,
394
394
  sx: {
395
395
  bottom: isSmallScreen ? "23px" : "23px",
@@ -355,11 +355,11 @@ export default function AvatarDialog({
355
355
  console.error("Avatar ref is not attached to a DOM node.");
356
356
  return;
357
357
  }
358
- /*
359
- We should switch this but we need to
360
- have the Avatar component pass the ref to DOM
361
- so we can grab the outerHTML
362
- const svgNode = avatarRef.current;
358
+ /*
359
+ We should switch this but we need to
360
+ have the Avatar component pass the ref to DOM
361
+ so we can grab the outerHTML
362
+ const svgNode = avatarRef.current;
363
363
  */
364
364
  const canvas = canvasRef.current;
365
365
  const ctx = canvas.getContext("2d");
@@ -18,7 +18,7 @@ function validateResponse(response, template) {
18
18
  if (typeof item !== "object" || item === null || Array.isArray(item)) {
19
19
  return false;
20
20
  }
21
- if (template === 'nualang.quiz.0.0.1') {
21
+ if (template === "nualang.quiz.0.0.1") {
22
22
  if (!Object.hasOwn(item, "question") || !Object.hasOwn(item, "answer")) {
23
23
  return false;
24
24
  }
@@ -29,7 +29,7 @@ function validateResponse(response, template) {
29
29
  return false;
30
30
  }
31
31
  }
32
- if (template === 'nualang.open-ended.0.0.1' && !Object.hasOwn(item, "text")) {
32
+ if (template === "nualang.open-ended.0.0.1" && !Object.hasOwn(item, "text")) {
33
33
  return false;
34
34
  }
35
35
  }
@@ -130,11 +130,11 @@ export default function GroupFeedbackDialog({
130
130
  const user = userData.find(u => u.memberId === memberId);
131
131
  return {
132
132
  memberId: memberId,
133
- name: user?.username || user?.createdByName || 'Unknown',
133
+ name: user?.username || user?.createdByName || "Unknown",
134
134
  username: user?.username,
135
135
  createdByName: user?.createdByName
136
136
  };
137
- }).filter(member => member.name !== 'Unknown');
137
+ }).filter(member => member.name !== "Unknown");
138
138
  };
139
139
  const handleGenerateFeedback = async discussionData => {
140
140
  if (handleBatchGradeConversations) {
@@ -55,7 +55,7 @@ function SelectClassroom({
55
55
  component: RouterLink,
56
56
  to: path,
57
57
  sx: {
58
- color: 'inherit'
58
+ color: "inherit"
59
59
  },
60
60
  children: [/*#__PURE__*/_jsx(ListItemAvatar, {
61
61
  children: picture ? /*#__PURE__*/_jsx(Avatar, {
@@ -90,14 +90,14 @@ function UploadBotVars({
90
90
  useEffect(() => {
91
91
  const handleUploadCsvDataText = async () => {
92
92
  setErrors([]);
93
- const lines = csvDataInput.trim().split('\n');
94
- const headers = lines[0].split(',').map(h => h.trim());
93
+ const lines = csvDataInput.trim().split("\n");
94
+ const headers = lines[0].split(",").map(h => h.trim());
95
95
  const rows = lines.slice(1);
96
96
  const jsonArray = rows.map(row => {
97
- const values = row.match(/(".*?"|[^",\n]+)(?=\s*,|\s*$)/g).map(v => v.replace(/^"|"$/g, '').trim());
97
+ const values = row.match(/(".*?"|[^",\n]+)(?=\s*,|\s*$)/g).map(v => v.replace(/^"|"$/g, "").trim());
98
98
  const entry = {};
99
99
  headers.forEach((header, i) => {
100
- entry[header] = values[i] ?? '';
100
+ entry[header] = values[i] ?? "";
101
101
  });
102
102
  return entry;
103
103
  });
@@ -87,12 +87,12 @@ function parsePhraseWithAlternatives(phraseString) {
87
87
  };
88
88
  }
89
89
 
90
- /**
91
- * Processes CSV data with the following format:
92
- * - phrase: Primary phrase with optional alternatives separated by '|'
93
- * Example: "Hello|Hi|Hey"
94
- * - translations: Multiple translations separated by '|'
95
- * - image: Optional image URL
90
+ /**
91
+ * Processes CSV data with the following format:
92
+ * - phrase: Primary phrase with optional alternatives separated by '|'
93
+ * Example: "Hello|Hi|Hey"
94
+ * - translations: Multiple translations separated by '|'
95
+ * - image: Optional image URL
96
96
  */
97
97
 
98
98
  function UploadPhrases({
@@ -134,7 +134,7 @@ function UploadPhrases({
134
134
  primary,
135
135
  alternatives
136
136
  } = parsePhraseWithAlternatives(data.phrase);
137
- const formattedTranslations = data.translations.split("|").map(t => t.trim().replace(/^"|"$/g, '').trim());
137
+ const formattedTranslations = data.translations.split("|").map(t => t.trim().replace(/^"|"$/g, "").trim());
138
138
  return {
139
139
  phrase: primary,
140
140
  alternativeVersions: alternatives,
@@ -150,14 +150,14 @@ function UploadPhrases({
150
150
  useEffect(() => {
151
151
  const handleUploadCsvDataText = async () => {
152
152
  setErrors([]);
153
- const lines = csvDataInput.trim().split('\n');
154
- const headers = lines[0].split(',').map(h => h.trim());
153
+ const lines = csvDataInput.trim().split("\n");
154
+ const headers = lines[0].split(",").map(h => h.trim());
155
155
  const rows = lines.slice(1);
156
156
  const jsonArray = rows.map(row => {
157
- const values = row.match(/(".*?"|[^",\n]+)(?=\s*,|\s*$)/g).map(v => v.replace(/^"|"$/g, '').trim());
157
+ const values = row.match(/(".*?"|[^",\n]+)(?=\s*,|\s*$)/g).map(v => v.replace(/^"|"$/g, "").trim());
158
158
  const entry = {};
159
159
  headers.forEach((header, i) => {
160
- entry[header] = values[i] ?? '';
160
+ entry[header] = values[i] ?? "";
161
161
  });
162
162
  return entry;
163
163
  });
@@ -62,8 +62,8 @@ function UploadRoleplayScript({
62
62
  const row = index + 2;
63
63
  let parseErrors = [];
64
64
 
65
- /**
66
- * Checks if row is an actor or question
65
+ /**
66
+ * Checks if row is an actor or question
67
67
  */
68
68
  if (!Object.prototype.hasOwnProperty.call(data, "actor") && !Object.prototype.hasOwnProperty.call(data, "question")) {
69
69
  parseErrors.push({
@@ -72,10 +72,10 @@ function UploadRoleplayScript({
72
72
  });
73
73
  }
74
74
 
75
- /**
76
- * Checks if a actor text object has the following:
77
- * - text exists
78
- * - actor attribute is a number e.g. actor1, actor2
75
+ /**
76
+ * Checks if a actor text object has the following:
77
+ * - text exists
78
+ * - actor attribute is a number e.g. actor1, actor2
79
79
  */
80
80
  if (Object.prototype.hasOwnProperty.call(data, "actor")) {
81
81
  if (!data.text) {
@@ -92,11 +92,11 @@ function UploadRoleplayScript({
92
92
  }
93
93
  }
94
94
 
95
- /**
96
- * Checks if a question object has the following:
97
- * - question text
98
- * - answers exists
99
- * - correct answer(s) exists
95
+ /**
96
+ * Checks if a question object has the following:
97
+ * - question text
98
+ * - answers exists
99
+ * - correct answer(s) exists
100
100
  */
101
101
  if (Object.prototype.hasOwnProperty.call(data, "question")) {
102
102
  if (!data?.question) {
@@ -190,14 +190,14 @@ function UploadRoleplayScript({
190
190
  useEffect(() => {
191
191
  const handleUploadCsvDataText = async () => {
192
192
  setErrors([]);
193
- const lines = csvDataInput.trim().split('\n');
194
- const headers = lines[0].split(',').map(h => h.trim());
193
+ const lines = csvDataInput.trim().split("\n");
194
+ const headers = lines[0].split(",").map(h => h.trim());
195
195
  const rows = lines.slice(1);
196
196
  const jsonArray = rows.map(row => {
197
- const values = row.match(/(".*?"|[^",\n]+)(?=\s*,|\s*$)/g).map(v => v.replace(/^"|"$/g, '').trim());
197
+ const values = row.match(/(".*?"|[^",\n]+)(?=\s*,|\s*$)/g).map(v => v.replace(/^"|"$/g, "").trim());
198
198
  const entry = {};
199
199
  headers.forEach((header, i) => {
200
- entry[header] = values[i] ?? '';
200
+ entry[header] = values[i] ?? "";
201
201
  });
202
202
  return entry;
203
203
  });
@@ -265,8 +265,8 @@ function PhrasesEditor(props) {
265
265
  setSpreadsheetPhrases(JSON.parse(phrasesJson));
266
266
  }, [phrasesJson]);
267
267
 
268
- /**
269
- * Validates each row for missing phrase or translation
268
+ /**
269
+ * Validates each row for missing phrase or translation
270
270
  */
271
271
  const validateRow = useCallback((phrase, translation, index) => {
272
272
  const row = index + 1;
@@ -413,9 +413,9 @@ function Bot({
413
413
  navigate(`/classrooms/${classroomId}#Assignments`);
414
414
  };
415
415
 
416
- /**
417
- * searchParams not showing new results so instruction dialog won't open
418
- * when bot opens
416
+ /**
417
+ * searchParams not showing new results so instruction dialog won't open
418
+ * when bot opens
419
419
  */
420
420
  // useEffect(() => {
421
421
  // console.log('in use effect [searchParams.botId, searchParams.instructions]', searchParams);
@@ -1,6 +1,6 @@
1
- {
2
- "name": "ActItOutGame",
3
- "version": "0.0.0",
4
- "private": true,
5
- "main": "./ActItOutGame.jsx"
6
- }
1
+ {
2
+ "name": "ActItOutGame",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "main": "./ActItOutGame.jsx"
6
+ }
@@ -1,6 +1,6 @@
1
- {
2
- "name": "ActItOutGameListening",
3
- "version": "0.0.0",
4
- "private": true,
5
- "main": "./ActItOutGameListening.jsx"
6
- }
1
+ {
2
+ "name": "ActItOutGameListening",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "main": "./ActItOutGameListening.jsx"
6
+ }
@@ -30,7 +30,7 @@ function CreateClassroom({
30
30
  visibility: "private",
31
31
  vchatEnabled: true,
32
32
  vchatEnabledInSettings: true,
33
- isDynamicResponsesEnabled: 'enabled',
33
+ isDynamicResponsesEnabled: "enabled",
34
34
  enrolmentKey: "",
35
35
  allowedDomains: [],
36
36
  collaborators: [],
@@ -308,18 +308,18 @@ function ClassroomSettings({
308
308
  row: true,
309
309
  "aria-label": "isDynamicResponsesEnabled",
310
310
  name: "isDynamicResponsesEnabled",
311
- value: isDynamicResponsesEnabled || 'enabled',
311
+ value: isDynamicResponsesEnabled || "enabled",
312
312
  onChange: handleChange,
313
313
  onBlur: handleBlur,
314
314
  helperText: touched.isDynamicResponsesEnabled ? errors.isDynamicResponsesEnabled : "",
315
315
  error: touched.isDynamicResponsesEnabled && Boolean(errors.isDynamicResponsesEnabled),
316
316
  children: [/*#__PURE__*/_jsx(FormControlLabel, {
317
- value: 'enabled',
317
+ value: "enabled",
318
318
  control: /*#__PURE__*/_jsx(Radio, {}),
319
319
  label: t("enabled"),
320
320
  "data-cy": "enable-dynamic-responses-in-settings"
321
321
  }), /*#__PURE__*/_jsx(FormControlLabel, {
322
- value: 'disabled',
322
+ value: "disabled",
323
323
  control: /*#__PURE__*/_jsx(Radio, {}),
324
324
  label: t("disabled"),
325
325
  "data-cy": "disable-dynamic-responses-in-settings"
@@ -194,7 +194,7 @@ function Settings({
194
194
  row: true,
195
195
  "aria-label": "Dynamic Responses",
196
196
  name: `courseSettings.${courseId}.isDynamicResponsesEnabled`,
197
- value: classroomIsDynamicResponsesEnabled === 'disabled' ? 'disabled' : thisCoursesSettings.isDynamicResponsesEnabled || "default",
197
+ value: classroomIsDynamicResponsesEnabled === "disabled" ? "disabled" : thisCoursesSettings.isDynamicResponsesEnabled || "default",
198
198
  onChange: event => {
199
199
  const newCourseSettings = {
200
200
  ...courseSettings,
@@ -212,7 +212,7 @@ function Settings({
212
212
  value: "default",
213
213
  control: /*#__PURE__*/_jsx(Radio, {}),
214
214
  label: t("course_default"),
215
- disabled: classroomIsDynamicResponsesEnabled === 'disabled'
215
+ disabled: classroomIsDynamicResponsesEnabled === "disabled"
216
216
  }), /*#__PURE__*/_jsx(FormControlLabel, {
217
217
  value: "disabled",
218
218
  control: /*#__PURE__*/_jsx(Radio, {}),
@@ -20,7 +20,7 @@ import { DndContext, DragOverlay, closestCenter, KeyboardSensor, PointerSensor,
20
20
  import { SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, useSortable } from "@dnd-kit/sortable";
21
21
  import { CSS } from "@dnd-kit/utilities";
22
22
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
23
- const accentColors = ['#6366f1', '#8b5cf6', '#06b6d4', '#22c55e', '#f97316', '#ec4899'];
23
+ const accentColors = ["#6366f1", "#8b5cf6", "#06b6d4", "#22c55e", "#f97316", "#ec4899"];
24
24
  const MAX_GROUP_SIZE = 4;
25
25
  const groupByN = (n, arr) => {
26
26
  arr = arr.sort(() => Math.random() - 0.5);
@@ -206,7 +206,7 @@ function NewGroupDropZone({
206
206
  const {
207
207
  setNodeRef
208
208
  } = useDroppable({
209
- id: 'new-group-drop-zone'
209
+ id: "new-group-drop-zone"
210
210
  });
211
211
  return /*#__PURE__*/_jsx(Card, {
212
212
  ref: setNodeRef,
@@ -515,14 +515,14 @@ function GroupAssignment({
515
515
  setOverNewGroup(false);
516
516
  return;
517
517
  }
518
- if (over.id === 'new-group-drop-zone') {
518
+ if (over.id === "new-group-drop-zone") {
519
519
  setOverNewGroup(true);
520
520
  setOverGroupIndex(null);
521
521
  return;
522
522
  }
523
523
  setOverNewGroup(false);
524
- const activeMemberIndex = parseInt(active.id.replace('member-', ''));
525
- const overMemberIndex = parseInt(over.id.replace('member-', ''));
524
+ const activeMemberIndex = parseInt(active.id.replace("member-", ""));
525
+ const overMemberIndex = parseInt(over.id.replace("member-", ""));
526
526
  const activeGroupIndex = findGroupIndex(activeMemberIndex);
527
527
  const overGroupIdx = findGroupIndex(overMemberIndex);
528
528
  if (activeGroupIndex !== overGroupIdx && overGroupIdx !== -1) {
@@ -540,8 +540,8 @@ function GroupAssignment({
540
540
  setOverGroupIndex(null);
541
541
  setOverNewGroup(false);
542
542
  if (!over) return;
543
- const activeMemberIndex = parseInt(active.id.replace('member-', ''));
544
- if (over.id === 'new-group-drop-zone') {
543
+ const activeMemberIndex = parseInt(active.id.replace("member-", ""));
544
+ if (over.id === "new-group-drop-zone") {
545
545
  const sourceGroupIndex = findGroupIndex(activeMemberIndex);
546
546
  if (sourceGroupIndex === -1) return;
547
547
  const newGroups = [...groups];
@@ -558,7 +558,7 @@ function GroupAssignment({
558
558
  });
559
559
  return;
560
560
  }
561
- const overMemberIndex = parseInt(over.id.replace('member-', ''));
561
+ const overMemberIndex = parseInt(over.id.replace("member-", ""));
562
562
  const sourceGroupIndex = findGroupIndex(activeMemberIndex);
563
563
  const destGroupIndex = findGroupIndex(overMemberIndex);
564
564
  if (sourceGroupIndex === -1 || destGroupIndex === -1) return;
@@ -615,7 +615,7 @@ function GroupAssignment({
615
615
  };
616
616
  const getActiveMember = () => {
617
617
  if (!activeId) return null;
618
- const memberIndex = parseInt(activeId.replace('member-', ''));
618
+ const memberIndex = parseInt(activeId.replace("member-", ""));
619
619
  return members[memberIndex];
620
620
  };
621
621
  return /*#__PURE__*/_jsxs(DndContext, {
@@ -666,7 +666,7 @@ function GroupAssignment({
666
666
  variant: "outlined",
667
667
  size: "small",
668
668
  sx: {
669
- minWidth: '100%'
669
+ minWidth: "100%"
670
670
  },
671
671
  children: [/*#__PURE__*/_jsx(InputLabel, {
672
672
  id: "pref-group-label",
@@ -724,7 +724,7 @@ function GroupAssignment({
724
724
  value,
725
725
  index,
726
726
  username: members[index]?.username
727
- })).sort((a, b) => (a.username || '').localeCompare(b.username || '')).map(({
727
+ })).sort((a, b) => (a.username || "").localeCompare(b.username || "")).map(({
728
728
  value,
729
729
  index
730
730
  }) => {
@@ -792,10 +792,10 @@ function GroupAssignment({
792
792
  },
793
793
  sx: {
794
794
  display: {
795
- xs: 'none',
796
- md: 'flex'
795
+ xs: "none",
796
+ md: "flex"
797
797
  },
798
- justifyContent: 'center'
798
+ justifyContent: "center"
799
799
  },
800
800
  children: /*#__PURE__*/_jsx(Divider, {
801
801
  orientation: "vertical",
@@ -27,7 +27,7 @@ export default function UpdateClassroom({
27
27
  tags: [],
28
28
  picture: null,
29
29
  courseSettings: {},
30
- isDynamicResponsesEnabled: 'enabled'
30
+ isDynamicResponsesEnabled: "enabled"
31
31
  },
32
32
  onSubmit,
33
33
  enableReinitialize,
@@ -1,8 +1,8 @@
1
1
  import Axios from "axios";
2
2
  import { ReactQuery } from "@nualang/nualang-api-and-queries/Queries";
3
- /**
4
- * Component to demonstrate how to mock data (GET request) with react-query and axios
5
- *
3
+ /**
4
+ * Component to demonstrate how to mock data (GET request) with react-query and axios
5
+ *
6
6
  */
7
7
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
8
8
  const {
@@ -6,9 +6,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  const {
7
7
  useMutation
8
8
  } = ReactQuery;
9
- /**
10
- * function to send something to the url
11
- * @param {String} textValue
9
+ /**
10
+ * function to send something to the url
11
+ * @param {String} textValue
12
12
  */
13
13
  async function SendMessage({
14
14
  textValue
@@ -25,9 +25,9 @@ async function SendMessage({
25
25
  }
26
26
  }
27
27
 
28
- /**
29
- * Component to demonstrate how to mock data (POST request) with react-query and axios
30
- * @param {String} initialvalue a dummy prop for the component
28
+ /**
29
+ * Component to demonstrate how to mock data (POST request) with react-query and axios
30
+ * @param {String} initialvalue a dummy prop for the component
31
31
  */
32
32
  const AxiosRestPostExample = ({
33
33
  initialvalue = "Ricky Sanchez"
@@ -1,7 +1,6 @@
1
- {
2
- "name": "TopicSelector",
3
- "version": "0.0.0",
4
- "private": true,
5
- "main": "./TopicSelector.js"
6
- }
7
-
1
+ {
2
+ "name": "TopicSelector",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "main": "./TopicSelector.js"
6
+ }
@@ -10,7 +10,7 @@ const useStyles = makeStyles()(theme => ({
10
10
  width: "100%",
11
11
  position: "sticky",
12
12
  bottom: 0,
13
- backgroundColor: theme.palette.mode === 'dark' ? "#525252" : "white"
13
+ backgroundColor: theme.palette.mode === "dark" ? "#525252" : "white"
14
14
  }
15
15
  }));
16
16
  function ExerciseBottomBar({
@@ -1,7 +1,6 @@
1
- {
2
- "name": "HtmlTooltipComponent",
3
- "version": "0.0.0",
4
- "private": true,
5
- "main": "./HtmlTooltipComponent.js"
6
- }
7
-
1
+ {
2
+ "name": "HtmlTooltipComponent",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "main": "./HtmlTooltipComponent.js"
6
+ }
@@ -491,7 +491,7 @@ export default function Attempt({
491
491
  cursor: "pointer" // make it look clickable
492
492
  },
493
493
  onClick: scrollToMessages,
494
- children: ["\u26A0 ", t('student_sent_inappropriate_messages')]
494
+ children: ["\u26A0 ", t("student_sent_inappropriate_messages")]
495
495
  }), (picture || topicName || description) && /*#__PURE__*/_jsxs("div", {
496
496
  className: classes.header,
497
497
  children: [picture ? /*#__PURE__*/_jsx(Avatar, {
@@ -580,8 +580,8 @@ function Classroom({
580
580
  }
581
581
  const removeCourseSectionTopicIdFromUrl = () => {
582
582
  const url = new URL(window.location);
583
- url.searchParams.delete('courseSectionTopicId');
584
- window.history.replaceState({}, '', url);
583
+ url.searchParams.delete("courseSectionTopicId");
584
+ window.history.replaceState({}, "", url);
585
585
  };
586
586
  useEffect(() => {
587
587
  if (createLiveGame && isNualangLiveEnabled) {
@@ -263,8 +263,8 @@ const MeetingPromptsList = withStyles(({
263
263
  src: TeacherCreate,
264
264
  alt: "teacher-create",
265
265
  style: {
266
- width: '100%',
267
- height: 'auto'
266
+ width: "100%",
267
+ height: "auto"
268
268
  }
269
269
  })
270
270
  })]
@@ -56,9 +56,9 @@ function LinearProgressWithLabel(props) {
56
56
  });
57
57
  }
58
58
  LinearProgressWithLabel.propTypes = {
59
- /**
60
- * The value of the progress indicator for the determinate and buffer variants.
61
- * Value between 0 and 100.
59
+ /**
60
+ * The value of the progress indicator for the determinate and buffer variants.
61
+ * Value between 0 and 100.
62
62
  */
63
63
  value: PropTypes.number.isRequired
64
64
  };
@@ -197,6 +197,7 @@ function DataCell({
197
197
  variant: "outlined",
198
198
  sx: theme => ({
199
199
  color: theme.palette.common.black,
200
+ borderColor: theme.palette.common.black,
200
201
  fontWeight: "bold"
201
202
  }),
202
203
  size: "small",
@@ -229,6 +230,7 @@ function ExerciseCell({
229
230
  variant: "outlined",
230
231
  sx: theme => ({
231
232
  color: theme.palette.common.black,
233
+ borderColor: theme.palette.common.black,
232
234
  fontWeight: "bold"
233
235
  }),
234
236
  size: "small",
@@ -529,7 +531,8 @@ function ExerciseCellData({
529
531
  botId,
530
532
  index,
531
533
  isChallengeModeStudent = false,
532
- botIsChallengeModeOnly = false
534
+ botIsChallengeModeOnly = false,
535
+ assignmentDueDateMs = null
533
536
  }) {
534
537
  let icon = null;
535
538
  let type = null;
@@ -537,10 +540,6 @@ function ExerciseCellData({
537
540
  let memberActivityLink = null;
538
541
  let submittedLate = false;
539
542
  let daysLate = null;
540
- if (data?.submittedLate.isLate === true) {
541
- submittedLate = true;
542
- daysLate = data.submittedLate.daysLate;
543
- }
544
543
  if (isLoading) {
545
544
  return /*#__PURE__*/_jsx(DataCell, {
546
545
  t: t,
@@ -607,6 +606,14 @@ function ExerciseCellData({
607
606
  memberActivityLink = `../../courses/${courseId}/${sectionId}/${topicId}/activity/${exercise}/${memberId}/${completions[0].createdAt}`;
608
607
  }
609
608
  }
609
+ if (assignmentDueDateMs && completions && completions.length > 0) {
610
+ const lateCompletion = completions.find(c => c.createdAt > assignmentDueDateMs);
611
+ if (lateCompletion) {
612
+ submittedLate = true;
613
+ const daysDiff = (lateCompletion.createdAt - assignmentDueDateMs) / (1000 * 60 * 60 * 24);
614
+ daysLate = Math.max(1, Math.ceil(daysDiff));
615
+ }
616
+ }
610
617
  }
611
618
  return /*#__PURE__*/_jsx(ExerciseCell, {
612
619
  t: t,
@@ -886,7 +893,8 @@ function TableRow({
886
893
  sectionId: sectionId,
887
894
  topicId: selectedTopic.topicId,
888
895
  memberId: memberId,
889
- index: index * 2 * tableExercises.length + i * 2 + 1
896
+ index: index * 2 * tableExercises.length + i * 2 + 1,
897
+ assignmentDueDateMs: selectedAssignment?.dueDate ? new Date(selectedAssignment.dueDate).getTime() : null
890
898
  }, `student-progress-data-cell-${i}`);
891
899
  }), (currentView === "topic" || currentView === "assignment-topic") && tableRoleplays.map((roleplay, i) => {
892
900
  const [courseId, sectionId] = selectedTopic.courseSectionId.split("|");
@@ -903,7 +911,8 @@ function TableRow({
903
911
  topicId: selectedTopic.topicId,
904
912
  memberId: memberId,
905
913
  roleplayId: roleplay.roleplayId,
906
- index: index * 2 * tableRoleplays.length + i * 2 + 1
914
+ index: index * 2 * tableRoleplays.length + i * 2 + 1,
915
+ assignmentDueDateMs: selectedAssignment?.dueDate ? new Date(selectedAssignment.dueDate).getTime() : null
907
916
  }, `student-progress-data-cell-${i}`);
908
917
  }), (currentView === "topic" || currentView === "assignment-topic") && tableBots.map((bot, i) => {
909
918
  const [courseId, sectionId] = selectedTopic.courseSectionId.split("|");
@@ -922,7 +931,8 @@ function TableRow({
922
931
  botId: bot.botId,
923
932
  botIsChallengeModeOnly: bot?.isChallengeBot,
924
933
  isChallengeModeStudent: isChallengeModeStudent,
925
- index: index * 2 * tableBots.length + i * 2 + 1
934
+ index: index * 2 * tableBots.length + i * 2 + 1,
935
+ assignmentDueDateMs: selectedAssignment?.dueDate ? new Date(selectedAssignment.dueDate).getTime() : null
926
936
  }, `student-progress-data-cell-${i}`);
927
937
  }), (currentView === "roleplay" || currentView === "assignment-roleplay") && tableRoleplayGames.map((roleplay, i) => {
928
938
  const [courseId, sectionId] = selectedTopic.courseSectionId.split("|");
@@ -134,6 +134,7 @@ export const formatMemberCourseCompletions = (courses = [], completions = [], as
134
134
  export const formatMemberAssignmentCompletions = (assignments = [], courses = [], completions = [], isChallengeModeStudent = false) => {
135
135
  const stats = assignments.reduce((obj, assignment) => {
136
136
  const assignmentCompletions = completions.filter(completion => assignment.exercises?.some(e => {
137
+ if (!e.name && !e.roleplayId && !e.botId && !e.courseSectionTopicId) return false;
137
138
  if (e.name && completion.exercise !== e.name) return false;
138
139
  if (e.roleplayId && completion.roleplayId !== e.roleplayId) return false;
139
140
  if (e.botId && completion.botId !== e.botId) return false;
@@ -32,7 +32,7 @@ const RecordingListCards = withStyles(({
32
32
  const [feedbackOpen, setFeedbackOpen] = useState(false);
33
33
  const [cardsToShow, setCardsToShow] = useState(3);
34
34
  const [recordingIdFromUrl, setRecordingIdFromUrl, clearRecordingIdFromUrl] = useUrlQueryParam("meetingId", {
35
- hash: 'Discuss'
35
+ hash: "Discuss"
36
36
  });
37
37
  useEffect(() => {
38
38
  if (recordingIdFromUrl && recordingsTableData.length > 0) {
@@ -1540,7 +1540,7 @@ export default function useExerciseState({
1540
1540
  };
1541
1541
  const handleSubmitBotMessage = async () => {
1542
1542
  try {
1543
- if (botText && botText.trim() !== '') {
1543
+ if (botText && botText.trim() !== "") {
1544
1544
  const method = `${finalTranscript || ""} ${interimTranscript || ""}`.trim() === botText.trim() ? "voice" : "text";
1545
1545
  addBotMessage(botText, true, false, method, false, finalTranscript);
1546
1546
  resetTranscript();
@@ -98,8 +98,8 @@ export default function useRecognition(props = {}) {
98
98
  };
99
99
  }, []);
100
100
 
101
- /**
102
- * Stops recording and closes everything down. Runs on error or on stop.
101
+ /**
102
+ * Stops recording and closes everything down. Runs on error or on stop.
103
103
  */
104
104
  function closeAll(keepSocketAlive) {
105
105
  // Clear the listeners (prevents issue if opening and closing repeatedly)
@@ -140,10 +140,10 @@ export default function useRecognition(props = {}) {
140
140
  }
141
141
  }
142
142
 
143
- /**
144
- * @param {object} gcloudParams Params for gcloud
145
- * @param {function} onData Callback to run on data each time it's received
146
- * @param {function} onError Callback to run on an error if one is emitted.
143
+ /**
144
+ * @param {object} gcloudParams Params for gcloud
145
+ * @param {function} onData Callback to run on data each time it's received
146
+ * @param {function} onError Callback to run on an error if one is emitted.
147
147
  */
148
148
  const initRecording = async (gcloudParams, onData, onError) => {
149
149
  try {
@@ -1,12 +1,12 @@
1
1
  import { useEffect, useState } from "react";
2
2
 
3
- /**
4
- * Custom hook to manage the state and handlers of spreadsheet in react-spreadsheet-grid component
5
- * @param data
6
- * @param rowTemplate
7
- * @param initColumns
8
- * @param isRowEmpty
9
- * @returns {{onCellClick: (function(*): void), setRows: (value: (((prevState: *[]) => *[]) | *[])) => void, columns: unknown, onColumnResize: onColumnResize, selectedRow: unknown, rows: *[], onFieldChange: (function(*, *): function(*): void), isRowEmpty, setSelectedRow: (value: unknown) => void, deleteSelectedRow: deleteSelectedRow}}
3
+ /**
4
+ * Custom hook to manage the state and handlers of spreadsheet in react-spreadsheet-grid component
5
+ * @param data
6
+ * @param rowTemplate
7
+ * @param initColumns
8
+ * @param isRowEmpty
9
+ * @returns {{onCellClick: (function(*): void), setRows: (value: (((prevState: *[]) => *[]) | *[])) => void, columns: unknown, onColumnResize: onColumnResize, selectedRow: unknown, rows: *[], onFieldChange: (function(*, *): function(*): void), isRowEmpty, setSelectedRow: (value: unknown) => void, deleteSelectedRow: deleteSelectedRow}}
10
10
  */
11
11
  export default function useSpreadsheetState({
12
12
  data = [],
@@ -9,11 +9,11 @@ function getRadianAngle(degreeValue) {
9
9
  return degreeValue * Math.PI / 180;
10
10
  }
11
11
 
12
- /**
13
- * This function was adapted from the one in the ReadMe of https://github.com/DominicTobias/react-image-crop
14
- * @param {File} image - Image File url
15
- * @param {Object} pixelCrop - pixelCrop Object provided by react-easy-crop
16
- * @param {number} rotation - optional rotation parameter
12
+ /**
13
+ * This function was adapted from the one in the ReadMe of https://github.com/DominicTobias/react-image-crop
14
+ * @param {File} image - Image File url
15
+ * @param {Object} pixelCrop - pixelCrop Object provided by react-easy-crop
16
+ * @param {number} rotation - optional rotation parameter
17
17
  */
18
18
  export async function getCroppedImg(imageSrc, pixelCrop, rotation = 0) {
19
19
  const image = await createImage(imageSrc);
@@ -389,14 +389,14 @@ export const calcPercentageCompletion = (sections = []) => {
389
389
  return result;
390
390
  };
391
391
 
392
- /**
393
- * Compares an array from old and new props between renders. Used as the validation
394
- * function when using `memo()` on a component
395
- * @param prevProps
396
- * @param nextProps
397
- * @param key The array to compare props e.g. roleplays, courses
398
- * @param idField The unique object ID of an object e.g. courseId, roleplayId
399
- * @returns {this is *[]|boolean}
392
+ /**
393
+ * Compares an array from old and new props between renders. Used as the validation
394
+ * function when using `memo()` on a component
395
+ * @param prevProps
396
+ * @param nextProps
397
+ * @param key The array to compare props e.g. roleplays, courses
398
+ * @param idField The unique object ID of an object e.g. courseId, roleplayId
399
+ * @returns {this is *[]|boolean}
400
400
  */
401
401
  export const propsAreEqual = (prevProps, nextProps, key, idField) => {
402
402
  const oldPropsArray = prevProps[key] || [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nualang/nualang-ui-components",
3
- "version": "0.1.1336",
3
+ "version": "0.1.1337",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "files": [