@nualang/nualang-ui-components 0.1.1414 → 0.1.1416
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/Cards/Course/Course.js +145 -13
- package/dist/Chat/Messages/Message/Message.js +4 -1
- package/dist/Chat/Messages/Message/Message.test.js +55 -0
- package/dist/Containers/App/App.js +25 -8
- package/dist/Dialogs/FindChatbotDialog/FindChatbotDialog.js +23 -9
- package/dist/Dialogs/FindScenarioDialog/FindScenarioDialog.js +23 -9
- package/dist/Dialogs/FindTopicsDialog/FindTopicsDialog.js +22 -8
- package/dist/Dialogs/GenerateBot/GenerateBot.js +29 -8
- package/dist/Dialogs/GenerateRoleplay/GenerateRoleplay.js +5 -2
- package/dist/Dialogs/QuickCreateChatbotDialog/QuickCreateChatbotDialog.js +22 -4
- package/dist/Editors/Bot/Bot.js +54 -11
- package/dist/Editors/Bot/Editor/Editor.js +78 -49
- package/dist/Editors/Bot/Editor/QuestionsEditor/AnswerOptionsList.js +94 -0
- package/dist/Editors/Bot/Editor/QuestionsEditor/QuestionRow.js +211 -0
- package/dist/Editors/Bot/Editor/QuestionsEditor/QuestionsEditor.js +115 -0
- package/dist/Editors/Roleplay/Roleplay.js +5 -2
- package/dist/Exercises/ListenAndSpeak/ListenAndSpeak.js +1 -1
- package/dist/Exercises/Listener/Listener.js +1 -1
- package/dist/Exercises/Meaning/Meaning.js +1 -1
- package/dist/Exercises/MeaningReadWrite/MeaningReadWriteGame.js +4 -1
- package/dist/Exercises/Pronouncer/Pronouncer.js +1 -1
- package/dist/Exercises/Roleplay/Roleplay.js +5 -2
- package/dist/Exercises/Translator/Translator.js +1 -1
- package/dist/Forms/CreateBot/Steps/BotInformation/BotInformation.js +13 -2
- package/dist/Forms/CreateRoleplay/Steps/RoleplayInformation/RoleplayInformation.js +4 -2
- package/dist/Forms/UpdatePhraseList/UpdatePhraseList.js +5 -3
- package/dist/Forms/VoiceSelector/VoiceSelector.js +29 -14
- package/dist/Forms/VoiceSelector/VoiceSelector.test.js +125 -3
- package/dist/Lists/CourseOutline/CourseOutline.js +4 -0
- package/dist/Lists/Courses/Courses.js +5 -2
- package/dist/Lists/Courses/Courses.test.js +10 -0
- package/dist/Lists/Exercises/Exercises.js +6 -3
- package/dist/Lists/Roleplays/Roleplays.js +5 -2
- package/dist/Live/LiveListener/LiveListener.js +1 -1
- package/dist/Live/LiveMeaning/LiveMeaning.js +1 -1
- package/dist/Live/LiveMeaningReadWrite/LiveMeaningReadWrite.js +1 -1
- package/dist/Live/LiveTranslator/LiveTranslator.js +1 -1
- package/dist/Misc/ChatBubble/ChatBubble.js +2 -0
- package/dist/Misc/HoverText/HoverText.js +7 -2
- package/dist/Misc/NotFound/Classroom.js +1 -1
- package/dist/Misc/NotFound/Course.js +1 -1
- package/dist/hooks/useExerciseState.js +2 -0
- package/dist/index.js +1 -1
- package/dist/theme/getComponents.js +5 -1
- package/dist/utils/index.js +138 -0
- package/dist/utils/voiceImages.js +19 -4
- package/dist/utils/voices.js +12 -2
- package/package.json +1 -1
- package/dist/Cards/SubmissionsCard/SubmissionsCard.js +0 -138
- package/dist/Cards/index.js +0 -2
- package/dist/Dialogs/Code/Code.js +0 -147
- package/dist/Dialogs/Code/Code.test.js +0 -20
- package/dist/Dialogs/Code/package.json +0 -6
- package/dist/Dialogs/CreateTopic/CreateTopic.js +0 -135
- package/dist/Dialogs/CreateTopic/CreateTopic.test.js +0 -15
- package/dist/Dialogs/CreateTopic/package.json +0 -6
- package/dist/Dialogs/FeedbackDialog/FeedbackDialog.js +0 -123
- package/dist/Dialogs/Icons/Icons.js +0 -18
- package/dist/Dialogs/Icons/package.json +0 -6
- package/dist/Dialogs/InactivePopover/InactivePopover.js +0 -71
- package/dist/Dialogs/InviteDialog/upload-emails-csv.png +0 -0
- package/dist/Dialogs/PDFViewer/pdf24.pdf +0 -0
- package/dist/Dialogs/SearchTopic/SearchTopic.js +0 -87
- package/dist/Dialogs/UploadPhrases/upload-phrases-csv.png +0 -0
- package/dist/Exercises/Roleplay/OriginalRoleplay.js +0 -944
- package/dist/Forms/CreateBot/CreateBot.js +0 -77
- package/dist/Forms/CreateBot/CreateBot.test.js +0 -20
- package/dist/Forms/CreateBot/package.json +0 -6
- package/dist/Forms/CreateClassroom/CreateClassroom.js +0 -56
- package/dist/Forms/CreateClassroom/CreateClassroom.test.js +0 -20
- package/dist/Forms/CreateClassroom/package.json +0 -6
- package/dist/Forms/CreateCourse/CreateCourse.js +0 -66
- package/dist/Forms/CreateCourse/CreateCourse.test.js +0 -20
- package/dist/Forms/CreateCourse/package.json +0 -6
- package/dist/Forms/CreateMeetingMultiStepForm/images/input1.js +0 -2
- package/dist/Forms/CreateMeetingMultiStepForm/images/input5.js +0 -2
- package/dist/Forms/CreateMeetingMultiStepForm/images/mode1.js +0 -2
- package/dist/Forms/CreateMeetingMultiStepForm/images/mode2.js +0 -2
- package/dist/Forms/CreateMeetingMultiStepForm/sampleTemplates.js +0 -44
- package/dist/Forms/CreateRoleplay/CreateRoleplay.js +0 -87
- package/dist/Forms/CreateRoleplay/CreateRoleplay.test.js +0 -20
- package/dist/Forms/CreateRoleplay/package.json +0 -6
- package/dist/Forms/CreateUser/CreateUser.js +0 -77
- package/dist/Forms/FeedbackForm/FeedbackForm.js +0 -1036
- package/dist/Forms/MultiStepFormDialog/MultiStepFormDialog.js +0 -354
- package/dist/Forms/MultiStepFormDialog/MultiStepFormDialog.test.js +0 -20
- package/dist/Forms/MultiStepFormDialog/package.json +0 -6
- package/dist/Forms/MultiStepFormHook/MultiStepFormHook.js +0 -279
- package/dist/Forms/RecordingInfo/RecordingInfo.js +0 -140
- package/dist/Lists/Categories/sample.png +0 -0
- package/dist/Lists/Sections/Sections.js +0 -238
- package/dist/Lists/Sections/Sections.test.js +0 -20
- package/dist/Lists/Sections/package.json +0 -6
- package/dist/Lists/Voices/horse.ogm +0 -0
- package/dist/Misc/AxiosRestExample/AxiosRestExample.js +0 -46
- package/dist/Misc/AxiosRestExample/AxiosRestPostExample.js +0 -65
- package/dist/Misc/ColorSelector/ColorSelector.js +0 -143
- package/dist/Misc/ColorSelector/package.json +0 -6
- package/dist/Misc/EmojiInput/css/emoji-mart.css +0 -497
- package/dist/Misc/MedalSelector/PlaceholderMedal.svg +0 -27
- package/dist/Misc/NotFound/Bot.js +0 -62
- package/dist/Misc/NotFound/Bots.js +0 -75
- package/dist/Misc/NotFound/Discussions.js +0 -52
- package/dist/Misc/NotFound/Roleplay.js +0 -62
- package/dist/Misc/NotFound/Roleplays.js +0 -75
- package/dist/Misc/NotFound/Script.js +0 -47
- package/dist/Misc/Notes/Notes.js +0 -77
- package/dist/Misc/Notes/Notes.test.js +0 -20
- package/dist/Misc/Notes/package.json +0 -6
- package/dist/Misc/NualaCreating/Nuala1.js +0 -352
- package/dist/Misc/OnboardingEnrolment/CheckIfCourse.js +0 -33
- package/dist/Misc/OnboardingEnrolment/ClassCodeInvalid.js +0 -14
- package/dist/Misc/OnboardingEnrolment/ClassroomCodeInvalid.js +0 -14
- package/dist/Misc/OnboardingEnrolment/EnrolKey.js +0 -14
- package/dist/Misc/RoleplayChatWaveForm/RoleplayChatWaveForm.js +0 -199
- package/dist/Misc/RoleplayChatWaveForm/package.json +0 -6
- package/dist/Misc/RoleplayWaveform/RoleplayWaveform.js +0 -95
- package/dist/Misc/RoleplayWaveform/package.json +0 -6
- package/dist/Misc/SpeedDial/SpeedDial.js +0 -49
- package/dist/Misc/SpeedDial/package.json +0 -6
- package/dist/Misc/SubmitTextField/SubmitTextField.js +0 -48
- package/dist/Misc/newGenerateCardBackground/Generate Card Background (standalone) (1).html +0 -186
- package/dist/Navigation/ResponsiveStepper/ResponsiveStepper.js +0 -253
- package/dist/Navigation/ResponsiveStepper/ResponsiveStepper.test.js +0 -20
- package/dist/Navigation/ResponsiveStepper/package.json +0 -6
- package/dist/Navigation/ResponsiveTabs/TabContainer.js +0 -28
- package/dist/Tables/CreatedDiscussionsTable/CreatedDiscussionsTable.js +0 -188
- package/dist/Tables/GameRows/GameRows.js +0 -87
- package/dist/Tables/GameRows/package.json +0 -6
- package/dist/Tables/Games/Games.js +0 -258
- package/dist/Tables/Games/package.json +0 -6
- package/dist/Tables/Phrases/Phrases.js +0 -200
- package/dist/Tables/Phrases/Phrases.test.js +0 -20
- package/dist/Tables/Phrases/package.json +0 -6
- package/dist/Tables/Plans/Plans.js +0 -74
- package/dist/Tables/Plans/plans.json +0 -162
- package/dist/Tables/Plans/plans2.json +0 -151
- package/dist/Tables/Progress/Header.js +0 -559
- package/dist/Tables/Progress/README.md +0 -53
- package/dist/Tables/SubmissionsTableCards/SubmissionsTableCards.js +0 -296
- package/dist/Tables/Users/UsersTable.js +0 -159
- package/dist/img/Bronze Medal.svg +0 -545
- package/dist/img/Bronze.svg +0 -349
- package/dist/img/BronzeBadge.svg +0 -493
- package/dist/img/BronzeBadgeWithoutRibbon.svg +0 -317
- package/dist/img/Frame 51.svg +0 -98
- package/dist/img/Frame 52.svg +0 -131
- package/dist/img/Frame 53.svg +0 -139
- package/dist/img/Gold Badge.svg +0 -362
- package/dist/img/Gold Medal.svg +0 -481
- package/dist/img/GoldBadge.svg +0 -470
- package/dist/img/GoldBadgeWithoutRibbon.svg +0 -324
- package/dist/img/NualaCreateImage.svg +0 -129
- package/dist/img/NualangLiveAnwerQuicklyListening.svg +0 -3608
- package/dist/img/NualangLiveListen.png +0 -0
- package/dist/img/NualangLiveListenQuickly.png +0 -0
- package/dist/img/NualangLiveListenToPhrase.svg +0 -4286
- package/dist/img/NualangLivePickAnswer.svg +0 -134
- package/dist/img/NualangLiveResult.png +0 -0
- package/dist/img/NualangLiveRoleplayAnswer.png +0 -0
- package/dist/img/NualangLiveRoleplayConvo.png +0 -0
- package/dist/img/NualangLiveSeePlace.svg +0 -134
- package/dist/img/NualangLiveTranslate.png +0 -0
- package/dist/img/NualangLiveTranslateQuickly.png +0 -0
- package/dist/img/NualangLiveViewConversations.svg +0 -135
- package/dist/img/Polly.svg +0 -18
- package/dist/img/Silver Badge.svg +0 -523
- package/dist/img/Silver Medal.svg +0 -549
- package/dist/img/SilverBadge.svg +0 -655
- package/dist/img/SilverBadgeWithoutRibbon.svg +0 -491
- package/dist/img/avatars/0-nuala-001.svg +0 -34
- package/dist/img/avatars/0-nuala-002.svg +0 -25
- package/dist/img/avatars/0-nuala-003.svg +0 -27
- package/dist/img/avatars/0-nuala-004.svg +0 -41
- package/dist/img/avatars/0-nuala-005.svg +0 -33
- package/dist/img/avatars/0-nuala-006.svg +0 -32
- package/dist/img/avatars/0-nuala-007.svg +0 -37
- package/dist/img/avatars/0-nuala-008.svg +0 -21
- package/dist/img/avatars/0-nuala-009.svg +0 -21
- package/dist/img/avatars/0-nuala-010.svg +0 -39
- package/dist/img/avatars/0-nuala-011.svg +0 -33
- package/dist/img/avatars/0-nuala-012.svg +0 -43
- package/dist/img/avatars/0-nuala-013.svg +0 -36
- package/dist/img/avatars/0-nuala-014.svg +0 -30
- package/dist/img/avatars/0-nuala-015.svg +0 -28
- package/dist/img/avatars/0-nuala-016.svg +0 -24
- package/dist/img/avatars/0-nuala-017.svg +0 -31
- package/dist/img/avatars/0-nuala-018.svg +0 -29
- package/dist/img/avatars/0-nuala-019.svg +0 -57
- package/dist/img/avatars/0-nuala-020.svg +0 -30
- package/dist/img/avatars/0-nuala-021.svg +0 -28
- package/dist/img/avatars/001-man.svg +0 -104
- package/dist/img/avatars/002-girl.svg +0 -92
- package/dist/img/avatars/006-woman-1.svg +0 -99
- package/dist/img/avatars/028-woman-7.svg +0 -95
- package/dist/img/avatars/029-man-8.svg +0 -95
- package/dist/img/avatars/035-woman-9.svg +0 -86
- package/dist/img/avatars/066-boy-6.svg +0 -91
- package/dist/img/avatars/1-teacher-001.svg +0 -38
- package/dist/img/avatars/1-teacher-002.svg +0 -52
- package/dist/img/avatars/1-teacher-003.svg +0 -86
- package/dist/img/avatars/avatar1.png +0 -0
- package/dist/img/avatars/avatar2.png +0 -0
- package/dist/img/avatars/nuala_10_speaking.svg +0 -45
- package/dist/img/avatars/nuala_11_speaking.svg +0 -41
- package/dist/img/avatars/nuala_12_speaking.svg +0 -49
- package/dist/img/avatars/nuala_13_speaking.svg +0 -44
- package/dist/img/avatars/nuala_14_speaking.svg +0 -36
- package/dist/img/avatars/nuala_15_speaking.svg +0 -36
- package/dist/img/avatars/nuala_16_speaking.svg +0 -32
- package/dist/img/avatars/nuala_17_speaking.svg +0 -39
- package/dist/img/avatars/nuala_18_speaking.svg +0 -37
- package/dist/img/avatars/nuala_19_speaking.svg +0 -65
- package/dist/img/avatars/nuala_1_celebrating.svg +0 -30
- package/dist/img/avatars/nuala_1_speaking.svg +0 -37
- package/dist/img/avatars/nuala_20_speaking.svg +0 -38
- package/dist/img/avatars/nuala_21_speaking.svg +0 -36
- package/dist/img/avatars/nuala_2_speaking.svg +0 -29
- package/dist/img/avatars/nuala_3_speaking.svg +0 -28
- package/dist/img/avatars/nuala_4_speaking.svg +0 -41
- package/dist/img/avatars/nuala_5_speaking.svg +0 -36
- package/dist/img/avatars/nuala_6_speaking.svg +0 -36
- package/dist/img/avatars/nuala_7_speaking.svg +0 -43
- package/dist/img/avatars/nuala_8_speaking.svg +0 -29
- package/dist/img/avatars/nuala_9_speaking.svg +0 -27
- package/dist/img/dashboard.svg +0 -104
- package/dist/img/defaultImg.png +0 -0
- package/dist/img/nua-glyph.svg +0 -6
- package/dist/img/nuala-silhouette.svg +0 -43
- package/dist/img/nuala_1_celebrating.svg +0 -33
- package/dist/img/nuala_2_celebrating.svg +0 -20
- package/dist/img/nuala_3_celebrating.svg +0 -21
- package/dist/img/nuala_4_celebrating.svg +0 -30
- package/dist/img/nuala_5_celebrating.svg +0 -30
- package/dist/img/nuala_celebrating.svg +0 -39
- package/dist/img/nuala_speaking_4.svg +0 -41
- package/dist/img/nualas.svg +0 -185
- package/dist/img/soundWave.png +0 -0
- package/dist/img/stars1.svg +0 -44
- package/dist/img/stars2.svg +0 -30
- package/dist/img/teacher.svg +0 -89
- package/dist/img/work-chat.svg +0 -1
- package/dist/img/world.svg +0 -41
|
@@ -2,8 +2,10 @@ import { useState, memo } from "react";
|
|
|
2
2
|
import PropTypes from "prop-types";
|
|
3
3
|
import { useInView } from "react-intersection-observer";
|
|
4
4
|
// Mui
|
|
5
|
-
import { Button, Box, Typography, IconButton, Menu, MenuItem, Checkbox, Tooltip, CardActions, CardActionArea, Divider } from "@mui/material";
|
|
5
|
+
import { Button, Box, Typography, IconButton, Menu, MenuItem, Checkbox, Tooltip, CardActions, CardActionArea, Divider, Chip } from "@mui/material";
|
|
6
6
|
import Skeleton from "@mui/material/Skeleton";
|
|
7
|
+
import { styled } from "@mui/material/styles";
|
|
8
|
+
import { tooltipClasses } from "@mui/material/Tooltip";
|
|
7
9
|
import Group from "@mui/icons-material/Group";
|
|
8
10
|
import ExitToApp from "@mui/icons-material/ExitToApp";
|
|
9
11
|
import Close from "@mui/icons-material/Close";
|
|
@@ -159,6 +161,141 @@ function OverflowMenu({
|
|
|
159
161
|
})]
|
|
160
162
|
});
|
|
161
163
|
}
|
|
164
|
+
const DIFFICULTY_ORDER = ["A1", "A2", "B1", "B2", "C1", "C2"];
|
|
165
|
+
const MAX_VISIBLE_DIFFICULTIES = 2;
|
|
166
|
+
const DifficultyOverflowTooltip = styled(({
|
|
167
|
+
className,
|
|
168
|
+
...props
|
|
169
|
+
}) => /*#__PURE__*/_jsx(Tooltip, {
|
|
170
|
+
...props,
|
|
171
|
+
classes: {
|
|
172
|
+
popper: className
|
|
173
|
+
}
|
|
174
|
+
}))(({
|
|
175
|
+
theme
|
|
176
|
+
}) => ({
|
|
177
|
+
[`& .${tooltipClasses.tooltip}`]: {
|
|
178
|
+
backgroundColor: theme.palette.background.paper,
|
|
179
|
+
color: theme.palette.text.primary,
|
|
180
|
+
border: `1px solid ${theme.palette.divider}`,
|
|
181
|
+
borderRadius: theme.shape.borderRadius,
|
|
182
|
+
boxShadow: theme.shadows[4],
|
|
183
|
+
padding: theme.spacing(1.5, 2),
|
|
184
|
+
minWidth: 160,
|
|
185
|
+
maxWidth: 260
|
|
186
|
+
}
|
|
187
|
+
}));
|
|
188
|
+
function normalizeDifficulties(difficulty) {
|
|
189
|
+
const values = Array.isArray(difficulty) ? difficulty : difficulty ? [difficulty] : [];
|
|
190
|
+
return DIFFICULTY_ORDER.filter(level => values.includes(level));
|
|
191
|
+
}
|
|
192
|
+
function difficultyChipSx(theme, compact) {
|
|
193
|
+
return {
|
|
194
|
+
height: compact ? 20 : 24,
|
|
195
|
+
backgroundColor: theme.palette.action.selected,
|
|
196
|
+
color: theme.palette.text.secondary,
|
|
197
|
+
"& .MuiChip-label": {
|
|
198
|
+
fontSize: compact ? "0.7rem" : "0.75rem",
|
|
199
|
+
px: 0.75
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function CourseDifficulty({
|
|
204
|
+
t,
|
|
205
|
+
difficulty,
|
|
206
|
+
compact
|
|
207
|
+
}) {
|
|
208
|
+
const levels = normalizeDifficulties(difficulty);
|
|
209
|
+
if (levels.length === 0) {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
const levelLabels = levels.map(level => t(`card_${level}`));
|
|
213
|
+
const visibleLabels = levelLabels.slice(0, MAX_VISIBLE_DIFFICULTIES);
|
|
214
|
+
const overflowLabels = levelLabels.slice(MAX_VISIBLE_DIFFICULTIES);
|
|
215
|
+
return /*#__PURE__*/_jsxs(Box, {
|
|
216
|
+
component: "span",
|
|
217
|
+
role: "group",
|
|
218
|
+
"aria-label": `${t("difficulty")}: ${levelLabels.join(", ")}`,
|
|
219
|
+
sx: {
|
|
220
|
+
display: "inline-flex",
|
|
221
|
+
alignItems: "center",
|
|
222
|
+
gap: 0.5
|
|
223
|
+
},
|
|
224
|
+
"data-cy": "course-difficulty",
|
|
225
|
+
children: [/*#__PURE__*/_jsx(DifficultyIcon, {
|
|
226
|
+
sx: {
|
|
227
|
+
...classes.infoIcon,
|
|
228
|
+
fontSize: compact ? 18 : undefined
|
|
229
|
+
},
|
|
230
|
+
fontSize: "small"
|
|
231
|
+
}), visibleLabels.map((label, index) => /*#__PURE__*/_jsx(Chip, {
|
|
232
|
+
label: label,
|
|
233
|
+
size: "small",
|
|
234
|
+
"data-cy": `course-difficulty-chip-${index}`,
|
|
235
|
+
sx: theme => difficultyChipSx(theme, compact)
|
|
236
|
+
}, label)), overflowLabels.length > 0 && /*#__PURE__*/_jsx(DifficultyOverflowTooltip, {
|
|
237
|
+
placement: "bottom-start",
|
|
238
|
+
title: /*#__PURE__*/_jsxs(Box, {
|
|
239
|
+
sx: {
|
|
240
|
+
minWidth: 140
|
|
241
|
+
},
|
|
242
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
243
|
+
variant: "caption",
|
|
244
|
+
sx: {
|
|
245
|
+
display: "block",
|
|
246
|
+
color: "text.secondary",
|
|
247
|
+
fontWeight: 700,
|
|
248
|
+
letterSpacing: 0.5,
|
|
249
|
+
textTransform: "uppercase",
|
|
250
|
+
pb: 1,
|
|
251
|
+
borderBottom: "1px solid",
|
|
252
|
+
borderColor: "divider"
|
|
253
|
+
},
|
|
254
|
+
children: t("difficulties")
|
|
255
|
+
}), levelLabels.map((label, index) => /*#__PURE__*/_jsxs(Box, {
|
|
256
|
+
sx: {
|
|
257
|
+
display: "flex",
|
|
258
|
+
alignItems: "center",
|
|
259
|
+
gap: 1,
|
|
260
|
+
py: 0.75,
|
|
261
|
+
borderBottom: index < levelLabels.length - 1 ? "1px solid" : "none",
|
|
262
|
+
borderColor: "divider"
|
|
263
|
+
},
|
|
264
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
265
|
+
component: "span",
|
|
266
|
+
sx: {
|
|
267
|
+
color: "text.disabled",
|
|
268
|
+
fontSize: "1.3rem",
|
|
269
|
+
lineHeight: 1
|
|
270
|
+
},
|
|
271
|
+
children: "\u2022"
|
|
272
|
+
}), /*#__PURE__*/_jsx(Typography, {
|
|
273
|
+
variant: "body2",
|
|
274
|
+
sx: {
|
|
275
|
+
color: "text.primary"
|
|
276
|
+
},
|
|
277
|
+
children: label
|
|
278
|
+
})]
|
|
279
|
+
}, label))]
|
|
280
|
+
}),
|
|
281
|
+
children: /*#__PURE__*/_jsx(Chip, {
|
|
282
|
+
label: `+${overflowLabels.length}`,
|
|
283
|
+
size: "small",
|
|
284
|
+
"aria-label": `${t("difficulty")}: ${levelLabels.join(", ")}`,
|
|
285
|
+
"data-cy": "course-difficulty-overflow",
|
|
286
|
+
sx: theme => ({
|
|
287
|
+
...difficultyChipSx(theme, compact),
|
|
288
|
+
fontWeight: 600
|
|
289
|
+
})
|
|
290
|
+
})
|
|
291
|
+
})]
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
CourseDifficulty.propTypes = {
|
|
295
|
+
t: PropTypes.func.isRequired,
|
|
296
|
+
difficulty: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
|
297
|
+
compact: PropTypes.bool
|
|
298
|
+
};
|
|
162
299
|
function CourseLoading({
|
|
163
300
|
compact = false
|
|
164
301
|
}) {
|
|
@@ -278,7 +415,7 @@ function CourseCard({
|
|
|
278
415
|
limit: 1000
|
|
279
416
|
}
|
|
280
417
|
}, {
|
|
281
|
-
enabled: !!course?.courseId && isOutlineOpen
|
|
418
|
+
enabled: !!course?.courseId && (isOutlineOpen || visible)
|
|
282
419
|
});
|
|
283
420
|
const {
|
|
284
421
|
membersQuery,
|
|
@@ -356,6 +493,7 @@ function CourseCard({
|
|
|
356
493
|
handleLeaveCourse(courseId);
|
|
357
494
|
};
|
|
358
495
|
const courseSections = sectionsQuery.isSuccess ? sectionsQuery.data.Items : [];
|
|
496
|
+
const topicDifficulties = sectionsQuery.isSuccess ? [...new Set(courseSections.flatMap(section => section.topics || []).map(topic => topic.difficulty).filter(Boolean))] : [];
|
|
359
497
|
const deleteCourse = async () => {
|
|
360
498
|
const confirmed = await confirm(t("delete_course"), t("delete_course_confirmation"), "", true, t("course_deletion_note", {
|
|
361
499
|
sectionCount: courseSections ? courseSections.length : 0
|
|
@@ -497,16 +635,10 @@ function CourseCard({
|
|
|
497
635
|
columnGap: 1.5,
|
|
498
636
|
rowGap: 0.5
|
|
499
637
|
},
|
|
500
|
-
children: [
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
sx: {
|
|
505
|
-
...classes.infoIcon,
|
|
506
|
-
fontSize: compact ? 18 : undefined
|
|
507
|
-
},
|
|
508
|
-
fontSize: "small"
|
|
509
|
-
}), t(`card_${course.difficulty}`)]
|
|
638
|
+
children: [/*#__PURE__*/_jsx(CourseDifficulty, {
|
|
639
|
+
t: t,
|
|
640
|
+
difficulty: topicDifficulties.length > 0 ? topicDifficulties : course?.difficulty,
|
|
641
|
+
compact: compact
|
|
510
642
|
}), (course?.pdf || course.topicsWithPdf && Object.keys(course?.topicsWithPdf)?.length > 0) && /*#__PURE__*/_jsxs(CardInfoSectionItem, {
|
|
511
643
|
variant: "caption",
|
|
512
644
|
component: "span",
|
|
@@ -673,7 +805,7 @@ MemoizedCourse.propTypes = {
|
|
|
673
805
|
courseId: PropTypes.string.isRequired,
|
|
674
806
|
courseName: PropTypes.string.isRequired,
|
|
675
807
|
description: PropTypes.string.isRequired,
|
|
676
|
-
difficulty: PropTypes.string,
|
|
808
|
+
difficulty: PropTypes.oneOfType([PropTypes.string, PropTypes.arrayOf(PropTypes.string)]),
|
|
677
809
|
picture: PropTypes.string.isRequired,
|
|
678
810
|
learnLang: PropTypes.string.isRequired,
|
|
679
811
|
forLang: PropTypes.string.isRequired,
|
|
@@ -272,6 +272,7 @@ export default function Message({
|
|
|
272
272
|
speak,
|
|
273
273
|
stopSpeaking,
|
|
274
274
|
voice,
|
|
275
|
+
voiceLanguageCode,
|
|
275
276
|
voicePitch,
|
|
276
277
|
voiceSpeed,
|
|
277
278
|
handleEditMessage,
|
|
@@ -471,6 +472,7 @@ export default function Message({
|
|
|
471
472
|
forLang: forLang,
|
|
472
473
|
handleSpeak: handleSpeak,
|
|
473
474
|
voice: voice,
|
|
475
|
+
voiceLanguageCode: voiceLanguageCode,
|
|
474
476
|
voicePitch: voicePitch,
|
|
475
477
|
voiceSpeed: voiceSpeed,
|
|
476
478
|
disableTranslation: disableTranslation,
|
|
@@ -538,6 +540,7 @@ export default function Message({
|
|
|
538
540
|
forLang: forLang,
|
|
539
541
|
handleSpeak: handleSpeak,
|
|
540
542
|
voice: voice,
|
|
543
|
+
voiceLanguageCode: voiceLanguageCode,
|
|
541
544
|
voicePitch: voicePitch,
|
|
542
545
|
voiceSpeed: voiceSpeed,
|
|
543
546
|
disableTranslation: disableTranslation,
|
|
@@ -623,7 +626,7 @@ export default function Message({
|
|
|
623
626
|
children: /*#__PURE__*/_jsx(IconButton, {
|
|
624
627
|
color: "inherit",
|
|
625
628
|
disabled: isSpeaking || recognizing || muteSound || isMessagePlaying || showTranslation,
|
|
626
|
-
onClick: () => speak(text, voice, false, messageContainerIdSelector,
|
|
629
|
+
onClick: () => speak(text, voice, false, messageContainerIdSelector, voiceLanguageCode, false, voicePitch),
|
|
627
630
|
size: "small",
|
|
628
631
|
"aria-label": messageIndex !== -1 ? accessibilityText ? accessibilityText : `Speak message ${messageIndex + 1}` : accessibilityText ? accessibilityText : `Speak message`,
|
|
629
632
|
sx: {
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { render, screen, fireEvent, cleanup } from "@testing-library/react";
|
|
2
|
+
import { describe, it, expect, vi, afterEach } from "vitest";
|
|
3
|
+
import { createTheme, ThemeProvider } from "@mui/material/styles";
|
|
4
|
+
import Message from "./Message";
|
|
5
|
+
|
|
6
|
+
// Messages spreads each message over these props, so a roleplay message carries
|
|
7
|
+
// its own actor's voice and accent; a bot chat supplies one voice for the
|
|
8
|
+
// whole chat. Replaying must use whichever applies, not the language default.
|
|
9
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
10
|
+
const renderMessage = (overrides = {}) => {
|
|
11
|
+
const theme = createTheme();
|
|
12
|
+
const speak = vi.fn();
|
|
13
|
+
render(/*#__PURE__*/_jsx(ThemeProvider, {
|
|
14
|
+
theme: theme,
|
|
15
|
+
children: /*#__PURE__*/_jsx(Message, {
|
|
16
|
+
t: k => k,
|
|
17
|
+
text: "Kia ora",
|
|
18
|
+
speak: speak,
|
|
19
|
+
messageIndex: 0,
|
|
20
|
+
index: 0,
|
|
21
|
+
messageCount: 1,
|
|
22
|
+
messages: [{
|
|
23
|
+
text: "Kia ora"
|
|
24
|
+
}],
|
|
25
|
+
...overrides
|
|
26
|
+
})
|
|
27
|
+
}));
|
|
28
|
+
return speak;
|
|
29
|
+
};
|
|
30
|
+
describe("Message replay", () => {
|
|
31
|
+
afterEach(cleanup);
|
|
32
|
+
it("speaks with the accent the message carries", () => {
|
|
33
|
+
const speak = renderMessage({
|
|
34
|
+
voice: "Aria",
|
|
35
|
+
voiceLanguageCode: "en-NZ"
|
|
36
|
+
});
|
|
37
|
+
fireEvent.click(screen.getByRole("button", {
|
|
38
|
+
name: /Speak message/i
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
// 5th positional argument of speak() is the accent.
|
|
42
|
+
expect(speak).toHaveBeenCalled();
|
|
43
|
+
expect(speak.mock.calls[0][1]).toBe("Aria");
|
|
44
|
+
expect(speak.mock.calls[0][4]).toBe("en-NZ");
|
|
45
|
+
});
|
|
46
|
+
it("sends no accent when nothing supplies one", () => {
|
|
47
|
+
const speak = renderMessage({
|
|
48
|
+
voice: "Joanna"
|
|
49
|
+
});
|
|
50
|
+
fireEvent.click(screen.getByRole("button", {
|
|
51
|
+
name: /Speak message/i
|
|
52
|
+
}));
|
|
53
|
+
expect(speak.mock.calls[0][4]).toBeUndefined();
|
|
54
|
+
});
|
|
55
|
+
});
|
|
@@ -217,6 +217,7 @@ function App({
|
|
|
217
217
|
isCollapsed,
|
|
218
218
|
setIsCollapsed,
|
|
219
219
|
isUserInternal,
|
|
220
|
+
isCommunityAccessRestricted,
|
|
220
221
|
handleQuickCreateClassroom,
|
|
221
222
|
handleQuickCreateCourse,
|
|
222
223
|
learnLang,
|
|
@@ -303,6 +304,18 @@ function App({
|
|
|
303
304
|
};
|
|
304
305
|
const isSearchActive = searchText || isMobileSearchExpanded;
|
|
305
306
|
const isTeacher = user && user["custom:role"] === "teach";
|
|
307
|
+
const isSearchAllowed = isTeacher && !isCommunityAccessRestricted;
|
|
308
|
+
|
|
309
|
+
// If the restriction flag loads/flips async while search is already active
|
|
310
|
+
// (e.g. mobile search expanded, or text typed in), force-close it rather
|
|
311
|
+
// than only gating the initial render - otherwise a user already mid-search
|
|
312
|
+
// keeps access until they manually close it.
|
|
313
|
+
useEffect(() => {
|
|
314
|
+
if (!isSearchAllowed && (searchText || isMobileSearchExpanded)) {
|
|
315
|
+
setSearchText("");
|
|
316
|
+
setIsMobileSearchExpanded(false);
|
|
317
|
+
}
|
|
318
|
+
}, [isSearchAllowed, searchText, isMobileSearchExpanded]);
|
|
306
319
|
const isUpgradePossible = subscription && subscription.isUpgradePossible;
|
|
307
320
|
const hidden = useMediaQuery(theme => theme.breakpoints.down("sm"));
|
|
308
321
|
return /*#__PURE__*/_jsxs("div", {
|
|
@@ -334,7 +347,7 @@ function App({
|
|
|
334
347
|
})]
|
|
335
348
|
}) : /*#__PURE__*/_jsxs(_Fragment, {
|
|
336
349
|
children: [isLgScreen ? /*#__PURE__*/_jsx(_Fragment, {
|
|
337
|
-
children:
|
|
350
|
+
children: isSearchAllowed && /*#__PURE__*/_jsxs(_Fragment, {
|
|
338
351
|
children: [/*#__PURE__*/_jsx(AppSearch, {
|
|
339
352
|
t: t,
|
|
340
353
|
navigate: navigate,
|
|
@@ -348,7 +361,7 @@ function App({
|
|
|
348
361
|
})]
|
|
349
362
|
})
|
|
350
363
|
}) : /*#__PURE__*/_jsx(_Fragment, {
|
|
351
|
-
children:
|
|
364
|
+
children: isSearchAllowed && /*#__PURE__*/_jsx(_Fragment, {
|
|
352
365
|
children: /*#__PURE__*/_jsx(Tooltip, {
|
|
353
366
|
title: t("search"),
|
|
354
367
|
enterDelay: 300,
|
|
@@ -386,16 +399,20 @@ function App({
|
|
|
386
399
|
startIcon: /*#__PURE__*/_jsx(Add, {}),
|
|
387
400
|
color: "secondary",
|
|
388
401
|
id: "appbar-create-button",
|
|
402
|
+
disabled: !subscription?.isSubscriptionLoaded,
|
|
389
403
|
children: t("create")
|
|
390
404
|
}) : /*#__PURE__*/_jsx(Tooltip, {
|
|
391
405
|
title: t("create"),
|
|
392
406
|
enterDelay: 300,
|
|
393
|
-
children: /*#__PURE__*/_jsx(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
407
|
+
children: /*#__PURE__*/_jsx("span", {
|
|
408
|
+
children: /*#__PURE__*/_jsx(IconButton, {
|
|
409
|
+
color: "inherit",
|
|
410
|
+
"aria-label": t("create"),
|
|
411
|
+
"aria-haspopup": "true",
|
|
412
|
+
onClick: handleClickCreate,
|
|
413
|
+
disabled: !subscription?.isSubscriptionLoaded,
|
|
414
|
+
children: /*#__PURE__*/_jsx(Add, {})
|
|
415
|
+
})
|
|
399
416
|
})
|
|
400
417
|
})), isLgScreen && /*#__PURE__*/_jsx(WhatsNew, {
|
|
401
418
|
t: t,
|
|
@@ -29,6 +29,7 @@ export default function FindChatbotDialog({
|
|
|
29
29
|
onLoadMoreCommunity,
|
|
30
30
|
hasMoreCommunity = false,
|
|
31
31
|
isLoadingMoreCommunity = false,
|
|
32
|
+
isCommunityAccessRestricted = false,
|
|
32
33
|
t = text => text
|
|
33
34
|
}) {
|
|
34
35
|
const scrollRef = useRef(null);
|
|
@@ -39,6 +40,8 @@ export default function FindChatbotDialog({
|
|
|
39
40
|
threshold: 0
|
|
40
41
|
});
|
|
41
42
|
const [tab, setTab] = useState(0);
|
|
43
|
+
// Restricted users are always kept on the library tab, regardless of state.
|
|
44
|
+
const effectiveTab = isCommunityAccessRestricted ? 0 : tab;
|
|
42
45
|
const [selectedBotIds, setSelectedBotIds] = useState([]);
|
|
43
46
|
const [search, setSearch] = useState("");
|
|
44
47
|
const [filters, setFilters] = useState({
|
|
@@ -81,12 +84,22 @@ export default function FindChatbotDialog({
|
|
|
81
84
|
reset();
|
|
82
85
|
handleClose();
|
|
83
86
|
};
|
|
87
|
+
|
|
88
|
+
// If restriction flips on while the dialog is already open (e.g. the flag
|
|
89
|
+
// loads async), a previously-selected community chatbot could otherwise
|
|
90
|
+
// stay selected/added even though the Community tab is now locked out.
|
|
84
91
|
useEffect(() => {
|
|
85
|
-
if (
|
|
92
|
+
if (isCommunityAccessRestricted) {
|
|
93
|
+
reset();
|
|
94
|
+
}
|
|
95
|
+
}, [isCommunityAccessRestricted]);
|
|
96
|
+
useEffect(() => {
|
|
97
|
+
if (sentinelInView && effectiveTab === 1 && hasMoreCommunity && !isLoadingMoreCommunity) {
|
|
86
98
|
onLoadMoreCommunity?.();
|
|
87
99
|
}
|
|
88
|
-
}, [sentinelInView,
|
|
100
|
+
}, [sentinelInView, effectiveTab, hasMoreCommunity, isLoadingMoreCommunity, onLoadMoreCommunity]);
|
|
89
101
|
const handleTabChange = (_, v) => {
|
|
102
|
+
if (isCommunityAccessRestricted) return;
|
|
90
103
|
setTab(v);
|
|
91
104
|
setSearch("");
|
|
92
105
|
setFilters({
|
|
@@ -114,9 +127,9 @@ export default function FindChatbotDialog({
|
|
|
114
127
|
}
|
|
115
128
|
return result;
|
|
116
129
|
};
|
|
117
|
-
const currentChatbots =
|
|
118
|
-
const isLoading =
|
|
119
|
-
const sectionLabel =
|
|
130
|
+
const currentChatbots = effectiveTab === 0 ? filterChatbots(libraryChatbots) : applyCommunityFilters(communityChatbots);
|
|
131
|
+
const isLoading = effectiveTab === 0 ? isLoadingLibrary : isLoadingCommunity;
|
|
132
|
+
const sectionLabel = effectiveTab === 0 ? t("add_own_exercise_message", {
|
|
120
133
|
exerciseName: t("chatbots").toLowerCase()
|
|
121
134
|
}) : t("or_select_a_community_template_desc");
|
|
122
135
|
const addButtonLabel = selectedBotIds.length > 0 ? `${t("add_chatbots")} (${selectedBotIds.length})` : t("add_chatbots");
|
|
@@ -166,17 +179,18 @@ export default function FindChatbotDialog({
|
|
|
166
179
|
borderColor: "divider"
|
|
167
180
|
},
|
|
168
181
|
children: /*#__PURE__*/_jsxs(Tabs, {
|
|
169
|
-
value:
|
|
182
|
+
value: effectiveTab,
|
|
170
183
|
onChange: handleTabChange,
|
|
171
184
|
indicatorColor: "primary",
|
|
172
185
|
textColor: "primary",
|
|
173
186
|
children: [/*#__PURE__*/_jsx(Tab, {
|
|
174
187
|
label: t("my_library") || "My Library"
|
|
175
188
|
}), /*#__PURE__*/_jsx(Tab, {
|
|
176
|
-
label: t("community") || "Community"
|
|
189
|
+
label: t("community") || "Community",
|
|
190
|
+
disabled: isCommunityAccessRestricted
|
|
177
191
|
})]
|
|
178
192
|
})
|
|
179
|
-
}),
|
|
193
|
+
}), effectiveTab === 1 && /*#__PURE__*/_jsx(Box, {
|
|
180
194
|
sx: {
|
|
181
195
|
px: 3,
|
|
182
196
|
py: {
|
|
@@ -232,7 +246,7 @@ export default function FindChatbotDialog({
|
|
|
232
246
|
isSelected: bot => selectedBotIds.includes(bot.botId),
|
|
233
247
|
compact: true,
|
|
234
248
|
handleViewUserProfile: () => {}
|
|
235
|
-
}),
|
|
249
|
+
}), effectiveTab === 1 && /*#__PURE__*/_jsx(Box, {
|
|
236
250
|
ref: sentinelRef,
|
|
237
251
|
sx: {
|
|
238
252
|
height: 40,
|
|
@@ -17,6 +17,7 @@ export default function FindScenarioDialog({
|
|
|
17
17
|
onLoadMoreCommunity,
|
|
18
18
|
hasMoreCommunity = false,
|
|
19
19
|
isLoadingMoreCommunity = false,
|
|
20
|
+
isCommunityAccessRestricted = false,
|
|
20
21
|
t = text => text
|
|
21
22
|
}) {
|
|
22
23
|
const scrollRef = useRef(null);
|
|
@@ -27,6 +28,8 @@ export default function FindScenarioDialog({
|
|
|
27
28
|
threshold: 0
|
|
28
29
|
});
|
|
29
30
|
const [tab, setTab] = useState(0);
|
|
31
|
+
// Restricted users are always kept on the library tab, regardless of state.
|
|
32
|
+
const effectiveTab = isCommunityAccessRestricted ? 0 : tab;
|
|
30
33
|
const [selectedRoleplayIds, setSelectedRoleplayIds] = useState([]);
|
|
31
34
|
const [search, setSearch] = useState("");
|
|
32
35
|
const [filters, setFilters] = useState({
|
|
@@ -69,12 +72,22 @@ export default function FindScenarioDialog({
|
|
|
69
72
|
reset();
|
|
70
73
|
handleClose();
|
|
71
74
|
};
|
|
75
|
+
|
|
76
|
+
// If restriction flips on while the dialog is already open (e.g. the flag
|
|
77
|
+
// loads async), a previously-selected community scenario could otherwise
|
|
78
|
+
// stay selected/added even though the Community tab is now locked out.
|
|
72
79
|
useEffect(() => {
|
|
73
|
-
if (
|
|
80
|
+
if (isCommunityAccessRestricted) {
|
|
81
|
+
reset();
|
|
82
|
+
}
|
|
83
|
+
}, [isCommunityAccessRestricted]);
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
if (sentinelInView && effectiveTab === 1 && hasMoreCommunity && !isLoadingMoreCommunity) {
|
|
74
86
|
onLoadMoreCommunity?.();
|
|
75
87
|
}
|
|
76
|
-
}, [sentinelInView,
|
|
88
|
+
}, [sentinelInView, effectiveTab, hasMoreCommunity, isLoadingMoreCommunity, onLoadMoreCommunity]);
|
|
77
89
|
const handleTabChange = (_, v) => {
|
|
90
|
+
if (isCommunityAccessRestricted) return;
|
|
78
91
|
setTab(v);
|
|
79
92
|
setSearch("");
|
|
80
93
|
setFilters({
|
|
@@ -102,9 +115,9 @@ export default function FindScenarioDialog({
|
|
|
102
115
|
}
|
|
103
116
|
return result;
|
|
104
117
|
};
|
|
105
|
-
const currentScenarios =
|
|
106
|
-
const isLoading =
|
|
107
|
-
const sectionLabel =
|
|
118
|
+
const currentScenarios = effectiveTab === 0 ? filterScenarios(libraryScenarios) : applyCommunityFilters(communityScenarios);
|
|
119
|
+
const isLoading = effectiveTab === 0 ? isLoadingLibrary : isLoadingCommunity;
|
|
120
|
+
const sectionLabel = effectiveTab === 0 ? t("add_own_exercise_message", {
|
|
108
121
|
exerciseName: t("scenarios").toLowerCase()
|
|
109
122
|
}) : t("or_select_a_community_template_desc");
|
|
110
123
|
const addButtonLabel = selectedRoleplayIds.length > 0 ? `${t("add_scenarios")} (${selectedRoleplayIds.length})` : t("add_scenarios");
|
|
@@ -154,17 +167,18 @@ export default function FindScenarioDialog({
|
|
|
154
167
|
borderColor: "divider"
|
|
155
168
|
},
|
|
156
169
|
children: /*#__PURE__*/_jsxs(Tabs, {
|
|
157
|
-
value:
|
|
170
|
+
value: effectiveTab,
|
|
158
171
|
onChange: handleTabChange,
|
|
159
172
|
indicatorColor: "primary",
|
|
160
173
|
textColor: "primary",
|
|
161
174
|
children: [/*#__PURE__*/_jsx(Tab, {
|
|
162
175
|
label: t("my_library") || "My Library"
|
|
163
176
|
}), /*#__PURE__*/_jsx(Tab, {
|
|
164
|
-
label: t("community") || "Community"
|
|
177
|
+
label: t("community") || "Community",
|
|
178
|
+
disabled: isCommunityAccessRestricted
|
|
165
179
|
})]
|
|
166
180
|
})
|
|
167
|
-
}),
|
|
181
|
+
}), effectiveTab === 1 && /*#__PURE__*/_jsx(Box, {
|
|
168
182
|
sx: {
|
|
169
183
|
px: 3,
|
|
170
184
|
py: {
|
|
@@ -220,7 +234,7 @@ export default function FindScenarioDialog({
|
|
|
220
234
|
isSelected: roleplay => selectedRoleplayIds.includes(roleplay.roleplayId),
|
|
221
235
|
compact: true,
|
|
222
236
|
handleViewUserProfile: () => {}
|
|
223
|
-
}),
|
|
237
|
+
}), effectiveTab === 1 && /*#__PURE__*/_jsx(Box, {
|
|
224
238
|
ref: sentinelRef,
|
|
225
239
|
sx: {
|
|
226
240
|
height: 40,
|
|
@@ -150,6 +150,7 @@ export default function FindTopicsDialog({
|
|
|
150
150
|
onLoadMoreCommunity,
|
|
151
151
|
hasMoreCommunity = false,
|
|
152
152
|
isLoadingMoreCommunity = false,
|
|
153
|
+
isCommunityAccessRestricted = false,
|
|
153
154
|
t = text => text
|
|
154
155
|
}) {
|
|
155
156
|
const scrollRef = useRef(null);
|
|
@@ -160,6 +161,8 @@ export default function FindTopicsDialog({
|
|
|
160
161
|
threshold: 0
|
|
161
162
|
});
|
|
162
163
|
const [tab, setTab] = useState(0);
|
|
164
|
+
// Restricted users are always kept on the library tab, regardless of state.
|
|
165
|
+
const effectiveTab = isCommunityAccessRestricted ? 0 : tab;
|
|
163
166
|
const [selectedTopicIds, setSelectedTopicIds] = useState([]);
|
|
164
167
|
const [search, setSearch] = useState("");
|
|
165
168
|
const [filters, setFilters] = useState({
|
|
@@ -202,12 +205,22 @@ export default function FindTopicsDialog({
|
|
|
202
205
|
reset();
|
|
203
206
|
handleClose();
|
|
204
207
|
};
|
|
208
|
+
|
|
209
|
+
// If restriction flips on while the dialog is already open (e.g. the flag
|
|
210
|
+
// loads async), a previously-selected community topic could otherwise stay
|
|
211
|
+
// selected/added even though the Community tab is now locked out - clear it.
|
|
205
212
|
useEffect(() => {
|
|
206
|
-
if (
|
|
213
|
+
if (isCommunityAccessRestricted) {
|
|
214
|
+
reset();
|
|
215
|
+
}
|
|
216
|
+
}, [isCommunityAccessRestricted]);
|
|
217
|
+
useEffect(() => {
|
|
218
|
+
if (sentinelInView && effectiveTab === 1 && hasMoreCommunity && !isLoadingMoreCommunity) {
|
|
207
219
|
onLoadMoreCommunity?.();
|
|
208
220
|
}
|
|
209
|
-
}, [sentinelInView,
|
|
221
|
+
}, [sentinelInView, effectiveTab, hasMoreCommunity, isLoadingMoreCommunity, onLoadMoreCommunity]);
|
|
210
222
|
const handleTabChange = (_, v) => {
|
|
223
|
+
if (isCommunityAccessRestricted) return;
|
|
211
224
|
setTab(v);
|
|
212
225
|
setSearch("");
|
|
213
226
|
setFilters({
|
|
@@ -235,9 +248,9 @@ export default function FindTopicsDialog({
|
|
|
235
248
|
}
|
|
236
249
|
return result;
|
|
237
250
|
};
|
|
238
|
-
const currentTopics =
|
|
239
|
-
const isLoading =
|
|
240
|
-
const sectionLabel =
|
|
251
|
+
const currentTopics = effectiveTab === 0 ? filterTopics(libraryTopics) : applyCommunityFilters(communityTopics);
|
|
252
|
+
const isLoading = effectiveTab === 0 ? isLoadingLibrary : isLoadingCommunity;
|
|
253
|
+
const sectionLabel = effectiveTab === 0 ? t("add_own_exercise_message", {
|
|
241
254
|
exerciseName: t("topic").toLowerCase()
|
|
242
255
|
}) : t("or_select_a_community_template_desc") || "Community Topics";
|
|
243
256
|
const addButtonLabel = selectedTopicIds.length > 0 ? `${t("add_topics") || "Add Topics"} (${selectedTopicIds.length})` : t("add_topics") || "Add Topics";
|
|
@@ -281,7 +294,7 @@ export default function FindTopicsDialog({
|
|
|
281
294
|
borderColor: "divider"
|
|
282
295
|
},
|
|
283
296
|
children: /*#__PURE__*/_jsxs(Tabs, {
|
|
284
|
-
value:
|
|
297
|
+
value: effectiveTab,
|
|
285
298
|
onChange: handleTabChange,
|
|
286
299
|
indicatorColor: "primary",
|
|
287
300
|
textColor: "primary",
|
|
@@ -291,10 +304,11 @@ export default function FindTopicsDialog({
|
|
|
291
304
|
"data-cy": "find-topics-tab-library"
|
|
292
305
|
}), /*#__PURE__*/_jsx(Tab, {
|
|
293
306
|
label: t("community") || "Community",
|
|
307
|
+
disabled: isCommunityAccessRestricted,
|
|
294
308
|
"data-cy": "find-topics-tab-community"
|
|
295
309
|
})]
|
|
296
310
|
})
|
|
297
|
-
}),
|
|
311
|
+
}), effectiveTab === 1 && /*#__PURE__*/_jsx(Box, {
|
|
298
312
|
sx: {
|
|
299
313
|
px: 3,
|
|
300
314
|
py: {
|
|
@@ -350,7 +364,7 @@ export default function FindTopicsDialog({
|
|
|
350
364
|
selectionEnabled: true,
|
|
351
365
|
isSelected: topic => selectedTopicIds.includes(topic.topicId),
|
|
352
366
|
compact: true
|
|
353
|
-
}),
|
|
367
|
+
}), effectiveTab === 1 && /*#__PURE__*/_jsx(Box, {
|
|
354
368
|
ref: sentinelRef,
|
|
355
369
|
sx: {
|
|
356
370
|
height: 40,
|