@nualang/nualang-ui-components 0.1.1352 → 0.1.1354
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/CreatePhrase/CreatePhrase.js +43 -0
- package/dist/Dialogs/GenerateDefinitions/GenerateDefinitions.js +1 -0
- package/dist/Dialogs/GeneratePhrases/GeneratePhrases.js +33 -3
- package/dist/Dialogs/GroupedFeedbackDialog/GroupedFeedbackDialog.js +146 -47
- package/dist/Dialogs/GroupedFeedbackDialog/package.json +6 -0
- package/dist/Dialogs/UploadPhrases/UploadPhrases.js +75 -14
- package/dist/Dialogs/UploadPhrases/upload-phrases-csv.png +0 -0
- package/dist/Editors/PhraseList/PhraseList.js +38 -34
- package/dist/Editors/Phrases/Phrases.js +37 -13
- package/dist/Exercises/Meaning/Meaning.js +788 -0
- package/dist/Exercises/PhraseGroup/Games/MeaningGame/MeaningGame.js +86 -0
- package/dist/Exercises/PhraseGroup/Games/index.js +2 -1
- package/dist/Exercises/PhraseGroup/PhraseGroup.js +4 -3
- package/dist/Exercises/Roleplay/Roleplay.js +2 -1
- package/dist/Exercises/Roleplay/RoleplayGameOptions/RoleplayGames.js +11 -4
- package/dist/Forms/CreateRoleplay/Steps/RoleplaySettings/RoleplaySettings.js +5 -2
- package/dist/Forms/UpdatePhraseList/UpdatePhraseList.js +8 -0
- package/dist/Forms/UpdateRoleplay/UpdateRoleplay.js +2 -1
- package/dist/Lists/Phrases/Phrases.js +9 -5
- package/dist/Misc/ChatBubble/ChatBubble.js +7 -2
- package/dist/Misc/HoverText/HoverText.js +18 -7
- package/dist/Misc/RubyText/RubyText.js +27 -0
- package/dist/Screens/Classrooms/ViewClassroom/ViewClassroom.js +2 -2
- package/dist/Screens/Courses/ViewCourse/ViewTopic/ViewTopic.js +94 -193
- package/dist/Tables/Progress/Header.js +557 -0
- package/dist/Tables/Progress/Progress.js +54 -349
- package/dist/Tables/Progress/ProgressList.js +21 -43
- package/dist/Tables/Progress/ProgressList.test.js +48 -0
- package/dist/Tables/Progress/ProgressTable.js +58 -1027
- package/dist/Tables/Progress/ProgressTable.test.js +258 -0
- package/dist/Tables/Progress/ProgressTableRow.js +270 -0
- package/dist/Tables/Progress/README.md +53 -0
- package/dist/Tables/Progress/TableToolbar.js +130 -0
- package/dist/Tables/Progress/TableToolbar.test.js +69 -0
- package/dist/Tables/Progress/cells/index.js +446 -0
- package/dist/Tables/Progress/cells/index.test.js +345 -0
- package/dist/Tables/Progress/constants.js +6 -0
- package/dist/Tables/Progress/exportToExcel.js +224 -0
- package/dist/Tables/Progress/exportToExcel.test.js +135 -0
- package/dist/Tables/Progress/helpers.js +42 -0
- package/dist/Tables/Progress/helpers.test.js +23 -0
- package/dist/Tables/Progress/sorting.js +78 -0
- package/dist/Tables/Progress/sorting.test.js +69 -0
- package/dist/Tables/Progress/styledComponents.js +156 -0
- package/dist/Tables/Progress/useProgressUrlParams.js +147 -0
- package/dist/Tables/Progress/useProgressUrlParams.test.js +183 -0
- package/dist/Tables/Progress/utils.js +2 -2
- package/dist/Tables/Progress/utils.test.js +129 -0
- package/dist/Tables/Progress/viewState.js +25 -0
- package/dist/hooks/useExerciseState.js +24 -4
- package/dist/hooks/usePhonetics.js +42 -0
- package/dist/utils/japaneseTokenizer.js +55 -0
- package/package.json +4 -1
|
@@ -56,6 +56,7 @@ function CreatePhrase({
|
|
|
56
56
|
validatePhraseRecognition,
|
|
57
57
|
verificationStatus,
|
|
58
58
|
makeChatGptApiRequest,
|
|
59
|
+
difficulty,
|
|
59
60
|
...otherProps
|
|
60
61
|
}) {
|
|
61
62
|
const translationInputRef = useRef();
|
|
@@ -78,6 +79,7 @@ function CreatePhrase({
|
|
|
78
79
|
const [definition, setDefinition] = useState("");
|
|
79
80
|
const [definitionList, setDefinitionList] = useState(otherProps.definitions || []);
|
|
80
81
|
const [autoTranslatedText, setAutoTranslatedText] = useState(false);
|
|
82
|
+
const [autoGeneratedDefinitions, setAutoGeneratedDefinitions] = useState(false);
|
|
81
83
|
const [checkingRecogniton, setCheckingRecogniton] = useState(false);
|
|
82
84
|
const [recognised, setRecognised] = useState(false);
|
|
83
85
|
const [notRecognised, setNotRecognised] = useState(false);
|
|
@@ -287,6 +289,39 @@ function CreatePhrase({
|
|
|
287
289
|
setIsSubmitting(false);
|
|
288
290
|
}
|
|
289
291
|
};
|
|
292
|
+
const generateDefinitions = async () => {
|
|
293
|
+
try {
|
|
294
|
+
setIsSubmitting(true);
|
|
295
|
+
let chatGptResponse = await makeChatGptApiRequest({
|
|
296
|
+
model: "gpt-4o",
|
|
297
|
+
promptKey: "generateDefinitions",
|
|
298
|
+
promptVariables: {
|
|
299
|
+
learnLang,
|
|
300
|
+
forLang,
|
|
301
|
+
siteLanguage,
|
|
302
|
+
difficulty,
|
|
303
|
+
phrases: [{
|
|
304
|
+
phrase,
|
|
305
|
+
translations: translationList
|
|
306
|
+
}],
|
|
307
|
+
generateDefinitions: true
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
const definitions = chatGptResponse?.result?.[0]?.definitions;
|
|
311
|
+
if (!validateResponse(definitions)) {
|
|
312
|
+
throw new Error("Invalid AI response");
|
|
313
|
+
}
|
|
314
|
+
setAutoGeneratedDefinitions(definitions[0]);
|
|
315
|
+
setDefinitionList(prevDefinitionList => {
|
|
316
|
+
const newDefinitions = definitions.filter(text => !prevDefinitionList.includes(text));
|
|
317
|
+
return [...prevDefinitionList, ...newDefinitions];
|
|
318
|
+
});
|
|
319
|
+
} catch (error) {
|
|
320
|
+
console.error("Error fetching definitions from OpenAI:", error.message);
|
|
321
|
+
} finally {
|
|
322
|
+
setIsSubmitting(false);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
290
325
|
const handleEditTranslation = (event, index) => {
|
|
291
326
|
const {
|
|
292
327
|
target: {
|
|
@@ -320,6 +355,7 @@ function CreatePhrase({
|
|
|
320
355
|
setDefinitionList([]);
|
|
321
356
|
setSelectedImage(initialSelectedImagestate);
|
|
322
357
|
setAutoTranslatedText("");
|
|
358
|
+
setAutoGeneratedDefinitions("");
|
|
323
359
|
};
|
|
324
360
|
const removePhraseImage = () => {
|
|
325
361
|
setSelectedImage({
|
|
@@ -495,6 +531,13 @@ function CreatePhrase({
|
|
|
495
531
|
className: classes.alternativeVersionButton,
|
|
496
532
|
startIcon: /*#__PURE__*/_jsx(AutoFixHighIcon, {}),
|
|
497
533
|
children: t("auto_generate_phrase_translation")
|
|
534
|
+
}), learnLang !== forLang && /*#__PURE__*/_jsx(DefaultButton, {
|
|
535
|
+
onClick: generateDefinitions,
|
|
536
|
+
disabled: !phrase || Array.isArray(definitionList) && definitionList.includes(autoGeneratedDefinitions),
|
|
537
|
+
size: "sm",
|
|
538
|
+
className: classes.alternativeVersionButton,
|
|
539
|
+
startIcon: /*#__PURE__*/_jsx(AutoFixHighIcon, {}),
|
|
540
|
+
children: t("auto_generate_definitions")
|
|
498
541
|
}), learnLang !== forLang && (import.meta.env.REACT_APP_STAGE === "dev" || window.location.host === "localhost:9009") && /*#__PURE__*/_jsx(Box, {
|
|
499
542
|
sx: {
|
|
500
543
|
display: "inline-flex",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
2
|
import ResponsiveDialog from "../ResponsiveDialog/ResponsiveDialog";
|
|
3
3
|
import { AutoFixHigh } from "@mui/icons-material";
|
|
4
|
-
import { Alert, Grid, InputAdornment, LinearProgress, MenuItem, Slider, TextField, Tooltip, Typography, ListItem, ListItemText, useMediaQuery } from "@mui/material";
|
|
4
|
+
import { Alert, Grid, InputAdornment, LinearProgress, MenuItem, Slider, TextField, Tooltip, Typography, ListItem, ListItemText, useMediaQuery, FormControlLabel, Switch, Box } from "@mui/material";
|
|
5
5
|
import { useTheme } from "@mui/material/styles";
|
|
6
6
|
import { Link } from "react-router";
|
|
7
7
|
import InfoIcon from "@mui/icons-material/Info";
|
|
@@ -115,6 +115,7 @@ function GeneratePhrases({
|
|
|
115
115
|
const [errorMessage, setErrorMessage] = useState(null);
|
|
116
116
|
const [phraseType, setPhraseType] = useState("words");
|
|
117
117
|
const [generatedPhrases, setGeneratedPhrases] = useState([]);
|
|
118
|
+
const [generateDefinitions, setGenerateDefinitions] = useState(true);
|
|
118
119
|
const isPhrases = currentPhrases && currentPhrases.length > 0;
|
|
119
120
|
const phrases = isPhrases ? currentPhrases.map(({
|
|
120
121
|
phrase,
|
|
@@ -188,7 +189,8 @@ function GeneratePhrases({
|
|
|
188
189
|
difficulty,
|
|
189
190
|
phraseType,
|
|
190
191
|
isPhrases,
|
|
191
|
-
phrases
|
|
192
|
+
phrases,
|
|
193
|
+
generateDefinitions
|
|
192
194
|
}
|
|
193
195
|
});
|
|
194
196
|
const phrasesArray = chatGptResponse.result;
|
|
@@ -217,6 +219,9 @@ function GeneratePhrases({
|
|
|
217
219
|
const handleCancel = () => {
|
|
218
220
|
setGeneratedPhrases([]);
|
|
219
221
|
};
|
|
222
|
+
useEffect(() => {
|
|
223
|
+
setGenerateDefinitions(phraseType === 'words');
|
|
224
|
+
}, [phraseType]);
|
|
220
225
|
return /*#__PURE__*/_jsxs(ResponsiveDialog, {
|
|
221
226
|
open: open,
|
|
222
227
|
closeText: generatedPhrases.length > 0 ? t("cancel") : t("close"),
|
|
@@ -353,6 +358,31 @@ function GeneratePhrases({
|
|
|
353
358
|
min: 4,
|
|
354
359
|
max: 16
|
|
355
360
|
})]
|
|
361
|
+
}), /*#__PURE__*/_jsxs(Grid, {
|
|
362
|
+
size: 12,
|
|
363
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
364
|
+
children: /*#__PURE__*/_jsx(Box, {
|
|
365
|
+
children: t("generate_definitions")
|
|
366
|
+
})
|
|
367
|
+
}), /*#__PURE__*/_jsx(FormControlLabel, {
|
|
368
|
+
label: "",
|
|
369
|
+
control: /*#__PURE__*/_jsx(_Fragment, {
|
|
370
|
+
children: /*#__PURE__*/_jsx("span", {
|
|
371
|
+
children: /*#__PURE__*/_jsx(Switch, {
|
|
372
|
+
checked: generateDefinitions,
|
|
373
|
+
onChange: e => {
|
|
374
|
+
setGenerateDefinitions(e.target.checked);
|
|
375
|
+
},
|
|
376
|
+
inputProps: {
|
|
377
|
+
"aria-label": "controlled"
|
|
378
|
+
},
|
|
379
|
+
sx: {
|
|
380
|
+
marginLeft: 0.5
|
|
381
|
+
}
|
|
382
|
+
})
|
|
383
|
+
})
|
|
384
|
+
})
|
|
385
|
+
})]
|
|
356
386
|
})]
|
|
357
387
|
}), generatedPhrases.length > 0 && /*#__PURE__*/_jsxs(_Fragment, {
|
|
358
388
|
children: [/*#__PURE__*/_jsx(Typography, {
|
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
import { useMemo, useEffect, useState, useRef, useCallback } from "react";
|
|
2
|
-
import { Dialog, DialogContent, IconButton, Box, Typography, AppBar, Toolbar, CircularProgress, Button, Checkbox, Paper, Divider, Tooltip, Skeleton, Grid } from "@mui/material";
|
|
2
|
+
import { Dialog, DialogContent, IconButton, Box, Typography, AppBar, Toolbar, CircularProgress, Button, Checkbox, Paper, Divider, Tooltip, Skeleton, Grid, TextField, LinearProgress, Alert } from "@mui/material";
|
|
3
|
+
import { Link } from "react-router";
|
|
4
|
+
import MuiLink from "@mui/material/Link";
|
|
3
5
|
import CloseIcon from "@mui/icons-material/Close";
|
|
4
6
|
import { red, orange, green, grey } from "@mui/material/colors";
|
|
5
7
|
import FeedbackCard from "../../Cards/FeedbackCard/FeedbackCard";
|
|
6
8
|
import AutoFixHighIcon from "@mui/icons-material/AutoFixHigh";
|
|
7
9
|
import NualaHeadphonesBackground from "../../img/NualaHeadphonesBackground.svg";
|
|
8
10
|
import CheckCircleIcon from "@mui/icons-material/CheckCircle";
|
|
11
|
+
import { AutoFixHigh } from "@mui/icons-material";
|
|
9
12
|
import useConfirm from "../../hooks/useConfirm";
|
|
10
13
|
import NualaCreating from "../../Misc/NualaCreating/NualaCreating";
|
|
14
|
+
import ResponsiveDialog from "../ResponsiveDialog/ResponsiveDialog";
|
|
15
|
+
import PDFViewer from "../PDFViewer/PDFViewer";
|
|
16
|
+
import level1Rubric from "../PDFViewer/rubrics/level1Rubric.pdf";
|
|
17
|
+
import level2Rubric from "../PDFViewer/rubrics/level2Rubric.pdf";
|
|
18
|
+
import level3Rubric from "../PDFViewer/rubrics/level3Rubric.pdf";
|
|
19
|
+
import level4Rubric from "../PDFViewer/rubrics/level4Rubric.pdf";
|
|
11
20
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
12
21
|
export default function GroupFeedbackDialog({
|
|
13
22
|
open = false,
|
|
@@ -44,7 +53,20 @@ export default function GroupFeedbackDialog({
|
|
|
44
53
|
const [hasReachedBottom, setHasReachedBottom] = useState(false);
|
|
45
54
|
const [isGeneratingAll, setIsGeneratingAll] = useState(false);
|
|
46
55
|
const [isGeneratingIndividual, setIsGeneratingIndividual] = useState(false);
|
|
56
|
+
const [customFeedbackOpen, setCustomFeedbackOpen] = useState(false);
|
|
57
|
+
const [customFeedbackText, setCustomFeedbackText] = useState("");
|
|
58
|
+
const [openPDF, setOpenPDF] = useState(false);
|
|
59
|
+
const [pageNumber, setPageNumber] = useState(1);
|
|
60
|
+
const [scale, setScale] = useState(1);
|
|
47
61
|
const isAnyGenerating = isGeneratingAll || isGeneratingIndividual;
|
|
62
|
+
const pendingGenerateRef = useRef(null);
|
|
63
|
+
const rubricMap = {
|
|
64
|
+
1: level1Rubric,
|
|
65
|
+
2: level2Rubric,
|
|
66
|
+
3: level3Rubric,
|
|
67
|
+
4: level4Rubric
|
|
68
|
+
};
|
|
69
|
+
const pdfUrl = rubricMap[selectedRecording?.level] || level1Rubric;
|
|
48
70
|
const [confirm] = useConfirm(t);
|
|
49
71
|
const bottomRefCallback = useCallback(node => {
|
|
50
72
|
if (node) {
|
|
@@ -59,6 +81,12 @@ export default function GroupFeedbackDialog({
|
|
|
59
81
|
node._observer = observer;
|
|
60
82
|
}
|
|
61
83
|
}, []);
|
|
84
|
+
const handleOpenPDF = () => {
|
|
85
|
+
setOpenPDF(true);
|
|
86
|
+
};
|
|
87
|
+
const handleClosePDF = () => {
|
|
88
|
+
setOpenPDF(false);
|
|
89
|
+
};
|
|
62
90
|
useEffect(() => {
|
|
63
91
|
// dialog is shared between discussions, so reset when teacher selects another discussion
|
|
64
92
|
setGenerateAllUsed(false);
|
|
@@ -69,7 +97,42 @@ export default function GroupFeedbackDialog({
|
|
|
69
97
|
setCanSubmit({});
|
|
70
98
|
setHasReachedBottom(false);
|
|
71
99
|
setIsGeneratingAll(false);
|
|
100
|
+
pendingGenerateRef.current = null;
|
|
72
101
|
}, [groupedId]);
|
|
102
|
+
const openCustomPromptForAll = () => {
|
|
103
|
+
pendingGenerateRef.current = {
|
|
104
|
+
type: "all"
|
|
105
|
+
};
|
|
106
|
+
setCustomFeedbackText("");
|
|
107
|
+
setCustomFeedbackOpen(true);
|
|
108
|
+
};
|
|
109
|
+
const openCustomPromptForSingle = discussionData => {
|
|
110
|
+
pendingGenerateRef.current = {
|
|
111
|
+
type: "single",
|
|
112
|
+
payload: discussionData
|
|
113
|
+
};
|
|
114
|
+
setCustomFeedbackText("");
|
|
115
|
+
setCustomFeedbackOpen(true);
|
|
116
|
+
};
|
|
117
|
+
const handleCustomPromptCancel = () => {
|
|
118
|
+
setCustomFeedbackOpen(false);
|
|
119
|
+
pendingGenerateRef.current = null;
|
|
120
|
+
setCustomFeedbackText("");
|
|
121
|
+
};
|
|
122
|
+
const handleCustomPromptContinue = async () => {
|
|
123
|
+
try {
|
|
124
|
+
const pending = pendingGenerateRef.current;
|
|
125
|
+
pendingGenerateRef.current = null;
|
|
126
|
+
if (!pending) return;
|
|
127
|
+
if (pending.type === "all") {
|
|
128
|
+
await handleGenerateFeedbackForAll();
|
|
129
|
+
} else if (pending.type === "single") {
|
|
130
|
+
await handleGenerateFeedback(pending.payload);
|
|
131
|
+
}
|
|
132
|
+
} finally {
|
|
133
|
+
setCustomFeedbackOpen(false);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
73
136
|
useEffect(() => {
|
|
74
137
|
if (pendingSubmissions === 0 && submitTrigger !== null) {
|
|
75
138
|
setCheckedSubmissions({});
|
|
@@ -137,7 +200,9 @@ export default function GroupFeedbackDialog({
|
|
|
137
200
|
}).filter(member => member.name !== "Unknown");
|
|
138
201
|
};
|
|
139
202
|
const handleGenerateFeedback = async discussionData => {
|
|
140
|
-
if (handleBatchGradeConversations)
|
|
203
|
+
if (!handleBatchGradeConversations) return;
|
|
204
|
+
setIsGeneratingIndividual(true);
|
|
205
|
+
try {
|
|
141
206
|
const joinedMembersWithNames = getJoinedMembersNames(discussionData.joinedMembers || [], userData);
|
|
142
207
|
await handleBatchGradeConversations([{
|
|
143
208
|
meetingId: discussionData.meetingId || discussionData.meetingID,
|
|
@@ -148,8 +213,11 @@ export default function GroupFeedbackDialog({
|
|
|
148
213
|
meetingPrompt: discussionData.meetingPrompt,
|
|
149
214
|
learnLang: learnLang,
|
|
150
215
|
joinedMembers: discussionData.joinedMembers || [],
|
|
151
|
-
joinedMembersNames: joinedMembersWithNames
|
|
216
|
+
joinedMembersNames: joinedMembersWithNames,
|
|
217
|
+
customFeedback: customFeedbackText
|
|
152
218
|
}]);
|
|
219
|
+
} finally {
|
|
220
|
+
setIsGeneratingIndividual(false);
|
|
153
221
|
}
|
|
154
222
|
};
|
|
155
223
|
const handleGenerateFeedbackForAll = async () => {
|
|
@@ -171,7 +239,8 @@ export default function GroupFeedbackDialog({
|
|
|
171
239
|
meetingPrompt: discussion.meetingPrompt || submissionForThisDiscussion.meetingPrompt,
|
|
172
240
|
learnLang: learnLang,
|
|
173
241
|
joinedMembers: submissionForThisDiscussion.joinedMembers || [],
|
|
174
|
-
joinedMembersNames: joinedMembersWithNames
|
|
242
|
+
joinedMembersNames: joinedMembersWithNames,
|
|
243
|
+
customFeedback: customFeedbackText
|
|
175
244
|
};
|
|
176
245
|
});
|
|
177
246
|
if (conversationsToGrade.length > 0 && handleBatchGradeConversations) {
|
|
@@ -478,7 +547,7 @@ export default function GroupFeedbackDialog({
|
|
|
478
547
|
color: "secondary",
|
|
479
548
|
endIcon: /*#__PURE__*/_jsx(AutoFixHighIcon, {}),
|
|
480
549
|
disabled: selectedCount === 0 || isGeneratingAll,
|
|
481
|
-
onClick:
|
|
550
|
+
onClick: openCustomPromptForAll,
|
|
482
551
|
sx: {
|
|
483
552
|
fontWeight: 600
|
|
484
553
|
},
|
|
@@ -528,7 +597,7 @@ export default function GroupFeedbackDialog({
|
|
|
528
597
|
hasSubmission: hasSubmission,
|
|
529
598
|
aiGrade: aiGrade[discussionData.meetingId || discussionData.meetingID] || {},
|
|
530
599
|
existingFeedback: feedbackByMeetingId[feedbackMeetingId] || {},
|
|
531
|
-
onGenerateFeedback: () =>
|
|
600
|
+
onGenerateFeedback: () => openCustomPromptForSingle(discussionData),
|
|
532
601
|
onSubmitFeedback: handleSubmitFeedback,
|
|
533
602
|
setSelectedRecording: setSelectedRecording,
|
|
534
603
|
selectedRecording: selectedRecording,
|
|
@@ -556,53 +625,83 @@ export default function GroupFeedbackDialog({
|
|
|
556
625
|
})]
|
|
557
626
|
})
|
|
558
627
|
})]
|
|
559
|
-
}), /*#__PURE__*/_jsx(
|
|
560
|
-
open:
|
|
628
|
+
}), /*#__PURE__*/_jsx(ResponsiveDialog, {
|
|
629
|
+
open: customFeedbackOpen,
|
|
630
|
+
handleClose: handleCustomPromptCancel,
|
|
631
|
+
handleSubmit: handleCustomPromptContinue,
|
|
632
|
+
submitText: t("generate_feedback"),
|
|
561
633
|
maxWidth: "sm",
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
634
|
+
dialogTitle: isAnyGenerating ? t("generating_feedback") : t("generate_feedback"),
|
|
635
|
+
isSubmitDisabled: isAnyGenerating,
|
|
636
|
+
isExperimentalFeature: true,
|
|
637
|
+
startIcon: /*#__PURE__*/_jsx(AutoFixHigh, {}),
|
|
638
|
+
children: /*#__PURE__*/_jsxs(_Fragment, {
|
|
639
|
+
children: [/*#__PURE__*/_jsx(Grid, {
|
|
640
|
+
textAlign: "center",
|
|
641
|
+
sx: {
|
|
642
|
+
...(!isAnyGenerating && {
|
|
643
|
+
display: "none"
|
|
644
|
+
})
|
|
645
|
+
},
|
|
646
|
+
mx: 2,
|
|
647
|
+
size: 12,
|
|
648
|
+
children: /*#__PURE__*/_jsx(NualaCreating, {})
|
|
649
|
+
}), /*#__PURE__*/_jsx(Grid, {
|
|
575
650
|
sx: {
|
|
576
|
-
|
|
651
|
+
...(!isAnyGenerating && {
|
|
652
|
+
display: "none"
|
|
653
|
+
})
|
|
577
654
|
},
|
|
578
|
-
|
|
655
|
+
mt: 2,
|
|
656
|
+
size: 12,
|
|
657
|
+
children: /*#__PURE__*/_jsx(LinearProgress, {})
|
|
658
|
+
}), !isAnyGenerating && /*#__PURE__*/_jsxs(Grid, {
|
|
659
|
+
container: true,
|
|
660
|
+
spacing: 2,
|
|
661
|
+
children: [/*#__PURE__*/_jsx(Grid, {
|
|
579
662
|
size: 12,
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
663
|
+
children: /*#__PURE__*/_jsxs(Alert, {
|
|
664
|
+
severity: "info",
|
|
665
|
+
children: [t("experimental_feature_desc"), " - ", " ", /*#__PURE__*/_jsxs(Link, {
|
|
666
|
+
to: "/contact",
|
|
667
|
+
children: [t("contact_form"), "."]
|
|
668
|
+
})]
|
|
669
|
+
})
|
|
670
|
+
}), /*#__PURE__*/_jsx(Grid, {
|
|
671
|
+
size: {
|
|
672
|
+
xs: 12
|
|
588
673
|
},
|
|
589
|
-
children:
|
|
590
|
-
variant: "
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
674
|
+
children: /*#__PURE__*/_jsxs(Typography, {
|
|
675
|
+
variant: "body2",
|
|
676
|
+
children: [t("generate_feedback_view_the"), " ", /*#__PURE__*/_jsx(MuiLink, {
|
|
677
|
+
component: "button",
|
|
678
|
+
variant: "body2",
|
|
679
|
+
onClick: handleOpenPDF,
|
|
680
|
+
children: t("generate_feedback_rubric_name")
|
|
681
|
+
}), ", ", t("generate_feedback_rubric_desc")]
|
|
682
|
+
})
|
|
683
|
+
}), /*#__PURE__*/_jsx(Grid, {
|
|
684
|
+
size: 12,
|
|
685
|
+
children: /*#__PURE__*/_jsx(TextField, {
|
|
686
|
+
value: customFeedbackText,
|
|
687
|
+
onChange: e => setCustomFeedbackText(e.target.value),
|
|
688
|
+
placeholder: t("custom_feedback_placeholder"),
|
|
689
|
+
multiline: true,
|
|
690
|
+
minRows: 4,
|
|
691
|
+
fullWidth: true
|
|
692
|
+
})
|
|
693
|
+
})]
|
|
694
|
+
})]
|
|
605
695
|
})
|
|
696
|
+
}), /*#__PURE__*/_jsx(PDFViewer, {
|
|
697
|
+
open: openPDF,
|
|
698
|
+
handleClose: handleClosePDF,
|
|
699
|
+
pdfUrl: pdfUrl,
|
|
700
|
+
pageNumber: pageNumber,
|
|
701
|
+
setPageNumber: setPageNumber,
|
|
702
|
+
scale: scale,
|
|
703
|
+
setScale: setScale,
|
|
704
|
+
t: t
|
|
606
705
|
})]
|
|
607
706
|
});
|
|
608
707
|
}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { useState, useRef, useEffect } from "react";
|
|
2
2
|
import { CSVLink } from "react-csv";
|
|
3
|
-
import { Box, DialogContentText, Typography, Alert, TextField, Divider } from "@mui/material";
|
|
3
|
+
import { Box, DialogContentText, Typography, Alert, TextField, Divider, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper } from "@mui/material";
|
|
4
4
|
import { makeStyles } from "tss-react/mui";
|
|
5
5
|
import FileCopyIcon from "@mui/icons-material/FileCopy";
|
|
6
6
|
import DownloadIcon from "@mui/icons-material/Download";
|
|
7
|
+
import FrontHandIcon from '@mui/icons-material/FrontHand';
|
|
8
|
+
import WavingHandIcon from '@mui/icons-material/WavingHand';
|
|
9
|
+
import BadgeIcon from '@mui/icons-material/Badge';
|
|
7
10
|
import PhraseList from "../../Lists/Phrases/Phrases";
|
|
8
|
-
import ExampleCSVImage from "./upload-phrases-csv.png";
|
|
9
11
|
import ParseScriptErrors from "../../Lists/ParseScriptErrors/ParseScriptErrors";
|
|
10
12
|
import DefaultButton from "../../Misc/DefaultColourButton/DefaultColourButton";
|
|
11
13
|
import CSVUploader from "../../Misc/CSVUploader/CSVUploader";
|
|
@@ -19,7 +21,6 @@ import DialogContent from "@mui/material/DialogContent";
|
|
|
19
21
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
|
20
22
|
import { useTheme } from "@mui/material/styles";
|
|
21
23
|
import Popper from "@mui/material/Popper";
|
|
22
|
-
import Paper from "@mui/material/Paper";
|
|
23
24
|
import Grow from "@mui/material/Grow";
|
|
24
25
|
import ClickAwayListener from "@mui/material/ClickAwayListener";
|
|
25
26
|
import MenuItem from "@mui/material/MenuItem";
|
|
@@ -29,14 +30,17 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
29
30
|
const phrasesData = [{
|
|
30
31
|
phrase: "Hello",
|
|
31
32
|
translation: "hola",
|
|
33
|
+
definitions: "a greeting",
|
|
32
34
|
image: "https://images.nualang.com/eyJidWNrZXQiOiJudWFsYW5nLWltYWdlcy1wcm9kIiwia2V5IjoicGhyYXNlcy9EYTNXcThzUUktMTYwMzg4Nzg3MjU2NS53ZWJwIn0="
|
|
33
35
|
}, {
|
|
34
36
|
phrase: "Goodbye",
|
|
35
37
|
translation: "adios",
|
|
38
|
+
definitions: "a farewell",
|
|
36
39
|
image: "https://images.nualang.com/eyJidWNrZXQiOiJudWFsYW5nLWltYWdlcy1wcm9kIiwia2V5IjoicGhyYXNlcy9RbXBFcWZYdHItMTYwMzg4Nzk2MjAzMC53ZWJwIn0="
|
|
37
40
|
}, {
|
|
38
41
|
phrase: "My name is",
|
|
39
42
|
translation: "mi nombre es | me llamo",
|
|
43
|
+
definitions: "used to introduce yourself",
|
|
40
44
|
image: "https://images.nualang.com/eyJidWNrZXQiOiJudWFsYW5nLWltYWdlcy1wcm9kIiwia2V5IjoicGhyYXNlcy9hREFmczhFeTEtMTYwMzg5MDM2MjAxNy53ZWJwIn0="
|
|
41
45
|
}];
|
|
42
46
|
const phrasesHeaders = [{
|
|
@@ -45,6 +49,9 @@ const phrasesHeaders = [{
|
|
|
45
49
|
}, {
|
|
46
50
|
label: "translations",
|
|
47
51
|
key: "translation"
|
|
52
|
+
}, {
|
|
53
|
+
label: "definitions",
|
|
54
|
+
key: "definitions"
|
|
48
55
|
}, {
|
|
49
56
|
label: "image",
|
|
50
57
|
key: "image"
|
|
@@ -54,6 +61,22 @@ const PhrasesCsvLink = {
|
|
|
54
61
|
data: phrasesData,
|
|
55
62
|
filename: "phrases_template.csv"
|
|
56
63
|
};
|
|
64
|
+
const csvStructureExampleRows = [{
|
|
65
|
+
phrase: "hello",
|
|
66
|
+
translations: "hola",
|
|
67
|
+
definitions: "A word used to greet someone",
|
|
68
|
+
image: FrontHandIcon
|
|
69
|
+
}, {
|
|
70
|
+
phrase: "goodbye",
|
|
71
|
+
translations: "adios",
|
|
72
|
+
definitions: "A word used when leaving someone",
|
|
73
|
+
image: WavingHandIcon
|
|
74
|
+
}, {
|
|
75
|
+
phrase: "my name is",
|
|
76
|
+
translations: "mi nombre es | me llamo",
|
|
77
|
+
definitions: "A phrase used to tell someone your name",
|
|
78
|
+
image: BadgeIcon
|
|
79
|
+
}];
|
|
57
80
|
const useStyles = makeStyles()(theme => ({
|
|
58
81
|
parseErrorsBox: {
|
|
59
82
|
marginTop: theme.spacing()
|
|
@@ -65,10 +88,6 @@ const useStyles = makeStyles()(theme => ({
|
|
|
65
88
|
dialog: {
|
|
66
89
|
marginBottom: "15px"
|
|
67
90
|
},
|
|
68
|
-
image: {
|
|
69
|
-
height: "auto",
|
|
70
|
-
width: "100%"
|
|
71
|
-
},
|
|
72
91
|
csvButton: {
|
|
73
92
|
textDecoration: "none"
|
|
74
93
|
},
|
|
@@ -92,6 +111,7 @@ function parsePhraseWithAlternatives(phraseString) {
|
|
|
92
111
|
* - phrase: Primary phrase with optional alternatives separated by '|'
|
|
93
112
|
* Example: "Hello|Hi|Hey"
|
|
94
113
|
* - translations: Multiple translations separated by '|'
|
|
114
|
+
* - definitions: Multiple definitions separated by '|'
|
|
95
115
|
* - image: Optional image URL
|
|
96
116
|
*/
|
|
97
117
|
|
|
@@ -135,10 +155,12 @@ function UploadPhrases({
|
|
|
135
155
|
alternatives
|
|
136
156
|
} = parsePhraseWithAlternatives(data.phrase);
|
|
137
157
|
const formattedTranslations = data.translations.split("|").map(t => t.trim().replace(/^"|"$/g, "").trim());
|
|
158
|
+
const formattedDefinitions = data.definitions ? data.definitions.split("|").map(d => d.trim().replace(/^"|"$/g, "").trim()).filter(Boolean) : [];
|
|
138
159
|
return {
|
|
139
160
|
phrase: primary,
|
|
140
161
|
alternativeVersions: alternatives,
|
|
141
162
|
translations: formattedTranslations,
|
|
163
|
+
definitions: formattedDefinitions,
|
|
142
164
|
image: data.image ? data.image.trim() : "",
|
|
143
165
|
voices: [],
|
|
144
166
|
idx: index
|
|
@@ -175,8 +197,6 @@ function UploadPhrases({
|
|
|
175
197
|
setErrorMessage("File must be in the .csv format");
|
|
176
198
|
return;
|
|
177
199
|
} else if (results.errors && results.errors.length) {
|
|
178
|
-
// formats errors coming from CSVReader into common error format
|
|
179
|
-
// used for validating rows
|
|
180
200
|
const formattedErrors = results.errors.reduce((acc, cur) => {
|
|
181
201
|
acc.push({
|
|
182
202
|
row: cur.row,
|
|
@@ -212,10 +232,51 @@ function UploadPhrases({
|
|
|
212
232
|
children: t("upload_phrases_desc")
|
|
213
233
|
}), /*#__PURE__*/_jsx(Box, {
|
|
214
234
|
py: 1,
|
|
215
|
-
children: /*#__PURE__*/_jsx(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
235
|
+
children: /*#__PURE__*/_jsx(TableContainer, {
|
|
236
|
+
component: Paper,
|
|
237
|
+
variant: "outlined",
|
|
238
|
+
children: /*#__PURE__*/_jsxs(Table, {
|
|
239
|
+
size: "small",
|
|
240
|
+
"aria-label": "CSV structure example",
|
|
241
|
+
children: [/*#__PURE__*/_jsx(TableHead, {
|
|
242
|
+
children: /*#__PURE__*/_jsxs(TableRow, {
|
|
243
|
+
children: [/*#__PURE__*/_jsx(TableCell, {
|
|
244
|
+
children: /*#__PURE__*/_jsx("strong", {
|
|
245
|
+
children: t("phrase")
|
|
246
|
+
})
|
|
247
|
+
}), /*#__PURE__*/_jsx(TableCell, {
|
|
248
|
+
children: /*#__PURE__*/_jsx("strong", {
|
|
249
|
+
children: t("translations")
|
|
250
|
+
})
|
|
251
|
+
}), /*#__PURE__*/_jsx(TableCell, {
|
|
252
|
+
children: /*#__PURE__*/_jsx("strong", {
|
|
253
|
+
children: t("definitions")
|
|
254
|
+
})
|
|
255
|
+
}), /*#__PURE__*/_jsx(TableCell, {
|
|
256
|
+
children: /*#__PURE__*/_jsx("strong", {
|
|
257
|
+
children: t("image")
|
|
258
|
+
})
|
|
259
|
+
})]
|
|
260
|
+
})
|
|
261
|
+
}), /*#__PURE__*/_jsx(TableBody, {
|
|
262
|
+
children: csvStructureExampleRows.map((row, index) => {
|
|
263
|
+
const ImageIcon = row.image;
|
|
264
|
+
return /*#__PURE__*/_jsxs(TableRow, {
|
|
265
|
+
children: [/*#__PURE__*/_jsx(TableCell, {
|
|
266
|
+
children: row.phrase
|
|
267
|
+
}), /*#__PURE__*/_jsx(TableCell, {
|
|
268
|
+
children: row.translations
|
|
269
|
+
}), /*#__PURE__*/_jsx(TableCell, {
|
|
270
|
+
children: row.definitions
|
|
271
|
+
}), /*#__PURE__*/_jsx(TableCell, {
|
|
272
|
+
children: /*#__PURE__*/_jsx(ImageIcon, {
|
|
273
|
+
fontSize: "small"
|
|
274
|
+
})
|
|
275
|
+
})]
|
|
276
|
+
}, `${row.phrase}-${index}`);
|
|
277
|
+
})
|
|
278
|
+
})]
|
|
279
|
+
})
|
|
219
280
|
})
|
|
220
281
|
}), /*#__PURE__*/_jsxs(Box, {
|
|
221
282
|
pb: 2,
|
|
@@ -257,7 +318,7 @@ function UploadPhrases({
|
|
|
257
318
|
id: "csvDataInput",
|
|
258
319
|
name: "csvDataInput",
|
|
259
320
|
label: t("paste_your_csv_data"),
|
|
260
|
-
placeholder: `phrase, translations\n"Hola", "Hello | Hi"\n"Adios", "Goodbye"`,
|
|
321
|
+
placeholder: `phrase, translations, definitions\n"Hola", "Hello | Hi", "a greeting"\n"Adios", "Goodbye", "a farewell"`,
|
|
261
322
|
value: csvDataInput,
|
|
262
323
|
fullWidth: true,
|
|
263
324
|
multiline: true,
|
|
Binary file
|