@nualang/nualang-ui-components 0.1.1276 → 0.1.1277

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.
@@ -158,7 +158,7 @@ function ChatLoading({
158
158
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
159
159
  className: classes.message,
160
160
  children: [userImage ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
161
- children: !isMyMessage && isDynamicResponsesEnabled === 'enable' && isUserInternal ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
161
+ children: !isMyMessage && isDynamicResponsesEnabled === 'enable' ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
162
162
  style: {
163
163
  position: "relative",
164
164
  display: "inline-block"
@@ -27,6 +27,7 @@ var _DragIndicator = _interopRequireDefault(require("@mui/icons-material/DragInd
27
27
  var _Grid = _interopRequireDefault(require("@mui/material/Grid"));
28
28
  var _BorderColor = _interopRequireDefault(require("@mui/icons-material/BorderColor"));
29
29
  var _AiExperienceIcon = _interopRequireDefault(require("../../../Misc/AiExperienceIcon/AiExperienceIcon"));
30
+ var _WarningAmber = _interopRequireDefault(require("@mui/icons-material/WarningAmber"));
30
31
  var _jsxRuntime = require("react/jsx-runtime");
31
32
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
32
33
  const useStyles = (0, _mui.makeStyles)()((theme, {
@@ -40,6 +41,19 @@ const useStyles = (0, _mui.makeStyles)()((theme, {
40
41
  justifyContent: isMyMessage ? isButton || hideAvatar ? "flex-start" : "flex-end" : null,
41
42
  marginBottom: isButton ? null : theme.spacing(3)
42
43
  },
44
+ warningIcon: {
45
+ marginLeft: 8,
46
+ verticalAlign: "middle"
47
+ },
48
+ indicatorWrapper: {
49
+ position: "relative"
50
+ },
51
+ inappropriateIcon: {
52
+ position: "absolute",
53
+ top: 0,
54
+ right: 0,
55
+ margin: 4
56
+ },
43
57
  message: {
44
58
  display: "flex",
45
59
  maxWidth: 500,
@@ -258,7 +272,6 @@ function Message({
258
272
  userImage,
259
273
  name,
260
274
  text,
261
- datetime,
262
275
  imageUrl,
263
276
  disableTranslation,
264
277
  handleTranslate,
@@ -273,7 +286,6 @@ function Message({
273
286
  moveScriptItem,
274
287
  learnLang,
275
288
  forLang,
276
- messageCount,
277
289
  messageIndex,
278
290
  formatUnderscores,
279
291
  isButton,
@@ -301,6 +313,7 @@ function Message({
301
313
  provided,
302
314
  isEdited = false,
303
315
  isDynamicResponsesEnabled,
316
+ studentInappropriate,
304
317
  isUserInternal
305
318
  }) {
306
319
  const theme = (0, _styles.useTheme)();
@@ -373,7 +386,7 @@ function Message({
373
386
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
374
387
  className: classes.message,
375
388
  children: [userImage ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
376
- children: !isMyMessage && isDynamicResponsesEnabled === 'enable' && isUserInternal ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
389
+ children: !isMyMessage && isDynamicResponsesEnabled === 'enable' ? /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
377
390
  style: {
378
391
  position: "relative",
379
392
  display: "inline-block"
@@ -423,13 +436,28 @@ function Message({
423
436
  },
424
437
  spacing: 4,
425
438
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Grid.default, {
426
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Typography.default, {
427
- variant: "subtitle2",
428
- component: "h4",
429
- gutterBottom: true,
430
- fontWeight: "bold",
431
- align: "left",
432
- children: name
439
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
440
+ sx: {
441
+ display: "flex",
442
+ alignItems: "center"
443
+ },
444
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Typography.default, {
445
+ variant: "subtitle2",
446
+ component: "h4",
447
+ gutterBottom: true,
448
+ fontWeight: "bold",
449
+ align: "left",
450
+ children: name
451
+ }), studentInappropriate && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, {
452
+ title: t("inappropriate_language_detected"),
453
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_WarningAmber.default, {
454
+ style: {
455
+ color: theme.palette.getContrastText(theme.palette.primary.main)
456
+ },
457
+ className: classes.warningIcon,
458
+ fontSize: "small"
459
+ })
460
+ })]
433
461
  })
434
462
  }), isEdited && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Grid.default, {
435
463
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, {
@@ -443,6 +471,7 @@ function Message({
443
471
  })
444
472
  })]
445
473
  }), !isListenOnly && /*#__PURE__*/(0, _jsxRuntime.jsx)(_HoverText.default, {
474
+ studentInappropriate: studentInappropriate,
446
475
  handleTranslate: handleTranslate,
447
476
  learnLang: learnLang,
448
477
  forLang: forLang,
@@ -462,36 +491,37 @@ function Message({
462
491
  languageTag: languageTag
463
492
  }), isListenOnly && /*#__PURE__*/(0, _jsxRuntime.jsx)(WaveformImage, {})]
464
493
  }), !isButton && /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
465
- className: classes.bubble,
466
- id: messageContainerId,
467
- style: {
468
- display: "flex",
469
- alignItems: "center",
470
- justifyContent: "space-between"
471
- },
494
+ className: classes.indicatorWrapper,
472
495
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
473
- children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_Grid.default, {
496
+ className: classes.bubble,
497
+ id: messageContainerId,
498
+ style: {
499
+ position: "relative"
500
+ },
501
+ children: [studentInappropriate && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, {
502
+ title: t("inappropriate_language_detected"),
503
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_WarningAmber.default, {
504
+ className: classes.inappropriateIcon,
505
+ fontSize: "small",
506
+ style: {
507
+ color: theme.palette.getContrastText(theme.palette.primary.main)
508
+ }
509
+ })
510
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_Grid.default, {
474
511
  container: true,
475
512
  alignItems: "center",
476
- justifyContent: "space-between" // Ensures space between name & icon
477
- ,
513
+ justifyContent: "space-between",
478
514
  sx: {
479
515
  display: "flex"
480
516
  },
481
517
  spacing: 4,
482
518
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Grid.default, {
483
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Box, {
484
- sx: {
485
- display: "flex",
486
- alignItems: "center"
487
- },
488
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Typography.default, {
489
- variant: "subtitle2",
490
- component: "h4",
491
- fontWeight: "bold",
492
- align: "left",
493
- children: name
494
- })
519
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Typography.default, {
520
+ variant: "subtitle2",
521
+ component: "h4",
522
+ fontWeight: "bold",
523
+ align: "left",
524
+ children: name
495
525
  })
496
526
  }), isEdited && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Grid.default, {
497
527
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, {
@@ -505,6 +535,7 @@ function Message({
505
535
  })
506
536
  })]
507
537
  }), !isListenOnly && /*#__PURE__*/(0, _jsxRuntime.jsx)(_HoverText.default, {
538
+ studentInappropriate: studentInappropriate,
508
539
  handleTranslate: handleTranslate,
509
540
  learnLang: learnLang,
510
541
  forLang: forLang,
@@ -535,29 +566,27 @@ function Message({
535
566
  src: voiceAttempt || audioURL,
536
567
  waveColor: isMyMessage ? theme.palette.grey[400] : theme.palette.grey[600],
537
568
  progressColor: isMyMessage ? theme.palette.getContrastText(theme.palette.primary.main) : theme.palette.primary.main
538
- }), isVoiceAttemptCheckboxIncluded && /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
539
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormGroup, {
540
- className: classes.checkBox,
541
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormControlLabel, {
542
- control: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Checkbox, {
543
- checked: checked,
544
- onChange: handleClickSaveRecordingCheckbox,
545
- inputProps: {
546
- "aria-label": t("save_recording_to_report")
547
- },
548
- name: "saveRecording",
549
- color: "default",
550
- size: "small",
551
- sx: theme => ({
552
- "& svg": {
553
- color: `${isMyMessage ? theme.palette.getContrastText(theme.palette.primary.main) : theme.palette.primary.main} !important`
554
- }
555
- })
556
- }),
557
- label: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Typography.default, {
558
- variant: "body2",
559
- children: t("save_recording_to_report")
569
+ }), isVoiceAttemptCheckboxIncluded && /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormGroup, {
570
+ className: classes.checkBox,
571
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormControlLabel, {
572
+ control: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Checkbox, {
573
+ checked: checked,
574
+ onChange: handleClickSaveRecordingCheckbox,
575
+ inputProps: {
576
+ "aria-label": t("save_recording_to_report")
577
+ },
578
+ name: "saveRecording",
579
+ color: "default",
580
+ size: "small",
581
+ sx: theme => ({
582
+ "& svg": {
583
+ color: `${isMyMessage ? theme.palette.getContrastText(theme.palette.primary.main) : theme.palette.primary.main} !important`
584
+ }
560
585
  })
586
+ }),
587
+ label: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Typography.default, {
588
+ variant: "body2",
589
+ children: t("save_recording_to_report")
561
590
  })
562
591
  })
563
592
  })]
@@ -50,7 +50,6 @@ function Messages({
50
50
  handleTimeUp,
51
51
  t,
52
52
  setCurrentAnswer,
53
- disableHover,
54
53
  isAnswered,
55
54
  setIsAnswered,
56
55
  handleContinue,
@@ -209,6 +208,7 @@ function Messages({
209
208
  siteLanguage: siteLanguage,
210
209
  moveScriptItem: moveScriptItem,
211
210
  isDynamicResponsesEnabled: isDynamicResponsesEnabled,
211
+ studentInappropriate: m?.studentInappropriate,
212
212
  ...m,
213
213
  ...otherProps
214
214
  }, `messy_${i}`)
@@ -263,6 +263,7 @@ function Messages({
263
263
  isLivePlayer: isLivePlayer,
264
264
  siteLanguage: siteLanguage,
265
265
  isDynamicResponsesEnabled: isDynamicResponsesEnabled,
266
+ studentInappropriate: m?.studentInappropriate,
266
267
  isUserInternal: isUserInternal,
267
268
  ...m,
268
269
  ...otherProps
@@ -107,7 +107,8 @@ function Bot({
107
107
  i18nLanguage,
108
108
  isDynamicResponsesEnabled,
109
109
  aiChatbotConversationHistory,
110
- isUserInternal
110
+ isUserInternal,
111
+ handleStudentMisuse
111
112
  }) {
112
113
  const theme = (0, _styles.useTheme)();
113
114
  const {
@@ -195,7 +196,8 @@ function Bot({
195
196
  i18nLanguage,
196
197
  isDynamicResponsesEnabled: isDynamicResponsesClassroomEnabled,
197
198
  aiChatbotConversationHistory,
198
- isUserInternal
199
+ isUserInternal,
200
+ handleStudentMisuse
199
201
  });
200
202
  const {
201
203
  muteSound,
@@ -144,7 +144,7 @@ function BotSettings({
144
144
  })]
145
145
  })]
146
146
  })
147
- }), isUserInternal && /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, {
147
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, {
148
148
  size: 12,
149
149
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, {
150
150
  component: "fieldset",
@@ -175,7 +175,7 @@ function Confirmation({
175
175
  })]
176
176
  })]
177
177
  })]
178
- }), isUserInternal && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Grid, {
178
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Grid, {
179
179
  size: 12,
180
180
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, {
181
181
  gutterBottom: true,
@@ -190,6 +190,14 @@ function Attempt({
190
190
  const [selectedActor, setSelectedActor] = (0, _react.useState)("");
191
191
  const [showBreakdownTable, setShowBreakdownTable] = (0, _react.useState)(false);
192
192
  const [showMethodTable, setShowMethodTable] = (0, _react.useState)(false);
193
+ const messagesRef = (0, _react.useRef)(null);
194
+ const scrollToMessages = () => {
195
+ if (messagesRef.current) {
196
+ messagesRef.current.scrollIntoView({
197
+ behavior: "smooth"
198
+ });
199
+ }
200
+ };
193
201
  const filteredVarKeys = vars && Object.keys(vars).length > 0 && Object.keys(vars).filter(v => !v.startsWith("__"));
194
202
  const [open, setOpen] = (0, _react.useState)(false);
195
203
  const [isTranscriptPlaying, setIsTranscriptPlaying] = (0, _react.useState)(false);
@@ -469,9 +477,21 @@ function Attempt({
469
477
  stop();
470
478
  setIsTranscriptPlaying(false);
471
479
  };
480
+ const hasInappropriate = (0, _react.useMemo)(() => (messages || []).some(m => m.studentInappropriate), [messages]);
472
481
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
473
482
  className: classes.root,
474
- children: [(picture || topicName || description) && /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
483
+ children: [hasInappropriate && /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
484
+ style: {
485
+ backgroundColor: "#ffcccc",
486
+ padding: "10px",
487
+ color: "#660000",
488
+ fontWeight: "bold",
489
+ border: "1px solid #cc0000",
490
+ cursor: "pointer" // make it look clickable
491
+ },
492
+ onClick: scrollToMessages,
493
+ children: ["\u26A0 ", t('student_sent_inappropriate_messages')]
494
+ }), (picture || topicName || description) && /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
475
495
  className: classes.header,
476
496
  children: [picture ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Avatar, {
477
497
  className: classes.avatarHeader,
@@ -932,6 +952,7 @@ function Attempt({
932
952
  })
933
953
  }), exercise === "bot" && /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, {
934
954
  size: 12,
955
+ ref: messagesRef,
935
956
  children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Card, {
936
957
  classes: {
937
958
  root: classes.card
@@ -499,7 +499,6 @@ function Classroom({
499
499
  progressHelpers,
500
500
  preferred_username,
501
501
  assignedCourses,
502
- assignedStudents,
503
502
  assignmentMembersById,
504
503
  email,
505
504
  ...otherProps
@@ -1090,7 +1089,7 @@ function Classroom({
1090
1089
  py: 1,
1091
1090
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Progress.default, {
1092
1091
  t: t,
1093
- assignedStudents: assignedStudents,
1092
+ assignmentMembersById: assignmentMembersById,
1094
1093
  courses: courses,
1095
1094
  members: members,
1096
1095
  courseIds: courseIds,
@@ -638,7 +638,7 @@ function Topic({
638
638
  };
639
639
  (0, _react.useEffect)(() => {
640
640
  if (isMember && topic && topic.completions) {
641
- const topicwWithCompletion = calcCompletions(topic, topic.completions, bots, roleplays);
641
+ const topicwWithCompletion = (0, _index.calcTopicCompletions)(topic, topic.completions, false);
642
642
  setTopicCompletion(topicwWithCompletion.completion);
643
643
  }
644
644
  }, [topicProgress, isMember, topic, bots, roleplays]);
@@ -164,7 +164,7 @@ function Progress({
164
164
  isVideoChatEnabled,
165
165
  assignedCourses,
166
166
  assignedCourseIds,
167
- assignedStudents
167
+ assignmentMembersById
168
168
  }) {
169
169
  const theme = (0, _styles.useTheme)();
170
170
  const isSmallScreen = (0, _useMediaQuery.default)(theme.breakpoints.down("sm"));
@@ -315,6 +315,7 @@ function Progress({
315
315
  }, [selectedCourse, selectedSection, selectedTopic, selectedRoleplay, selectedAssignment, reportType]); // If selections change, update the url to reflect those changes
316
316
 
317
317
  const [selectedMembers, setSelectedMembers] = (0, _react.useState)([]);
318
+ const assignedStudents = assignmentMembersById[selectedAssignment?.assignmentId] || [];
318
319
  const classroomMembers = (0, _react.useMemo)(() => {
319
320
  return members.filter(member => {
320
321
  if (memberId) {
@@ -72,7 +72,8 @@ function useExerciseState({
72
72
  i18nLanguage,
73
73
  isDynamicResponsesEnabled,
74
74
  aiChatbotConversationHistory,
75
- isUserInternal
75
+ isUserInternal,
76
+ handleStudentMisuse
76
77
  }) {
77
78
  const navigate = (0, _reactRouterDom.useNavigate)();
78
79
  const [questions, setQuestions] = (0, _react.useState)([]);
@@ -1349,9 +1350,10 @@ function useExerciseState({
1349
1350
  setIsWordBankAttemptChanged(false);
1350
1351
  };
1351
1352
  const formatBotMessages = async botMessages => {
1352
- const responses = await Promise.all(botMessages.map(async m => {
1353
+ const responses = await Promise.all(botMessages.map(async (m, i) => {
1353
1354
  const updatedMessage = {
1354
- ...m
1355
+ ...m,
1356
+ studentInappropriate: conversationHistory[i]?.studentInappropriate || false
1355
1357
  };
1356
1358
  if (updatedMessage.audioBlob) {
1357
1359
  delete updatedMessage.audioBlob;
@@ -1542,18 +1544,6 @@ function useExerciseState({
1542
1544
  lng: langCode
1543
1545
  }).toLowerCase() : "hello";
1544
1546
  };
1545
- const getInitialBotGreeting = (learnLang, botName) => {
1546
- const langCode = getLearnLangCode(learnLang);
1547
- if (!langCode) return "";
1548
- const translatedLearnLang = t(learnLang, {
1549
- lng: langCode
1550
- }).toLowerCase();
1551
- return t("initial_bot_greeting", {
1552
- botName,
1553
- learnLang: translatedLearnLang,
1554
- lng: langCode
1555
- });
1556
- };
1557
1547
  const resetConversationHistory = () => {
1558
1548
  setConversationHistory([]);
1559
1549
  setIsFirstMessage(true);
@@ -1573,18 +1563,17 @@ function useExerciseState({
1573
1563
  };
1574
1564
  setBotMessages(prevMessages => [...prevMessages, loadingMessage]);
1575
1565
  const intialStudentGreeting = getInitialStudentGreeting(learnLang);
1576
- const intialBotGreeting = getInitialBotGreeting(learnLang, bot.name);
1577
1566
  const body = {
1578
1567
  message: botText,
1579
1568
  vars: botVars,
1580
1569
  isFirstMessage: Array.isArray(aiChatbotConversationHistory) && aiChatbotConversationHistory.length !== 0 ? false : isFirstMessage,
1581
1570
  i18nLanguage,
1582
- isDynamicResponsesEnabled: isUserInternal ? isDynamicResponsesEnabled === "enable" ? true : false : false,
1571
+ isDynamicResponsesEnabled: isDynamicResponsesEnabled === "enable" ? true : false,
1583
1572
  conversationHistory,
1584
1573
  botProgess: aiChatbotConversationHistory,
1585
1574
  botTemplate: bot.selectedTemplate ? bot.selectedTemplate : "",
1586
1575
  intialStudentGreeting,
1587
- intialBotGreeting
1576
+ botIntroMessage: bot.introMessage ? bot.introMessage : ""
1588
1577
  };
1589
1578
  const response = await handleMessageBot(bot.botId, body);
1590
1579
  if (response.errorMessage) {
@@ -1677,7 +1666,30 @@ function useExerciseState({
1677
1666
  exerciseCompletion.picture = picture;
1678
1667
  exerciseCompletion.botName = botName;
1679
1668
  exerciseCompletion.description = description;
1680
- exerciseCompletion.messages = await formatBotMessages(botMessages);
1669
+ const formattedBotMessages = await formatBotMessages(botMessages);
1670
+ exerciseCompletion.messages = formattedBotMessages;
1671
+ const containsStudentInappropriate = formattedBotMessages.some(msg => msg?.studentInappropriate === true);
1672
+ const exerciseLink = `${window.location.origin}/courses/${courseId}/${sectionId}/${topicId}/activity/bot?botId=${botId}`;
1673
+ if (containsStudentInappropriate) {
1674
+ const responseFromMisuse = await handleStudentMisuse({
1675
+ params: {
1676
+ exerciseLink,
1677
+ exerciseName,
1678
+ problemDescription: "Student has been acting inapprpriately within your exercise"
1679
+ },
1680
+ botId: bot.botId,
1681
+ startTime,
1682
+ courseId,
1683
+ sectionId,
1684
+ topicId,
1685
+ exerciseLink
1686
+ });
1687
+ if (responseFromMisuse && responseFromMisuse.error) {
1688
+ openSnackbar(responseFromMisuse.error, "error");
1689
+ } else {
1690
+ openSnackbar("student_misuse_reported", "info");
1691
+ }
1692
+ }
1681
1693
  } else if (exerciseName === "roleplay") {
1682
1694
  const {
1683
1695
  picture,
@@ -249,7 +249,7 @@ const calcTopicCompletions = (topic, completions = [], isSectionHidden, assignme
249
249
  completedCount: 0
250
250
  };
251
251
  }
252
- const uniqueCompletions = Array.isArray(completions) ? Array.from(new Map(completions.filter(completion => completion.roleplayId === roleplay.roleplayId).map(completion => [completion.exercise, completion])).values()) : [];
252
+ const uniqueCompletions = Array.isArray(completions) ? Array.from(new Map(completions.filter(completion => completion.gameId && completion.roleplayId === roleplay.roleplayId).map(completion => [completion.exercise, completion])).values()) : [];
253
253
  completedGames += uniqueCompletions.length;
254
254
  totalNumberOfGames += numberOfUnhiddenGames;
255
255
  if (uniqueCompletions?.length === numberOfUnhiddenGames) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nualang/nualang-ui-components",
3
- "version": "0.1.1276",
3
+ "version": "0.1.1277",
4
4
  "main": "dist/index.js",
5
5
  "files": [
6
6
  "dist",