@nualang/nualang-ui-components 0.1.1319 → 0.1.1320

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (25) hide show
  1. package/dist/Cards/FeedbackCard/FeedbackCard.js +61 -14
  2. package/dist/Cards/ScheduleCard/ScheduleCard.js +0 -1
  3. package/dist/Dialogs/CreatePhrase/CreatePhrase.js +1 -1
  4. package/dist/Dialogs/FeedbackDialog/FeedbackDialog.js +3 -3
  5. package/dist/Dialogs/GenerateBot/GenerateBot.js +3 -0
  6. package/dist/Dialogs/GenerateDiscussion/GenerateDiscussion.js +103 -7
  7. package/dist/Dialogs/GenerateRoleplay/GenerateRoleplay.js +2 -0
  8. package/dist/Dialogs/GroupedFeedbackDialog/GroupedFeedbackDialog.js +189 -12
  9. package/dist/Dialogs/RecordingDialog/RecordingDialog.js +1 -1
  10. package/dist/Dialogs/ResponsiveDialog/ResponsiveDialog.js +3 -2
  11. package/dist/Forms/CreateMeetingMultiStepForm/CreateMeetingMultiStepForm.js +20 -4
  12. package/dist/Forms/CreateMeetingMultiStepForm/GroupAssignment.js +684 -207
  13. package/dist/Forms/CreateMeetingMultiStepForm/MeetingForm.js +62 -21
  14. package/dist/Forms/DiscussImageSelector/ImageSelector.js +0 -1
  15. package/dist/Forms/DiscussMultiStepFormDialog/MultiStepFormDialog.js +1 -1
  16. package/dist/Forms/FeedbackForm/FeedbackForm.js +5 -5
  17. package/dist/Misc/DiscussionTopicSelector/TopicSelector.js +187 -0
  18. package/dist/Misc/DiscussionTopicSelector/package.json +7 -0
  19. package/dist/Navigation/Breadcrumbs/Breadcrumbs.js +1 -1
  20. package/dist/Screens/Classrooms/ViewClassroom/ViewClassroom.js +15 -4
  21. package/dist/Screens/Courses/ViewCourse/ViewTopic/ViewTopic.js +22 -1
  22. package/dist/Tables/MeetingPrompstList/MeetingPromptsList.js +3 -3
  23. package/dist/Tables/RecordingListCards/RecordingListCards.js +3 -3
  24. package/dist/Tables/SubmissionsTableCards/SubmissionsTableCards.js +2 -2
  25. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  import { useState, useEffect, useRef } from "react";
2
- import { Card, CardContent, CardActions, Typography, Avatar, Box, Button, TextField, Chip, Grid, IconButton, Tooltip } from "@mui/material";
2
+ import { Card, CardContent, CardActions, Typography, Avatar, Box, Button, TextField, Chip, Grid, IconButton, Tooltip, Checkbox } from "@mui/material";
3
3
  import { grey, green } from "@mui/material/colors";
4
4
  import AutoFixHighIcon from "@mui/icons-material/AutoFixHigh";
5
5
  import ErrorOutlineIcon from "@mui/icons-material/ErrorOutline";
@@ -8,10 +8,10 @@ import { Formik } from "formik";
8
8
  import * as Yup from "yup";
9
9
  import RecordingDialog from "../../Dialogs/RecordingDialog/RecordingDialog";
10
10
  import PDFViewer from "../../Dialogs/PDFViewer/PDFViewer";
11
- import level1Rubric from '../../Dialogs/PDFViewer/rubrics/level1Rubric.pdf';
12
- import level2Rubric from '../../Dialogs/PDFViewer/rubrics/level2Rubric.pdf';
13
- import level3Rubric from '../../Dialogs/PDFViewer/rubrics/level3Rubric.pdf';
14
- import level4Rubric from '../../Dialogs/PDFViewer/rubrics/level4Rubric.pdf';
11
+ import level1Rubric from "../../Dialogs/PDFViewer/rubrics/level1Rubric.pdf";
12
+ import level2Rubric from "../../Dialogs/PDFViewer/rubrics/level2Rubric.pdf";
13
+ import level3Rubric from "../../Dialogs/PDFViewer/rubrics/level3Rubric.pdf";
14
+ import level4Rubric from "../../Dialogs/PDFViewer/rubrics/level4Rubric.pdf";
15
15
  import HelpOutlineIcon from "@mui/icons-material/HelpOutline";
16
16
  import NualaCreating from "../../Misc/NualaCreating/NualaCreating";
17
17
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
@@ -27,11 +27,19 @@ export default function FeedbackCard({
27
27
  s3Url = "",
28
28
  conversation = {},
29
29
  existingFeedback = {},
30
+ generateAllUsed = false,
31
+ setGenerateAllUsed = () => {},
32
+ checkedSubmissions = {},
33
+ setCheckedSubmissions = () => {},
34
+ submitTrigger = null,
35
+ onSubmissionComplete = () => {},
30
36
  playerRef = {
31
37
  current: null
32
38
  },
33
39
  goToTimestamp = () => {},
34
- startTimes = {}
40
+ startTimes = {},
41
+ canSubmitAll = {},
42
+ setCanSubmit = () => {}
35
43
  }) {
36
44
  const [isRecordingDialogOpen, setIsRecordingDialogOpen] = useState(false);
37
45
  const [hasBeenEdited, setHasBeenEdited] = useState(false);
@@ -42,6 +50,12 @@ export default function FeedbackCard({
42
50
  const [openPDF, setOpenPDF] = useState(false);
43
51
  const [pageNumber, setPageNumber] = useState(1);
44
52
  const [scale, setScale] = useState(1);
53
+ const handleChangeCheckbox = event => {
54
+ setCheckedSubmissions({
55
+ ...checkedSubmissions,
56
+ [discussionData.meetingId || discussionData.meetingID || discussionData.extMeetingId]: event.target.checked
57
+ });
58
+ };
45
59
  const rubricMap = {
46
60
  1: level1Rubric,
47
61
  2: level2Rubric,
@@ -106,12 +120,12 @@ export default function FeedbackCard({
106
120
  initialValuesRef.current = initialVals;
107
121
  setIsSubmitted(false);
108
122
  }
109
- }, [existingFeedback, hasSubmission, selectedMembers, attendedMembers, notAttendedMembers, t]);
123
+ }, [existingFeedback, hasSubmission, selectedMembers, t]);
110
124
  useEffect(() => {
111
125
  const formik = formikRef.current;
112
126
  if (!formik) return;
113
127
  if (!hasSubmission || !aiGrade || Object.keys(aiGrade).length === 0) return;
114
- if (!aiJustGenerated) return;
128
+ if (!aiJustGenerated && !generateAllUsed) return;
115
129
  userData.forEach(student => {
116
130
  const keyVariants = [student.username, `${student.username}Student`, student.username?.replace(/\s+/g, "")];
117
131
  const aiData = keyVariants.map(k => aiGrade?.[k]).find(v => v && typeof v === "object") || null;
@@ -122,7 +136,27 @@ export default function FeedbackCard({
122
136
  setHasBeenEdited(true);
123
137
  setIsSubmitted(false);
124
138
  setAiJustGenerated(false);
125
- }, [aiGrade, hasSubmission, userData, aiJustGenerated]);
139
+ setGenerateAllUsed(false);
140
+ }, [aiGrade, hasSubmission, userData, aiJustGenerated, generateAllUsed]);
141
+ useEffect(() => {
142
+ if (submitTrigger && checkedSubmissions[discussionData.meetingId]) {
143
+ const formik = formikRef.current;
144
+ if (formik) {
145
+ const submitAndNotify = async () => {
146
+ try {
147
+ await formik.submitForm();
148
+ } catch (error) {
149
+ console.error("Error during form submission:", error);
150
+ } finally {
151
+ onSubmissionComplete();
152
+ }
153
+ };
154
+ submitAndNotify();
155
+ } else {
156
+ onSubmissionComplete();
157
+ }
158
+ }
159
+ }, [submitTrigger]);
126
160
  const handleGenerateAI = async () => {
127
161
  setAiJustGenerated(true);
128
162
  await onGenerateFeedback(discussionData);
@@ -133,10 +167,11 @@ export default function FeedbackCard({
133
167
  const handleClosePDF = () => {
134
168
  setOpenPDF(false);
135
169
  };
170
+ const initialValues = getInitialValues();
136
171
  return /*#__PURE__*/_jsxs(_Fragment, {
137
172
  children: [/*#__PURE__*/_jsx(Formik, {
138
173
  innerRef: formikRef,
139
- initialValues: getInitialValues(),
174
+ initialValues: initialValues,
140
175
  validationSchema: getFeedbackValidationSchema(attendedMembers.map(m => m.memberId)),
141
176
  onSubmit: async (values, {
142
177
  setSubmitting
@@ -189,6 +224,13 @@ export default function FeedbackCard({
189
224
  });
190
225
  };
191
226
  const canSubmit = hasSubmission && !isSubmitting && isFormValid() && (hasChanges() || hasBeenEdited) && !isSubmitted;
227
+ // eslint-disable-next-line react-hooks/rules-of-hooks
228
+ useEffect(() => {
229
+ setCanSubmit({
230
+ ...canSubmitAll,
231
+ [discussionData.meetingId || discussionData.meetingID]: canSubmit
232
+ });
233
+ }, [canSubmit]);
192
234
  return /*#__PURE__*/_jsx("form", {
193
235
  onSubmit: handleSubmit,
194
236
  children: /*#__PURE__*/_jsxs(Card, {
@@ -196,7 +238,8 @@ export default function FeedbackCard({
196
238
  mb: 3,
197
239
  borderRadius: 3,
198
240
  boxShadow: 3,
199
- overflow: "hidden"
241
+ overflow: "hidden",
242
+ backgroundColor: "background.paper"
200
243
  },
201
244
  children: [aiJustGenerated ? /*#__PURE__*/_jsxs(Grid, {
202
245
  sx: {
@@ -215,7 +258,7 @@ export default function FeedbackCard({
215
258
  children: t("generating_feedback")
216
259
  }), /*#__PURE__*/_jsx(NualaCreating, {})]
217
260
  }) : /*#__PURE__*/_jsxs(_Fragment, {
218
- children: [/*#__PURE__*/_jsx(Box, {
261
+ children: [/*#__PURE__*/_jsxs(Box, {
219
262
  sx: {
220
263
  px: 3,
221
264
  pt: 2,
@@ -223,7 +266,11 @@ export default function FeedbackCard({
223
266
  fontWeight: 600,
224
267
  color: "text.secondary"
225
268
  },
226
- children: /*#__PURE__*/_jsxs(Grid, {
269
+ children: [/*#__PURE__*/_jsx(Checkbox, {
270
+ checked: checkedSubmissions[discussionData.meetingId || discussionData.meetingID || discussionData.extMeetingId] || false,
271
+ disabled: !hasSubmission,
272
+ onChange: handleChangeCheckbox
273
+ }), /*#__PURE__*/_jsxs(Grid, {
227
274
  container: true,
228
275
  alignItems: "center",
229
276
  spacing: 3,
@@ -261,7 +308,7 @@ export default function FeedbackCard({
261
308
  })]
262
309
  })
263
310
  })]
264
- })
311
+ })]
265
312
  }), /*#__PURE__*/_jsx(CardContent, {
266
313
  sx: {
267
314
  px: 3,
@@ -62,7 +62,6 @@ export default function ScheduleCard({
62
62
  children: scheduleListData?.meetingTopic
63
63
  }), /*#__PURE__*/_jsx(Typography, {
64
64
  variant: "body2",
65
- color: "text.secondary",
66
65
  sx: {
67
66
  ml: 1.5,
68
67
  fontWeight: 500
@@ -422,7 +422,7 @@ function CreatePhrase({
422
422
  disabled: !phrase || Array.isArray(translationList) && translationList.includes(autoTranslatedText),
423
423
  size: "sm",
424
424
  className: classes.alternativeVersionButton,
425
- endIcon: /*#__PURE__*/_jsx(AutoFixHighIcon, {}),
425
+ startIcon: /*#__PURE__*/_jsx(AutoFixHighIcon, {}),
426
426
  children: t("auto_generate_phrase_translation")
427
427
  }), learnLang !== forLang && (import.meta.env.REACT_APP_STAGE === "dev" || window.location.host === "localhost:9009") && /*#__PURE__*/_jsx(Box, {
428
428
  sx: {
@@ -18,7 +18,7 @@ export default function CreateFeedbackDialog({
18
18
  handleSubmit = () => {},
19
19
  attendeesData = [],
20
20
  data = {},
21
- handleGradeConversation = () => {},
21
+ handleBatchGradeConversations = () => {},
22
22
  aiGrade = "",
23
23
  conversation = {},
24
24
  t = text => text,
@@ -87,7 +87,7 @@ export default function CreateFeedbackDialog({
87
87
  loading: loading,
88
88
  saveTranscript: saveTranscript,
89
89
  conversation: conversation,
90
- handleGradeConversation: handleGradeConversation,
90
+ handleBatchGradeConversations: handleBatchGradeConversations,
91
91
  aiGrade: aiGrade,
92
92
  attendeesData: attendeesData,
93
93
  handleClosefeedbackForm: handleClosefeedbackForm,
@@ -116,7 +116,7 @@ CreateFeedbackDialog.propTypes = {
116
116
  attendeesData: PropTypes.array,
117
117
  t: PropTypes.func,
118
118
  data: PropTypes.object,
119
- handleGradeConversation: PropTypes.func,
119
+ handleBatchGradeConversations: PropTypes.func,
120
120
  aiGrade: PropTypes.string,
121
121
  conversation: PropTypes.object,
122
122
  loading: PropTypes.bool
@@ -8,6 +8,7 @@ import { Link } from "react-router-dom";
8
8
  import NualaCreating from "../../Misc/NualaCreating/NualaCreating";
9
9
  import ResponsiveDialog from "../ResponsiveDialog/ResponsiveDialog";
10
10
  import InputHelper from "../../Forms/InputHelper/InputHelper";
11
+ import AutoFixHighIcon from "@mui/icons-material/AutoFixHigh";
11
12
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
12
13
  function validateResponse(response, template) {
13
14
  if (!Array.isArray(response)) {
@@ -199,6 +200,8 @@ function GenerateBotDialog({
199
200
  handleClose: handleCloseDialog,
200
201
  handleSubmit: handleSubmit,
201
202
  submitText: t("generate_bot"),
203
+ startIcon: /*#__PURE__*/_jsx(AutoFixHighIcon, {}),
204
+ isGenerating: true,
202
205
  dialogTitle: isBotGenerating ? t("generating_bot") : t("generate_bot"),
203
206
  isSubmitDisabled: !values.botTopic && !usePhrasesContext || isBotGenerating,
204
207
  children: /*#__PURE__*/_jsxs(Grid, {
@@ -6,6 +6,9 @@ import { Link } from "react-router-dom";
6
6
  import NualaCreating from "../../Misc/NualaCreating/NualaCreating";
7
7
  import ResponsiveDialog from "../ResponsiveDialog/ResponsiveDialog";
8
8
  import InputHelper from "../../Forms/InputHelper/InputHelper";
9
+ import TopicSelector from "../../Misc/DiscussionTopicSelector/TopicSelector";
10
+ import { rivescriptToJson } from "../../utils/index";
11
+ import AutoFixHighIcon from "@mui/icons-material/AutoFixHigh";
9
12
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
10
13
  function GenerateDiscussionDialog({
11
14
  t = text => text,
@@ -17,20 +20,67 @@ function GenerateDiscussionDialog({
17
20
  learnLang,
18
21
  level,
19
22
  siteLanguage,
20
- meetingTopic
23
+ meetingTopic,
24
+ courses,
25
+ getCourseSections,
26
+ courseSectionTopicId,
27
+ createDiscussion,
28
+ handleChange,
29
+ setMeetingTopic
21
30
  }) {
22
31
  const [isGenerating, setIsGenerating] = useState(false);
23
32
  const [errorMessage, setErrorMessage] = useState(null);
24
33
  const [discussionTopic, setDiscussionTopic] = useState(meetingTopic);
25
34
  const [topicGoal, setTopicGoal] = useState("");
26
35
  const [promptLanguage, setPromptLanguage] = useState(forLang);
36
+ const [selectedTopic, setSelectedTopic] = useState(null);
37
+ const [selectedCourseSectionTopicId, setSelectedCourseSectionTopicId] = useState(null);
38
+ const [isAutoSelected, setIsAutoSelected] = useState(false);
39
+ const [hasAutoUpdated, setHasAutoUpdated] = useState(false);
40
+ const [localTopicName, setLocalTopicName] = useState("");
27
41
  useEffect(() => {
28
- setDiscussionTopic(meetingTopic || "");
29
- }, [meetingTopic]);
42
+ if (isGenerateDiscussionDialogOpen) {
43
+ setIsAutoSelected(false);
44
+ setHasAutoUpdated(false);
45
+ }
46
+ }, [isGenerateDiscussionDialogOpen]);
47
+ useEffect(() => {
48
+ if (!hasAutoUpdated) {
49
+ setDiscussionTopic(meetingTopic || "");
50
+ }
51
+ }, [meetingTopic, hasAutoUpdated]);
52
+ const extractQuizQuestions = parsedBotJson => {
53
+ const variables = parsedBotJson?.begin?.var;
54
+ if (!variables) {
55
+ return [];
56
+ }
57
+ const questionsArray = Object.entries(variables).filter(([key, value]) => (key.startsWith("question") || key.startsWith("text")) && !Number.isNaN(parseInt(key.substring(key.startsWith("question") ? 8 : 4))) && typeof value === "string" && value.trim() !== "").sort(([keyA], [keyB]) => {
58
+ const prefixA = keyA.startsWith("question") ? "question" : "text";
59
+ const prefixB = keyB.startsWith("question") ? "question" : "text";
60
+ const numA = parseInt(keyA.replace(prefixA, "")) || 0;
61
+ const numB = parseInt(keyB.replace(prefixB, "")) || 0;
62
+ return numA - numB;
63
+ }).map(([, value]) => value);
64
+ return questionsArray;
65
+ };
30
66
  const handleGenerateDiscussion = async () => {
31
67
  try {
32
68
  setIsGenerating(true);
33
69
  setErrorMessage("");
70
+ const phrasesText = selectedTopic?.phrases?.map(p => p.phrase) || [];
71
+ const roleplayScripts = selectedTopic?.roleplays?.map(r => r.script) || [];
72
+ const botRiveScripts = selectedTopic?.bots?.map(b => b.riveFile) || [];
73
+ const questionsArrays = botRiveScripts.map(riveFile => {
74
+ try {
75
+ const jsonBot = rivescriptToJson(riveFile);
76
+ return extractQuizQuestions(jsonBot);
77
+ } catch (e) {
78
+ console.error("Error parsing RiveScript file:", e);
79
+ return [];
80
+ }
81
+ });
82
+ const allDiscussionQuestions = questionsArrays.flat();
83
+ const uniqueQuestions = [...new Set(allDiscussionQuestions)];
34
84
  const response = await makeChatGptApiRequest({
35
85
  model: "gpt-4o",
36
86
  promptKey: "generateDiscussion",
@@ -40,11 +90,19 @@ function GenerateDiscussionDialog({
40
90
  promptLanguage,
41
91
  siteLanguage,
42
92
  level: level || 1,
43
- learnLang
93
+ learnLang,
94
+ phrases: phrasesText,
95
+ scenarios: roleplayScripts,
96
+ bots: uniqueQuestions
97
+ }
98
+ });
99
+ handleChange({
100
+ target: {
101
+ name: "courseSectionTopicId",
102
+ value: selectedCourseSectionTopicId
44
103
  }
45
104
  });
46
- const generatedData = response?.result || "";
47
- setDiscussionPrompt(generatedData);
105
+ setDiscussionPrompt(response?.result || "");
48
106
  setIsGenerating(false);
49
107
  handleClose();
50
108
  } catch (error) {
@@ -53,11 +111,37 @@ function GenerateDiscussionDialog({
53
111
  setErrorMessage(t("error_generating_discussion"));
54
112
  }
55
113
  };
114
+ const handleTopicSelect = (topic, {
115
+ cstId
116
+ }) => {
117
+ setSelectedCourseSectionTopicId(cstId);
118
+ setTopicGoal(topic.topicGoal || "");
119
+ setSelectedTopic(topic);
120
+ if (cstId === courseSectionTopicId && !isAutoSelected) {
121
+ const topicName = topic?.topicName || "";
122
+ setLocalTopicName(topicName);
123
+ setIsAutoSelected(true);
124
+ setHasAutoUpdated(true);
125
+ setDiscussionTopic(topicName);
126
+ setMeetingTopic(topicName);
127
+ }
128
+ };
129
+ const handleDiscussionTopicChange = e => {
130
+ const newValue = e.target.value;
131
+ setDiscussionTopic(newValue);
132
+ };
133
+ useEffect(() => {
134
+ if (localTopicName && !meetingTopic && hasAutoUpdated) {
135
+ setMeetingTopic(localTopicName);
136
+ }
137
+ }, [meetingTopic, localTopicName, hasAutoUpdated]);
56
138
  return /*#__PURE__*/_jsx(ResponsiveDialog, {
57
139
  open: isGenerateDiscussionDialogOpen,
58
140
  handleClose: handleClose,
59
141
  handleSubmit: handleGenerateDiscussion,
60
142
  submitText: t("generate_discussion"),
143
+ startIcon: /*#__PURE__*/_jsx(AutoFixHighIcon, {}),
144
+ isGenerating: true,
61
145
  dialogTitle: isGenerating ? t("generating_discussion") : t("generate_discussion"),
62
146
  isSubmitDisabled: !discussionTopic || isGenerating,
63
147
  children: /*#__PURE__*/_jsxs(Grid, {
@@ -106,9 +190,21 @@ function GenerateDiscussionDialog({
106
190
  variant: "outlined",
107
191
  fullWidth: true,
108
192
  required: true,
109
- onChange: e => setDiscussionTopic(e.target.value),
193
+ onChange: handleDiscussionTopicChange,
110
194
  multiline: true
111
195
  })
196
+ }), /*#__PURE__*/_jsx(Grid, {
197
+ size: 12,
198
+ marginTop: "8px",
199
+ children: /*#__PURE__*/_jsx(TopicSelector, {
200
+ courses: courses,
201
+ getCourseSections: getCourseSections,
202
+ t: t,
203
+ onTopicSelect: handleTopicSelect,
204
+ selectedTopicId: selectedCourseSectionTopicId,
205
+ createDiscussion: createDiscussion,
206
+ courseSectionTopicId: courseSectionTopicId
207
+ })
112
208
  }), /*#__PURE__*/_jsx(Grid, {
113
209
  size: 12,
114
210
  children: /*#__PURE__*/_jsx(TextField, {
@@ -330,6 +330,8 @@ function GenerateRoleplayDialog({
330
330
  submitText: autoSelect ? t("generate_roleplay") : t("select_roleplay"),
331
331
  dialogTitle: isRoleplayGenerating ? t("generating_roleplay") : t("generate_roleplay"),
332
332
  isSubmitDisabled: isSubmitDisabled,
333
+ startIcon: /*#__PURE__*/_jsx(AutoFixHighIcon, {}),
334
+ isGenerating: true,
333
335
  children: [/*#__PURE__*/_jsxs(Grid, {
334
336
  container: true,
335
337
  sx: {