@nualang/nualang-ui-components 0.1.1411 → 0.1.1413
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Dialogs/QuickCreateChatbotDialog/QuickCreateChatbotDialog.js +146 -34
- package/dist/Dialogs/QuickCreateClassroomDialog/QuickCreateClassroomDialog.js +2 -2
- package/dist/Dialogs/QuickCreateCourseDialog/QuickCreateCourseDialog.js +2 -2
- package/dist/Dialogs/QuickCreateScenarioDialog/QuickCreateScenarioDialog.js +0 -1
- package/dist/Dialogs/QuickCreateTopicDialog/QuickCreateTopicDialog.js +2 -2
- package/dist/Exercises/ListenAndSpeak/ListenAndSpeak.js +1 -18
- package/dist/Exercises/Meaning/Meaning.js +1 -18
- package/dist/Exercises/Translator/Translator.js +27 -7
- package/dist/Forms/ChipInput/ChipInput.js +17 -3
- package/dist/Forms/CreateClassroom/Steps/ClassroomSettings/ClassroomSettings.js +4 -1
- package/dist/Forms/UpdateClassroom/UpdateClassroom.js +3 -1
- package/dist/Forms/VoiceSelector/VoiceSelector.js +713 -296
- package/dist/Forms/VoiceSelector/VoiceSelector.test.js +247 -4
- package/dist/Lists/Courses/Courses.js +97 -7
- package/dist/Lists/Courses/Courses.test.js +61 -11
- package/dist/Lists/Exercises/Exercises.js +6 -2
- package/dist/Misc/NotFound/ClassCourses.js +2 -1
- package/dist/Misc/NotFound/Courses.js +2 -1
- package/dist/hooks/useExerciseState.js +92 -26
- package/dist/utils/courseSort.js +64 -0
- package/dist/utils/voices.js +26 -0
- package/package.json +1 -1
|
@@ -1,10 +1,14 @@
|
|
|
1
|
-
import { useState, useEffect, forwardRef,
|
|
2
|
-
import { TextField, MenuItem, ListSubheader, Box, Paper, Typography } from "@mui/material";
|
|
3
|
-
import { ForumRounded, QuizRounded, ImageRounded, SentimentSatisfiedRounded, CheckRounded } from "@mui/icons-material";
|
|
1
|
+
import { useState, useEffect, forwardRef, createElement } from "react";
|
|
2
|
+
import { TextField, MenuItem, ListSubheader, Box, Paper, Typography, Grid, Button, Avatar } from "@mui/material";
|
|
3
|
+
import { ForumRounded, QuizRounded, ImageRounded, SentimentSatisfiedRounded, CheckRounded, Person } from "@mui/icons-material";
|
|
4
4
|
import { useTheme, alpha } from "@mui/material/styles";
|
|
5
5
|
import { keyframes } from "@emotion/react";
|
|
6
6
|
import ResponsiveDialog from "../ResponsiveDialog/ResponsiveDialog";
|
|
7
7
|
import ChatbotNualaAnimation from "../../Misc/QuickCreateNualaAnimations/ChatbotNualaAnimation";
|
|
8
|
+
import VoiceSelector from "../../Forms/VoiceSelector/VoiceSelector";
|
|
9
|
+
import { findVoice, pickRandomVoice } from "../../utils/voices";
|
|
10
|
+
import { makeStyles } from "tss-react/mui";
|
|
11
|
+
import AvatarSelector from "../../Misc/AvatarSelector/AvatarSelector";
|
|
8
12
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
13
|
export const BOT_NAME_MAX_LENGTH = 100;
|
|
10
14
|
const peekAnimation = keyframes`
|
|
@@ -14,6 +18,18 @@ const peekAnimation = keyframes`
|
|
|
14
18
|
67% { transform: translateX(-150px) rotate(75deg); }
|
|
15
19
|
100% { transform: translateX(-150px) rotate(75deg); }
|
|
16
20
|
`;
|
|
21
|
+
const useStyles = makeStyles()(theme => ({
|
|
22
|
+
avatar: {
|
|
23
|
+
width: 70,
|
|
24
|
+
height: 70,
|
|
25
|
+
marginBottom: theme.spacing(2),
|
|
26
|
+
cursor: "pointer"
|
|
27
|
+
},
|
|
28
|
+
avatarIcon: {
|
|
29
|
+
width: "80%",
|
|
30
|
+
height: "80%"
|
|
31
|
+
}
|
|
32
|
+
}));
|
|
17
33
|
const ChatbotDialogPaper = /*#__PURE__*/forwardRef(function ChatbotDialogPaper({
|
|
18
34
|
children,
|
|
19
35
|
className,
|
|
@@ -378,7 +394,11 @@ export default function QuickCreateChatbotDialog({
|
|
|
378
394
|
topicDifficulty,
|
|
379
395
|
topicPicture,
|
|
380
396
|
defaultNumberOfQuestions = 5,
|
|
381
|
-
voices = []
|
|
397
|
+
voices = [],
|
|
398
|
+
handleSpeak,
|
|
399
|
+
isUserInternal = false,
|
|
400
|
+
avatars,
|
|
401
|
+
learnLang
|
|
382
402
|
}) {
|
|
383
403
|
const [botTitle, setBotTitle] = useState("");
|
|
384
404
|
const [difficulty, setDifficulty] = useState(topicDifficulty || "");
|
|
@@ -387,6 +407,14 @@ export default function QuickCreateChatbotDialog({
|
|
|
387
407
|
const [riveFile, setRiveFile] = useState("");
|
|
388
408
|
const [numberOfQuestions, setNumberOfQuestions] = useState(defaultNumberOfQuestions);
|
|
389
409
|
const [isLoading, setIsLoading] = useState(false);
|
|
410
|
+
const [selectedVoice, setSelectedVoice] = useState(null);
|
|
411
|
+
const [selectedVoiceLanguageCode, setSelectedVoiceLanguageCode] = useState(null);
|
|
412
|
+
const [selectedVoicePitch, setSelectedVoicePitch] = useState(null);
|
|
413
|
+
const [botPicture, setBotPicture] = useState("");
|
|
414
|
+
const [avatarSelector, setAvatarSelector] = useState(false);
|
|
415
|
+
const {
|
|
416
|
+
classes
|
|
417
|
+
} = useStyles();
|
|
390
418
|
|
|
391
419
|
// Keep the questions field in sync if the default changes while closed.
|
|
392
420
|
useEffect(() => {
|
|
@@ -408,31 +436,31 @@ export default function QuickCreateChatbotDialog({
|
|
|
408
436
|
setTemplateId("");
|
|
409
437
|
setNumberOfQuestions(defaultNumberOfQuestions);
|
|
410
438
|
handleClose();
|
|
439
|
+
// Cleared so the next open seeds a fresh random voice.
|
|
440
|
+
setSelectedVoice(null);
|
|
441
|
+
setSelectedVoiceLanguageCode(null);
|
|
442
|
+
setSelectedVoicePitch(null);
|
|
443
|
+
setBotPicture("");
|
|
444
|
+
setAvatarSelector(false);
|
|
445
|
+
};
|
|
446
|
+
const handleAvatarSelection = value => {
|
|
447
|
+
setBotPicture(value);
|
|
448
|
+
setAvatarSelector(false);
|
|
411
449
|
};
|
|
412
|
-
const voicesArray = useMemo(() => {
|
|
413
|
-
return Object.entries(voices).flatMap(([key, dialect]) => {
|
|
414
|
-
if (!dialect?.voiceOptions || !Array.isArray(dialect.voiceOptions)) {
|
|
415
|
-
return [];
|
|
416
|
-
}
|
|
417
|
-
return dialect.voiceOptions.filter(voice => voice?.name) // Validate required properties
|
|
418
|
-
.map(voice => ({
|
|
419
|
-
voice,
|
|
420
|
-
dialect,
|
|
421
|
-
key
|
|
422
|
-
}));
|
|
423
|
-
});
|
|
424
|
-
}, [voices]);
|
|
425
450
|
|
|
426
|
-
//
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
voice
|
|
433
|
-
dialect
|
|
434
|
-
|
|
435
|
-
|
|
451
|
+
// ActorForm assigns each actor a random voice on mount; standalone, the same
|
|
452
|
+
// seeding happens here each time the dialog opens with nothing chosen yet.
|
|
453
|
+
useEffect(() => {
|
|
454
|
+
if (!open || selectedVoice) return;
|
|
455
|
+
const picked = pickRandomVoice(voices);
|
|
456
|
+
if (!picked) return;
|
|
457
|
+
setSelectedVoice(picked.voice.name);
|
|
458
|
+
setSelectedVoiceLanguageCode(picked.dialect.languageCode);
|
|
459
|
+
setSelectedVoicePitch(null);
|
|
460
|
+
setBotPicture(picked.voice.userImage || "");
|
|
461
|
+
}, [open, selectedVoice, voices]);
|
|
462
|
+
const selectedVoiceOption = findVoice(voices, selectedVoice)?.voice;
|
|
463
|
+
const hasAvailableVoice = Boolean(selectedVoice && selectedVoiceLanguageCode);
|
|
436
464
|
const isValid = Boolean(botTitle.trim()) && Boolean(difficulty) && Boolean(templateId) && Boolean(template && template.botId === templateId) && Number(numberOfQuestions) > 0 && hasAvailableVoice;
|
|
437
465
|
const handleSubmit = async () => {
|
|
438
466
|
if (!isValid) return;
|
|
@@ -449,10 +477,11 @@ export default function QuickCreateChatbotDialog({
|
|
|
449
477
|
selectedTemplate: templateId,
|
|
450
478
|
numberOfQuestions: Number(numberOfQuestions),
|
|
451
479
|
courseSectionTopicId: `${courseId}|${sectionId}|${topicId}`,
|
|
452
|
-
voice:
|
|
453
|
-
voiceLanguageCode:
|
|
454
|
-
name:
|
|
455
|
-
|
|
480
|
+
voice: selectedVoice,
|
|
481
|
+
voiceLanguageCode: selectedVoiceLanguageCode,
|
|
482
|
+
name: selectedVoice,
|
|
483
|
+
voicePitch: selectedVoicePitch,
|
|
484
|
+
botAvatar: botPicture,
|
|
456
485
|
isChallengeBot: false,
|
|
457
486
|
isDynamicResponsesEnabled: "enable",
|
|
458
487
|
picture: topicPicture || template.picture
|
|
@@ -467,7 +496,7 @@ export default function QuickCreateChatbotDialog({
|
|
|
467
496
|
setIsLoading(false);
|
|
468
497
|
}
|
|
469
498
|
};
|
|
470
|
-
return /*#__PURE__*/
|
|
499
|
+
return /*#__PURE__*/_jsxs(ResponsiveDialog, {
|
|
471
500
|
open: open,
|
|
472
501
|
handleClose: isLoading ? undefined : resetAndClose,
|
|
473
502
|
handleSubmit: handleSubmit,
|
|
@@ -476,7 +505,7 @@ export default function QuickCreateChatbotDialog({
|
|
|
476
505
|
closeText: t("cancel") || "Cancel",
|
|
477
506
|
submitText: t("create") || "Create",
|
|
478
507
|
PaperComponent: ChatbotDialogPaper,
|
|
479
|
-
children: /*#__PURE__*/_jsxs(Box, {
|
|
508
|
+
children: [/*#__PURE__*/_jsxs(Box, {
|
|
480
509
|
display: "flex",
|
|
481
510
|
flexDirection: "column",
|
|
482
511
|
gap: 1,
|
|
@@ -537,7 +566,90 @@ export default function QuickCreateChatbotDialog({
|
|
|
537
566
|
},
|
|
538
567
|
onKeyDown: e => e.key === "Enter" && handleSubmit(),
|
|
539
568
|
margin: "normal"
|
|
569
|
+
}), /*#__PURE__*/_jsxs(Grid, {
|
|
570
|
+
size: 12,
|
|
571
|
+
sx: {
|
|
572
|
+
mt: 1
|
|
573
|
+
},
|
|
574
|
+
children: [botPicture ? /*#__PURE__*/_jsx(Avatar, {
|
|
575
|
+
"aria-label": selectedVoiceOption?.name,
|
|
576
|
+
src: botPicture,
|
|
577
|
+
className: classes.avatar,
|
|
578
|
+
onClick: () => setAvatarSelector(true),
|
|
579
|
+
alt: ""
|
|
580
|
+
}) : /*#__PURE__*/_jsx(Avatar, {
|
|
581
|
+
"aria-label": selectedVoiceOption?.name,
|
|
582
|
+
sx: theme => ({
|
|
583
|
+
backgroundColor: theme.palette.mode === "dark" ? "white" : "",
|
|
584
|
+
color: theme.palette.mode === "dark" ? theme.palette.background.default : ""
|
|
585
|
+
}),
|
|
586
|
+
className: classes.avatar,
|
|
587
|
+
onClick: () => setAvatarSelector(true),
|
|
588
|
+
alt: "",
|
|
589
|
+
children: /*#__PURE__*/_jsx(Person, {
|
|
590
|
+
className: classes.avatarIcon
|
|
591
|
+
})
|
|
592
|
+
}), /*#__PURE__*/_jsx(Button, {
|
|
593
|
+
color: "primary",
|
|
594
|
+
component: "span",
|
|
595
|
+
onClick: () => setAvatarSelector(true),
|
|
596
|
+
children: t("choose_avatar")
|
|
597
|
+
}), /*#__PURE__*/_jsx(VoiceSelector, {
|
|
598
|
+
id: "voiceSelector",
|
|
599
|
+
"data-cy": "voiceSelector",
|
|
600
|
+
name: "voiceSelector",
|
|
601
|
+
label: t("bot_voice"),
|
|
602
|
+
value: selectedVoice || "",
|
|
603
|
+
margin: "normal",
|
|
604
|
+
variant: "outlined",
|
|
605
|
+
fullWidth: true,
|
|
606
|
+
required: learnLang !== "irish",
|
|
607
|
+
t: t,
|
|
608
|
+
voices: voices,
|
|
609
|
+
onChange: e => {
|
|
610
|
+
if (e && e.target && e.target.value) {
|
|
611
|
+
const selectedVoice = e.target.value;
|
|
612
|
+
const languageCode = e.target.languageCode;
|
|
613
|
+
setSelectedVoice(selectedVoice);
|
|
614
|
+
handleSpeak?.({
|
|
615
|
+
text: selectedVoice,
|
|
616
|
+
learnLanguage: learnLang,
|
|
617
|
+
voiceName: selectedVoice,
|
|
618
|
+
languageCode
|
|
619
|
+
});
|
|
620
|
+
setSelectedVoiceLanguageCode(languageCode);
|
|
621
|
+
setSelectedVoicePitch(null);
|
|
622
|
+
setBotPicture(e.target.picture);
|
|
623
|
+
}
|
|
624
|
+
},
|
|
625
|
+
onVoicePitchChange: e => {
|
|
626
|
+
if (e && e.target) {
|
|
627
|
+
setSelectedVoicePitch(e.target.value);
|
|
628
|
+
handleSpeak?.({
|
|
629
|
+
text: selectedVoiceLanguageCode ? t("voice_pitch_example", {
|
|
630
|
+
name: selectedVoice,
|
|
631
|
+
lng: selectedVoiceLanguageCode?.substring(0, 2)
|
|
632
|
+
}) : selectedVoice,
|
|
633
|
+
learnLanguage: learnLang,
|
|
634
|
+
voiceName: selectedVoice,
|
|
635
|
+
languageCode: selectedVoiceLanguageCode,
|
|
636
|
+
pitch: e.target.value
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
},
|
|
640
|
+
voicePitch: selectedVoicePitch,
|
|
641
|
+
isUserInternal: isUserInternal,
|
|
642
|
+
handleSpeak: handleSpeak,
|
|
643
|
+
learnLang: learnLang
|
|
644
|
+
})]
|
|
540
645
|
})]
|
|
541
|
-
})
|
|
646
|
+
}), /*#__PURE__*/_jsx(AvatarSelector, {
|
|
647
|
+
t: t,
|
|
648
|
+
open: avatarSelector,
|
|
649
|
+
avatars: avatars,
|
|
650
|
+
picture: botPicture,
|
|
651
|
+
handleClose: () => setAvatarSelector(false),
|
|
652
|
+
handleSubmit: value => handleAvatarSelection(value)
|
|
653
|
+
})]
|
|
542
654
|
});
|
|
543
655
|
}
|
|
@@ -133,7 +133,7 @@ export default function QuickCreateClassroomDialog({
|
|
|
133
133
|
return String(Math.floor(Math.random() * 90000) + 10000);
|
|
134
134
|
};
|
|
135
135
|
const handleSubmit = async () => {
|
|
136
|
-
if (!classroomName.trim() || !classroomDescription.trim()) return;
|
|
136
|
+
if (!classroomName.trim() || !classroomDescription.trim() || !classroomLearnLang || !classroomForLang) return;
|
|
137
137
|
setIsLoading(true);
|
|
138
138
|
try {
|
|
139
139
|
const createdClassroom = await handleCreate({
|
|
@@ -187,7 +187,7 @@ export default function QuickCreateClassroomDialog({
|
|
|
187
187
|
handleClose: isLoading ? undefined : resetAndClose,
|
|
188
188
|
handleSubmit: handleSubmit,
|
|
189
189
|
dialogTitle: t("create_classroom") || "Create Classroom",
|
|
190
|
-
isSubmitDisabled: !classroomName.trim() || !classroomDescription.trim() || isLoading,
|
|
190
|
+
isSubmitDisabled: !classroomName.trim() || !classroomDescription.trim() || !classroomLearnLang || !classroomForLang || isLoading,
|
|
191
191
|
closeText: t("cancel") || "Cancel",
|
|
192
192
|
submitText: t("create") || "Create",
|
|
193
193
|
PaperComponent: ClassroomDialogPaper,
|
|
@@ -133,7 +133,7 @@ export default function QuickCreateCourseDialog({
|
|
|
133
133
|
return String(Math.floor(Math.random() * 90000) + 10000);
|
|
134
134
|
};
|
|
135
135
|
const handleSubmit = async () => {
|
|
136
|
-
if (!courseName.trim() || !courseDescription.trim() || !difficulty) return;
|
|
136
|
+
if (!courseName.trim() || !courseDescription.trim() || !difficulty || !courseLearnLang || !courseForLang) return;
|
|
137
137
|
setIsLoading(true);
|
|
138
138
|
try {
|
|
139
139
|
const createdCourse = await handleCreate({
|
|
@@ -185,7 +185,7 @@ export default function QuickCreateCourseDialog({
|
|
|
185
185
|
handleClose: isLoading ? undefined : resetAndClose,
|
|
186
186
|
handleSubmit: handleSubmit,
|
|
187
187
|
dialogTitle: t("create_course") || "Create Course",
|
|
188
|
-
isSubmitDisabled: !courseName.trim() || !difficulty || isLoading,
|
|
188
|
+
isSubmitDisabled: !courseName.trim() || !courseDescription.trim() || !difficulty || !courseLearnLang || !courseForLang || isLoading,
|
|
189
189
|
closeText: t("cancel") || "Cancel",
|
|
190
190
|
submitText: t("create") || "Create",
|
|
191
191
|
PaperComponent: CourseDialogPaper,
|
|
@@ -142,7 +142,7 @@ export default function QuickCreateTopicDialog({
|
|
|
142
142
|
setPicture(inheritCourseImage ? coursePicture : "");
|
|
143
143
|
};
|
|
144
144
|
const handleSubmit = async () => {
|
|
145
|
-
if (!topicName.trim() || !topicDifficulty) return;
|
|
145
|
+
if (!topicName.trim() || !topicDifficulty || !learnLang || !forLang) return;
|
|
146
146
|
setIsLoading(true);
|
|
147
147
|
try {
|
|
148
148
|
const createdTopic = await handleCreate({
|
|
@@ -190,7 +190,7 @@ export default function QuickCreateTopicDialog({
|
|
|
190
190
|
handleClose: isLoading ? undefined : resetAndClose,
|
|
191
191
|
handleSubmit: handleSubmit,
|
|
192
192
|
dialogTitle: t("create_topic") || "Create Topic",
|
|
193
|
-
isSubmitDisabled: !topicName.trim() || !topicDifficulty || isLoading,
|
|
193
|
+
isSubmitDisabled: !topicName.trim() || !topicDifficulty || !learnLang || !forLang || isLoading,
|
|
194
194
|
closeText: t("cancel") || "Cancel",
|
|
195
195
|
submitText: t("create") || "Create",
|
|
196
196
|
PaperComponent: TopicDialogPaper,
|
|
@@ -322,7 +322,7 @@ function ListenAndSpeak({
|
|
|
322
322
|
checked,
|
|
323
323
|
setChecked,
|
|
324
324
|
setIsAssignmentExercise,
|
|
325
|
-
|
|
325
|
+
definitionText
|
|
326
326
|
} = exerciseState;
|
|
327
327
|
useEffect(() => {
|
|
328
328
|
if (currentPhrase?.phrase) {
|
|
@@ -435,18 +435,6 @@ function ListenAndSpeak({
|
|
|
435
435
|
question = questions[0];
|
|
436
436
|
image = question.image || null;
|
|
437
437
|
}
|
|
438
|
-
const definitionText = (() => {
|
|
439
|
-
if (!question) return "";
|
|
440
|
-
if (question.definitions?.length > 0) {
|
|
441
|
-
const raw = question.definitions[0];
|
|
442
|
-
return typeof raw === "string" ? raw : "";
|
|
443
|
-
}
|
|
444
|
-
if (question.translations?.length > 0) {
|
|
445
|
-
const raw = question.translations[0];
|
|
446
|
-
return typeof raw === "string" ? raw : "";
|
|
447
|
-
}
|
|
448
|
-
return "";
|
|
449
|
-
})();
|
|
450
438
|
const handleMoreTranslations = () => {
|
|
451
439
|
setViewMoreTranslations(true);
|
|
452
440
|
};
|
|
@@ -471,11 +459,6 @@ function ListenAndSpeak({
|
|
|
471
459
|
const goBackToAssignment = () => {
|
|
472
460
|
navigate(`/classrooms/${classroomId}#Assignments`);
|
|
473
461
|
};
|
|
474
|
-
useEffect(() => {
|
|
475
|
-
if (open && definitionText && definitionText !== "" && !isCompletionModalOpen && !isCloseModalOpen && !isInProgressModalOpen && learnLang && learnLang !== "irish" && !muteSound && isTourFinished) {
|
|
476
|
-
speak(definitionText, voice, false, "#currentPhrase", voiceLanguageCode, false, voicePitch);
|
|
477
|
-
}
|
|
478
|
-
}, [currentPhrase, open, isTourFinished]);
|
|
479
462
|
const NualaSpeakingAnimation = (voiceMap.get(voice) || {}).speakingImage || null;
|
|
480
463
|
return /*#__PURE__*/_jsxs(Dialog, {
|
|
481
464
|
fullScreen: true,
|
|
@@ -217,7 +217,7 @@ function Meaning({
|
|
|
217
217
|
handleCloseClicked,
|
|
218
218
|
handleTranslationTourFinished,
|
|
219
219
|
setIsAssignmentExercise,
|
|
220
|
-
|
|
220
|
+
definitionText
|
|
221
221
|
} = exerciseState;
|
|
222
222
|
const [selectedPhrase, setSelectedPhrase] = useState(null);
|
|
223
223
|
const [phraseOptions, setPhraseOptions] = useState([]);
|
|
@@ -353,18 +353,6 @@ function Meaning({
|
|
|
353
353
|
question = questions[0];
|
|
354
354
|
image = question.image || null;
|
|
355
355
|
}
|
|
356
|
-
const definitionText = (() => {
|
|
357
|
-
if (!question) return "";
|
|
358
|
-
if (question.definitions?.length > 0) {
|
|
359
|
-
const raw = question.definitions[0];
|
|
360
|
-
return typeof raw === "string" ? raw : "";
|
|
361
|
-
}
|
|
362
|
-
if (question.translations?.length > 0) {
|
|
363
|
-
const raw = question.translations[0];
|
|
364
|
-
return typeof raw === "string" ? raw : "";
|
|
365
|
-
}
|
|
366
|
-
return "";
|
|
367
|
-
})();
|
|
368
356
|
const {
|
|
369
357
|
voice,
|
|
370
358
|
voiceLanguageCode,
|
|
@@ -410,11 +398,6 @@ function Meaning({
|
|
|
410
398
|
navigate(`/classrooms/${classroomId}#Assignments`);
|
|
411
399
|
};
|
|
412
400
|
const isSubmitDisabled = !selectedPhrase || showResult;
|
|
413
|
-
useEffect(() => {
|
|
414
|
-
if (open && definitionText && definitionText !== "" && !isCompletionModalOpen && !isCloseModalOpen && !isInProgressModalOpen && learnLang && learnLang !== "irish" && !isReadWriteModeStudent && !muteSound && isTourFinished) {
|
|
415
|
-
speak(definitionText, voice, false, "#currentPhrase", voiceLanguageCode, false, voicePitch);
|
|
416
|
-
}
|
|
417
|
-
}, [currentPhrase, open, isTourFinished]);
|
|
418
401
|
const NualaSpeakingAnimation = (voiceMap.get(voice) || {}).speakingImage || null;
|
|
419
402
|
return /*#__PURE__*/_jsxs(Dialog, {
|
|
420
403
|
fullScreen: true,
|
|
@@ -143,18 +143,40 @@ function Transition(props) {
|
|
|
143
143
|
...props
|
|
144
144
|
});
|
|
145
145
|
}
|
|
146
|
+
const getMatchingTranslationWords = (translationWords, translations, removeExtraWhiteSpaces) => {
|
|
147
|
+
if (!translations || !translations.length) return [];
|
|
148
|
+
const splitTranslations = translations.map(translation => removeExtraWhiteSpaces(translation ?? "").split(" "));
|
|
149
|
+
const attemptWords = translationWords.map(word => removeAllSymbols(word.text).toLowerCase());
|
|
150
|
+
const attemptJoined = attemptWords.join(" ");
|
|
151
|
+
const exactMatchIndex = splitTranslations.findIndex(words => words.map(w => removeAllSymbols(w).toLowerCase()).join(" ") === attemptJoined);
|
|
152
|
+
if (exactMatchIndex > -1) {
|
|
153
|
+
return splitTranslations[exactMatchIndex];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// No exact match (attempt is incomplete or wrong) - fall back to whichever
|
|
157
|
+
// translation shares the most words with the attempt at the same
|
|
158
|
+
// position, so feedback is still as helpful as possible.
|
|
159
|
+
let bestIndex = 0;
|
|
160
|
+
let bestScore = -1;
|
|
161
|
+
splitTranslations.forEach((words, translationIndex) => {
|
|
162
|
+
const score = attemptWords.filter((word, wordIndex) => words[wordIndex] && removeAllSymbols(words[wordIndex]).toLowerCase() === word).length;
|
|
163
|
+
if (score > bestScore) {
|
|
164
|
+
bestScore = score;
|
|
165
|
+
bestIndex = translationIndex;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return splitTranslations[bestIndex];
|
|
169
|
+
};
|
|
146
170
|
const WordBankButton = /*#__PURE__*/forwardRef(({
|
|
147
171
|
word,
|
|
148
172
|
index,
|
|
149
|
-
|
|
150
|
-
removeExtraWhiteSpaces,
|
|
173
|
+
translationWordList,
|
|
151
174
|
translationWords,
|
|
152
175
|
setTranslationWords,
|
|
153
176
|
classes,
|
|
154
177
|
handleSelectInAnswerBox,
|
|
155
178
|
checkAnswer
|
|
156
179
|
}, ref) => {
|
|
157
|
-
const translationWordList = question && question.translations && question.translations.length ? removeExtraWhiteSpaces(question.translations[0]).split(" ") : [];
|
|
158
180
|
const key = `${word.text}${word.id}`;
|
|
159
181
|
const isWordCorrect = translationWordList[index] && removeAllSymbols(word.text) === removeAllSymbols(translationWordList[index]);
|
|
160
182
|
const onButtonClick = () => {
|
|
@@ -495,7 +517,7 @@ function Translator({
|
|
|
495
517
|
question = questions[0];
|
|
496
518
|
image = question.image || null;
|
|
497
519
|
}
|
|
498
|
-
const translationWordList = question && question.translations && question.translations.length ?
|
|
520
|
+
const translationWordList = question && question.translations && question.translations.length ? getMatchingTranslationWords(translationWords, question.translations, removeExtraWhiteSpaces) : [];
|
|
499
521
|
const {
|
|
500
522
|
voice,
|
|
501
523
|
voiceLanguageCode,
|
|
@@ -798,9 +820,7 @@ function Translator({
|
|
|
798
820
|
"data-cy": "word-bank-button",
|
|
799
821
|
word: word,
|
|
800
822
|
index: index,
|
|
801
|
-
|
|
802
|
-
currentPhrase: currentPhrase,
|
|
803
|
-
removeExtraWhiteSpaces: removeExtraWhiteSpaces,
|
|
823
|
+
translationWordList: translationWordList,
|
|
804
824
|
translationWords: translationWords,
|
|
805
825
|
setTranslationWords: setTranslationWords,
|
|
806
826
|
classes: classes,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useState } from "react"; // already present, just ensure it's used
|
|
2
2
|
|
|
3
|
-
import CustomChipInput from "../../Misc/CustomChipInput/CustomChipInput";
|
|
3
|
+
import CustomChipInput, { DefaultChipRenderer } from "../../Misc/CustomChipInput/CustomChipInput";
|
|
4
4
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
5
5
|
function ChipInput(props) {
|
|
6
6
|
const {
|
|
@@ -11,6 +11,13 @@ function ChipInput(props) {
|
|
|
11
11
|
label,
|
|
12
12
|
delimiter = /[\s,]+/
|
|
13
13
|
} = props;
|
|
14
|
+
// `chipLabels` is a display-only map of { chipValue: label }. When provided,
|
|
15
|
+
// chips render the mapped label while their stored value stays unchanged.
|
|
16
|
+
// Strip it from the props spread so it doesn't leak onto the DOM node.
|
|
17
|
+
const {
|
|
18
|
+
chipLabels,
|
|
19
|
+
...rest
|
|
20
|
+
} = props;
|
|
14
21
|
const [errorMessage, setErrorMessage] = useState("");
|
|
15
22
|
const genericDomains = ["gmail.com", "yahoo.com", "hotmail.com", "outlook.com", "icloud.com"];
|
|
16
23
|
const handleAddChip = chip => {
|
|
@@ -61,14 +68,21 @@ function ChipInput(props) {
|
|
|
61
68
|
}
|
|
62
69
|
});
|
|
63
70
|
};
|
|
71
|
+
const labelledChipRenderer = chipLabels ? (chipProps, key) => DefaultChipRenderer({
|
|
72
|
+
...chipProps,
|
|
73
|
+
text: chipLabels[chipProps.value] ?? chipProps.text
|
|
74
|
+
}, key) : undefined;
|
|
64
75
|
return /*#__PURE__*/_jsx(CustomChipInput, {
|
|
65
76
|
label: label,
|
|
66
|
-
...
|
|
77
|
+
...rest,
|
|
67
78
|
onAdd: handleAddChip,
|
|
68
79
|
onDelete: deleteChip,
|
|
69
80
|
delimiter: delimiter,
|
|
70
81
|
helperText: errorMessage || props.helperText,
|
|
71
|
-
error: !!errorMessage || props.error
|
|
82
|
+
error: !!errorMessage || props.error,
|
|
83
|
+
...(labelledChipRenderer ? {
|
|
84
|
+
chipRenderer: labelledChipRenderer
|
|
85
|
+
} : {})
|
|
72
86
|
});
|
|
73
87
|
}
|
|
74
88
|
export default ChipInput;
|
|
@@ -21,7 +21,8 @@ function ClassroomSettings({
|
|
|
21
21
|
setScrollToDiscuss,
|
|
22
22
|
scrollToDynamicResponses = false,
|
|
23
23
|
setScrollToDynamicResponses,
|
|
24
|
-
isVideoChatEnabled
|
|
24
|
+
isVideoChatEnabled,
|
|
25
|
+
collaboratorLabels
|
|
25
26
|
}) {
|
|
26
27
|
const {
|
|
27
28
|
visibility,
|
|
@@ -351,6 +352,7 @@ function ClassroomSettings({
|
|
|
351
352
|
"data-cy": "collaborators",
|
|
352
353
|
label: t("collaborators"),
|
|
353
354
|
value: collaborators || [],
|
|
355
|
+
chipLabels: collaboratorLabels,
|
|
354
356
|
name: "collaborators",
|
|
355
357
|
helperText: touched.collaborators ? t(errors.collaborators) : t("add_collaborator_tag"),
|
|
356
358
|
error: touched.collaborators && Boolean(errors.collaborators),
|
|
@@ -370,6 +372,7 @@ function ClassroomSettings({
|
|
|
370
372
|
"data-cy": "collaborators",
|
|
371
373
|
label: t("collaborators"),
|
|
372
374
|
value: collaborators || [],
|
|
375
|
+
chipLabels: collaboratorLabels,
|
|
373
376
|
name: "collaborators",
|
|
374
377
|
helperText: touched.collaborators ? errors.collaborators : t("add_collaborator_tag"),
|
|
375
378
|
error: touched.collaborators && Boolean(errors.collaborators),
|
|
@@ -45,7 +45,8 @@ export default function UpdateClassroom({
|
|
|
45
45
|
setScrollToDynamicResponses,
|
|
46
46
|
isClassroomArchived = false,
|
|
47
47
|
verificationStatus,
|
|
48
|
-
isVideoChatEnabled
|
|
48
|
+
isVideoChatEnabled,
|
|
49
|
+
collaboratorLabels
|
|
49
50
|
}) {
|
|
50
51
|
const {
|
|
51
52
|
classes
|
|
@@ -230,6 +231,7 @@ export default function UpdateClassroom({
|
|
|
230
231
|
scrollToDynamicResponses: scrollToDynamicResponses,
|
|
231
232
|
setScrollToDynamicResponses: setScrollToDynamicResponses,
|
|
232
233
|
isVideoChatEnabled: isVideoChatEnabled,
|
|
234
|
+
collaboratorLabels: collaboratorLabels,
|
|
233
235
|
...otherProps
|
|
234
236
|
})
|
|
235
237
|
}), /*#__PURE__*/_jsx(Divider, {}), /*#__PURE__*/_jsx(CardActions, {
|