@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.
@@ -1,10 +1,14 @@
1
- import { useState, useEffect, forwardRef, useMemo, createElement } from "react";
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
- // exclude legacy for random selection
427
- const nonLegacyVoicePool = voicesArray.filter(v => !v.voice?.legacy && !v.legacy);
428
- const voicePool = nonLegacyVoicePool.length > 0 ? nonLegacyVoicePool : voicesArray;
429
- const randomIndex = Math.floor(Math.random() * voicePool.length);
430
- const randomVoiceObj = voicePool[randomIndex] || {};
431
- const {
432
- voice: randomVoice,
433
- dialect
434
- } = randomVoiceObj;
435
- const hasAvailableVoice = Boolean(randomVoice?.name && dialect?.languageCode);
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: randomVoice.name,
453
- voiceLanguageCode: dialect.languageCode,
454
- name: randomVoice.name,
455
- botAvatar: randomVoice.userImage,
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__*/_jsx(ResponsiveDialog, {
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,
@@ -330,7 +330,6 @@ export default function QuickCreateScenarioDialog({
330
330
  isUserInternal: isUserInternal,
331
331
  handleSpeak: handleSpeak,
332
332
  avatars: avatars,
333
- hidePitch: true,
334
333
  disabled: isLoading
335
334
  })
336
335
  }), /*#__PURE__*/_jsx(Grid, {
@@ -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
- isTourFinished
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
- isTourFinished
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
- question,
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 ? removeExtraWhiteSpaces(question.translations[0]).split(" ") : [];
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
- question: question,
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
- ...props,
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, {