@nualang/nualang-ui-components 0.1.1350 → 0.1.1353
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/README.md +24 -8
- package/dist/Cards/Classroom/Classroom.js +0 -4
- package/dist/Cards/Course/Course.js +0 -5
- package/dist/Dialogs/CreatePhraseGroupDialog/CreatePhraseGroupDialog.js +143 -0
- package/dist/Dialogs/Feedback/FeedbackDialog.js +1 -4
- package/dist/Dialogs/GenerateDefinitions/GenerateDefinitions.js +205 -0
- package/dist/Dialogs/GenerateDefinitions/package.json +6 -0
- package/dist/Dialogs/GeneratePhrases/GeneratePhrases.js +33 -3
- package/dist/Dialogs/UploadPhrases/UploadPhrases.js +75 -14
- package/dist/Dialogs/UploadPhrases/upload-phrases-csv.png +0 -0
- package/dist/Editors/PhraseList/PhraseList.js +263 -0
- package/dist/Editors/Phrases/Phrases.js +92 -11
- package/dist/Exercises/Listener/Listener.js +4 -2
- package/dist/Exercises/PhraseGroup/Games/ListeningGame/ListeningGame.js +89 -0
- package/dist/Exercises/PhraseGroup/Games/PronunciationGame/PronunciationGame.js +85 -0
- package/dist/Exercises/PhraseGroup/Games/TranslationGame/TranslationGame.js +90 -0
- package/dist/Exercises/PhraseGroup/Games/index.js +4 -0
- package/dist/Exercises/PhraseGroup/PhraseGroup.js +245 -0
- package/dist/Exercises/PhraseGroup/PhraseGroup.test.js +28 -0
- package/dist/Exercises/PhraseGroup/PhraseGroupGameOptions/PhraseGroupGame/PhraseGroupGame.js +259 -0
- package/dist/Exercises/PhraseGroup/PhraseGroupGameOptions/PhraseGroupGames.js +62 -0
- package/dist/Exercises/PhraseGroup/PhraseGroupInfo/PhraseGroupInfo.js +145 -0
- package/dist/Exercises/PhraseGroup/package.json +7 -0
- package/dist/Exercises/Pronouncer/Pronouncer.js +4 -2
- package/dist/Exercises/Translator/Translator.js +4 -2
- package/dist/Forms/UpdatePhraseList/UpdatePhraseList.js +175 -0
- package/dist/Lists/Classrooms/Classrooms.js +0 -1
- package/dist/Lists/CourseOutline/CourseOutline.js +0 -6
- package/dist/Lists/Courses/Courses.js +0 -6
- package/dist/Lists/Exercises/Exercises.js +589 -1012
- package/dist/Lists/Members/Members.js +17 -29
- package/dist/Lists/Phrases/Phrases.js +155 -107
- 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/Activity/Exercise/Attempt/Attempt.js +1 -3
- package/dist/Screens/Classrooms/Classrooms.js +0 -3
- package/dist/Screens/Classrooms/SearchClassrooms/SearchClassrooms.js +0 -2
- package/dist/Screens/Classrooms/ViewClassroom/ViewClassroom.js +141 -15
- package/dist/Screens/Courses/Courses.js +0 -6
- package/dist/Screens/Courses/SearchCourses/SearchCourses.js +0 -2
- package/dist/Screens/Courses/ViewCourse/ViewCourse.js +1 -9
- package/dist/Screens/Courses/ViewCourse/ViewTopic/ViewTopic.js +62 -197
- package/dist/Screens/Dashboard/Dashboard.js +1 -5
- package/dist/Screens/Profile/Profile.js +2 -262
- package/dist/Screens/Search/Search.js +0 -2
- package/dist/Tables/ClassroomRows/ClassroomRows.js +1 -4
- package/dist/Tables/CourseRows/CourseRows.js +1 -4
- 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 +8 -3
- package/dist/hooks/usePhonetics.js +42 -0
- package/dist/utils/japaneseTokenizer.js +55 -0
- package/package.json +7 -2
- package/dist/Screens/Bots/Bots.js +0 -240
- package/dist/Screens/Bots/SearchBots/SearchBots.js +0 -280
- package/dist/Screens/Bots/SearchBots/package.json +0 -6
- package/dist/Screens/Bots/package.json +0 -6
- package/dist/Screens/Roleplays/SearchRoleplays/SearchRoleplays.js +0 -290
- package/dist/Screens/Roleplays/SearchRoleplays/package.json +0 -6
package/README.md
CHANGED
|
@@ -5,27 +5,27 @@ to `npm` and consumed from there by nualang web apps.
|
|
|
5
5
|
|
|
6
6
|
## Getting Started
|
|
7
7
|
Clone this repository and then execute `npm install`. `npm start` will start
|
|
8
|
-
[
|
|
8
|
+
[storybook](https://storybook.js.org) and allow you to browse the available components.
|
|
9
9
|
|
|
10
10
|
## Develop
|
|
11
|
-
```
|
|
11
|
+
```bash
|
|
12
12
|
npm start
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
## Create Local Package
|
|
16
16
|
Compile and pack (This will create a '.tgz' file):
|
|
17
|
-
```
|
|
17
|
+
```bash
|
|
18
18
|
npm run localpack
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
Install package (Inside webapp):
|
|
22
22
|
```
|
|
23
|
-
npm i ../ui-components/
|
|
23
|
+
npm i ../ui-components/nualang-ui-components-0.1.XXX.tgz
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
If webapp is caching previous versions (Add a character to the end of the .tgz, e.g 'a') and install again:
|
|
27
|
-
```
|
|
28
|
-
yarn add ../ui-components/
|
|
27
|
+
```bash
|
|
28
|
+
yarn add ../ui-components/nualang-ui-components-0.1.XXXa.tgz
|
|
29
29
|
```
|
|
30
30
|
|
|
31
31
|
## Publish to npm
|
|
@@ -59,7 +59,23 @@ Check for any lint errors or warnings.
|
|
|
59
59
|
Run the babel compiler to compile es6 to js.
|
|
60
60
|
|
|
61
61
|
### `npm run postversion`
|
|
62
|
-
Publish a new version of the
|
|
62
|
+
Publish a new version of the package to npm. npm will complain
|
|
63
63
|
|
|
64
64
|
### `npm run chromatic`
|
|
65
|
-
Publish a new version of the
|
|
65
|
+
Publish a new version of the package to https://www.chromatic.com/
|
|
66
|
+
|
|
67
|
+
## Testing & Coverage
|
|
68
|
+
|
|
69
|
+
Run unit tests with Vitest in watch mode:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npm run vitest
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
To generate code coverage (uses V8 provider):
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npm run test:coverage
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Coverage reports will be written to the `coverage` directory. A text summary is printed to the console; an LCOV file (`coverage/lcov.info`) and a JSON report are also generated. This coverage step is intentionally not wired into CI — run it locally when you want to inspect or publish coverage results.
|
|
@@ -3,7 +3,6 @@ import PropTypes from "prop-types";
|
|
|
3
3
|
import { useInView } from "react-intersection-observer";
|
|
4
4
|
import { Button, Box, Typography, IconButton, Menu, MenuItem, Tooltip, CardActions, CardActionArea, Divider, CircularProgress } from "@mui/material";
|
|
5
5
|
import Skeleton from "@mui/material/Skeleton";
|
|
6
|
-
import Rating from "@mui/material/Rating";
|
|
7
6
|
import Group from "@mui/icons-material/Group";
|
|
8
7
|
import ExitToApp from "@mui/icons-material/ExitToApp";
|
|
9
8
|
import Add from "@mui/icons-material/Add";
|
|
@@ -12,7 +11,6 @@ import MoreVert from "@mui/icons-material/MoreVert";
|
|
|
12
11
|
|
|
13
12
|
// components
|
|
14
13
|
import MembersDialog from "../../Dialogs/Members/Members";
|
|
15
|
-
import MembersList from "../../Lists/Members/Members";
|
|
16
14
|
import { capitalize } from "../../utils/index";
|
|
17
15
|
import PlaceholderImages from "../../utils/placeholder-images/index";
|
|
18
16
|
import ProgressiveCardMedia from "../../Misc/ProgressiveCardMedia/ProgressiveCardMedia";
|
|
@@ -122,7 +120,6 @@ function ClassroomCard({
|
|
|
122
120
|
handleLeaveClassroom,
|
|
123
121
|
membersProps,
|
|
124
122
|
getMemberDetails,
|
|
125
|
-
getMemberInfo,
|
|
126
123
|
memberPlaceholderImageUrl,
|
|
127
124
|
getClassroomMember,
|
|
128
125
|
getClassroomMembers,
|
|
@@ -370,7 +367,6 @@ Classroom.propTypes = {
|
|
|
370
367
|
forLang: PropTypes.string.isRequired,
|
|
371
368
|
isMember: PropTypes.bool,
|
|
372
369
|
isCreator: PropTypes.bool,
|
|
373
|
-
members: MembersList.propTypes,
|
|
374
370
|
createdAt: PropTypes.number.isRequired,
|
|
375
371
|
createdBy: PropTypes.string.isRequired,
|
|
376
372
|
userImage: PropTypes.string
|
|
@@ -21,7 +21,6 @@ import DeleteIcon from "@mui/icons-material/Delete";
|
|
|
21
21
|
// components
|
|
22
22
|
import MembersDialog from "../../Dialogs/Members/Members";
|
|
23
23
|
import CourseOutlineDialog from "../../Dialogs/CourseOutline/CourseOutline";
|
|
24
|
-
import MembersList from "../../Lists/Members/Members";
|
|
25
24
|
import PlaceholderImages from "../../utils/placeholder-images/index";
|
|
26
25
|
import { capitalize, formatNumberTotal } from "../../utils/index";
|
|
27
26
|
import PlayArrowIcon from "@mui/icons-material/PlayArrow";
|
|
@@ -233,7 +232,6 @@ function CourseCard({
|
|
|
233
232
|
selectable,
|
|
234
233
|
selected,
|
|
235
234
|
getMemberDetails,
|
|
236
|
-
getMemberInfo,
|
|
237
235
|
memberPlaceholderImageUrl,
|
|
238
236
|
getCourseSections,
|
|
239
237
|
getCourseMember,
|
|
@@ -582,7 +580,6 @@ function CourseCard({
|
|
|
582
580
|
isClassroomCreator: isClassroomCreator,
|
|
583
581
|
isMember: isMember,
|
|
584
582
|
isAdmin: isAdmin,
|
|
585
|
-
viewCourse: viewCourse,
|
|
586
583
|
startCourse: startCourse,
|
|
587
584
|
leaveCourse: leaveCourse,
|
|
588
585
|
handleRemoveCourse: handleRemoveCourse,
|
|
@@ -603,7 +600,6 @@ function CourseCard({
|
|
|
603
600
|
open: isMembersOpen,
|
|
604
601
|
handleClose: handleCloseMembers,
|
|
605
602
|
isCreator: isClassroomCreator ?? isCreator,
|
|
606
|
-
isCourseMember: true,
|
|
607
603
|
members: membersQuery.isSuccess ? membersQuery.data.Items : [],
|
|
608
604
|
...membersProps
|
|
609
605
|
}), /*#__PURE__*/_jsx(CourseOutlineDialog, {
|
|
@@ -644,7 +640,6 @@ MemoizedCourse.propTypes = {
|
|
|
644
640
|
createdAt: PropTypes.number.isRequired,
|
|
645
641
|
createdBy: PropTypes.string.isRequired,
|
|
646
642
|
userImage: PropTypes.string,
|
|
647
|
-
members: PropTypes.arrayOf(MembersList.propTypes),
|
|
648
643
|
sections: PropTypes.arrayOf({
|
|
649
644
|
sectionId: PropTypes.string.isRequired,
|
|
650
645
|
sectionName: PropTypes.string.isRequired,
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
2
|
+
import TextField from "@mui/material/TextField";
|
|
3
|
+
import Box from "@mui/material/Box";
|
|
4
|
+
import Button from "@mui/material/Button";
|
|
5
|
+
import Dialog from "@mui/material/Dialog";
|
|
6
|
+
import DialogTitle from "@mui/material/DialogTitle";
|
|
7
|
+
import DialogContent from "@mui/material/DialogContent";
|
|
8
|
+
import DialogActions from "@mui/material/DialogActions";
|
|
9
|
+
import CircularProgress from "@mui/material/CircularProgress";
|
|
10
|
+
import Grid from "@mui/material/Grid";
|
|
11
|
+
import ImageSelector from "../../Forms/ImageSelector/ImageSelector";
|
|
12
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
13
|
+
export default function CreatePhraseGroupDialog({
|
|
14
|
+
open,
|
|
15
|
+
handleClose,
|
|
16
|
+
handleCreate,
|
|
17
|
+
onCreated,
|
|
18
|
+
t = text => text,
|
|
19
|
+
fileSizeLimit,
|
|
20
|
+
verificationStatus
|
|
21
|
+
}) {
|
|
22
|
+
const [name, setName] = useState("");
|
|
23
|
+
const [description, setDescription] = useState("");
|
|
24
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
25
|
+
const [selectedImage, setSelectedImage] = useState({
|
|
26
|
+
image: "",
|
|
27
|
+
imageSelectionMethod: "default_image"
|
|
28
|
+
});
|
|
29
|
+
const handleImageSelection = event => {
|
|
30
|
+
const {
|
|
31
|
+
name,
|
|
32
|
+
value
|
|
33
|
+
} = event.target;
|
|
34
|
+
setSelectedImage(prev => ({
|
|
35
|
+
...prev,
|
|
36
|
+
[name]: value
|
|
37
|
+
}));
|
|
38
|
+
};
|
|
39
|
+
const removeImage = () => {
|
|
40
|
+
setSelectedImage({
|
|
41
|
+
image: "",
|
|
42
|
+
imageSelectionMethod: "default_image"
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
const handleSubmit = async () => {
|
|
46
|
+
if (!name.trim()) return;
|
|
47
|
+
setIsLoading(true);
|
|
48
|
+
try {
|
|
49
|
+
const createdPhraseGroup = await handleCreate({
|
|
50
|
+
phraseGroupName: name.trim(),
|
|
51
|
+
description: description.trim()
|
|
52
|
+
});
|
|
53
|
+
resetAndClose();
|
|
54
|
+
if (onCreated && createdPhraseGroup) {
|
|
55
|
+
onCreated(createdPhraseGroup, selectedImage.image || null);
|
|
56
|
+
}
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error("Failed to create phrase group:", error);
|
|
59
|
+
} finally {
|
|
60
|
+
setIsLoading(false);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
const resetAndClose = () => {
|
|
64
|
+
setName("");
|
|
65
|
+
setDescription("");
|
|
66
|
+
setSelectedImage({
|
|
67
|
+
image: "",
|
|
68
|
+
imageSelectionMethod: "default_image"
|
|
69
|
+
});
|
|
70
|
+
handleClose();
|
|
71
|
+
};
|
|
72
|
+
return /*#__PURE__*/_jsxs(Dialog, {
|
|
73
|
+
open: open,
|
|
74
|
+
onClose: isLoading ? undefined : resetAndClose,
|
|
75
|
+
maxWidth: "sm",
|
|
76
|
+
fullWidth: true,
|
|
77
|
+
children: [/*#__PURE__*/_jsx(DialogTitle, {
|
|
78
|
+
children: t("create_phrase_group") || "Create Phrase List"
|
|
79
|
+
}), /*#__PURE__*/_jsx(DialogContent, {
|
|
80
|
+
children: /*#__PURE__*/_jsxs(Box, {
|
|
81
|
+
display: "flex",
|
|
82
|
+
flexDirection: "column",
|
|
83
|
+
gap: 2,
|
|
84
|
+
pt: 1,
|
|
85
|
+
children: [/*#__PURE__*/_jsx(TextField, {
|
|
86
|
+
label: t("phrase_group_name") || "Name",
|
|
87
|
+
value: name,
|
|
88
|
+
onChange: e => setName(e.target.value),
|
|
89
|
+
fullWidth: true,
|
|
90
|
+
autoFocus: true,
|
|
91
|
+
required: true,
|
|
92
|
+
disabled: isLoading,
|
|
93
|
+
inputProps: {
|
|
94
|
+
maxLength: 100
|
|
95
|
+
},
|
|
96
|
+
onKeyDown: e => e.key === "Enter" && handleSubmit()
|
|
97
|
+
}), /*#__PURE__*/_jsx(TextField, {
|
|
98
|
+
label: t("description") || "Description",
|
|
99
|
+
value: description,
|
|
100
|
+
onChange: e => setDescription(e.target.value),
|
|
101
|
+
fullWidth: true,
|
|
102
|
+
multiline: true,
|
|
103
|
+
rows: 3,
|
|
104
|
+
disabled: isLoading,
|
|
105
|
+
inputProps: {
|
|
106
|
+
maxLength: 300
|
|
107
|
+
}
|
|
108
|
+
}), /*#__PURE__*/_jsx(Grid, {
|
|
109
|
+
size: 12,
|
|
110
|
+
children: /*#__PURE__*/_jsx(ImageSelector, {
|
|
111
|
+
t: t,
|
|
112
|
+
name: "image",
|
|
113
|
+
value: selectedImage.image,
|
|
114
|
+
handleChange: handleImageSelection,
|
|
115
|
+
fileSizeLimit: fileSizeLimit,
|
|
116
|
+
removeImage: removeImage,
|
|
117
|
+
autoCrop: true,
|
|
118
|
+
autoSuggest: false,
|
|
119
|
+
selectedImage: selectedImage,
|
|
120
|
+
verificationStatus: verificationStatus
|
|
121
|
+
})
|
|
122
|
+
})]
|
|
123
|
+
})
|
|
124
|
+
}), /*#__PURE__*/_jsxs(DialogActions, {
|
|
125
|
+
children: [/*#__PURE__*/_jsx(Button, {
|
|
126
|
+
onClick: resetAndClose,
|
|
127
|
+
color: "primary",
|
|
128
|
+
disabled: isLoading,
|
|
129
|
+
children: t("cancel") || "Cancel"
|
|
130
|
+
}), /*#__PURE__*/_jsx(Button, {
|
|
131
|
+
onClick: handleSubmit,
|
|
132
|
+
color: "primary",
|
|
133
|
+
variant: "contained",
|
|
134
|
+
disabled: !name.trim() || isLoading,
|
|
135
|
+
startIcon: isLoading ? /*#__PURE__*/_jsx(CircularProgress, {
|
|
136
|
+
size: 16,
|
|
137
|
+
color: "inherit"
|
|
138
|
+
}) : null,
|
|
139
|
+
children: isLoading ? t("creating") || "Creating..." : t("create") || "Create"
|
|
140
|
+
})]
|
|
141
|
+
})]
|
|
142
|
+
});
|
|
143
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { useState, useEffect } from "react";
|
|
2
|
+
import ResponsiveDialog from "../ResponsiveDialog/ResponsiveDialog";
|
|
3
|
+
import { AutoFixHigh } from "@mui/icons-material";
|
|
4
|
+
import { Alert, Box, Card, Grid, LinearProgress, Typography } from "@mui/material";
|
|
5
|
+
import NualaCreating from "../../Misc/NualaCreating/NualaCreating";
|
|
6
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
7
|
+
function GenerateDefinitions({
|
|
8
|
+
t,
|
|
9
|
+
open,
|
|
10
|
+
handleClose,
|
|
11
|
+
selectedPhraseObjects,
|
|
12
|
+
makeChatGptApiRequest,
|
|
13
|
+
handleUpdatePhrase,
|
|
14
|
+
learnLang,
|
|
15
|
+
forLang,
|
|
16
|
+
siteLanguage,
|
|
17
|
+
difficulty
|
|
18
|
+
}) {
|
|
19
|
+
const [isGenerating, setIsGenerating] = useState(false);
|
|
20
|
+
const [errorMessage, setErrorMessage] = useState(null);
|
|
21
|
+
const [generatedDefinitions, setGeneratedDefinitions] = useState([]);
|
|
22
|
+
const [isConfirming, setIsConfirming] = useState(false);
|
|
23
|
+
const hasResults = generatedDefinitions.length > 0;
|
|
24
|
+
|
|
25
|
+
// Auto-trigger generation when dialog opens
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (open && selectedPhraseObjects.length > 0 && !hasResults && !isGenerating) {
|
|
28
|
+
handleGenerate();
|
|
29
|
+
}
|
|
30
|
+
}, [open]);
|
|
31
|
+
function validateResponse(results) {
|
|
32
|
+
if (!Array.isArray(results)) return false;
|
|
33
|
+
for (const item of results) {
|
|
34
|
+
if (!item || typeof item !== "object") return false;
|
|
35
|
+
if (!Array.isArray(item.definitions)) return false;
|
|
36
|
+
for (const def of item.definitions) {
|
|
37
|
+
if (typeof def !== "string" || def.trim() === "") return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
const handleGenerate = async () => {
|
|
43
|
+
try {
|
|
44
|
+
setIsGenerating(true);
|
|
45
|
+
setErrorMessage(null);
|
|
46
|
+
const phrasesData = selectedPhraseObjects.map(p => ({
|
|
47
|
+
phraseId: p.phraseId,
|
|
48
|
+
phrase: p.phrase,
|
|
49
|
+
translations: p.translations || []
|
|
50
|
+
}));
|
|
51
|
+
const chatGptResponse = await makeChatGptApiRequest({
|
|
52
|
+
model: "gpt-4o",
|
|
53
|
+
promptKey: "generateDefinitions",
|
|
54
|
+
promptVariables: {
|
|
55
|
+
learnLang,
|
|
56
|
+
forLang,
|
|
57
|
+
siteLanguage,
|
|
58
|
+
difficulty,
|
|
59
|
+
phrases: phrasesData
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
const results = chatGptResponse.result;
|
|
63
|
+
if (!validateResponse(results)) {
|
|
64
|
+
throw new Error("Invalid AI response");
|
|
65
|
+
}
|
|
66
|
+
const merged = selectedPhraseObjects.map((original, index) => {
|
|
67
|
+
const generated = results.find(r => r.phraseId === original.phraseId) || results[index];
|
|
68
|
+
return {
|
|
69
|
+
phraseId: original.phraseId,
|
|
70
|
+
phrase: original.phrase,
|
|
71
|
+
oldDefinitions: original.definitions || [],
|
|
72
|
+
newDefinitions: generated ? generated.definitions : []
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
setGeneratedDefinitions(merged);
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.error(error);
|
|
78
|
+
setErrorMessage(t("generate_defintions_error"));
|
|
79
|
+
} finally {
|
|
80
|
+
setIsGenerating(false);
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
const handleConfirm = async () => {
|
|
84
|
+
try {
|
|
85
|
+
setIsConfirming(true);
|
|
86
|
+
for (const item of generatedDefinitions) {
|
|
87
|
+
const originalPhrase = selectedPhraseObjects.find(p => p.phraseId === item.phraseId);
|
|
88
|
+
await handleUpdatePhrase(item.phraseId, {
|
|
89
|
+
phrase: originalPhrase?.phrase || item.phrase,
|
|
90
|
+
translations: originalPhrase?.translations || [],
|
|
91
|
+
definitions: item.newDefinitions
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
setGeneratedDefinitions([]);
|
|
95
|
+
setIsConfirming(false);
|
|
96
|
+
handleClose();
|
|
97
|
+
} catch (error) {
|
|
98
|
+
console.error(error);
|
|
99
|
+
setIsConfirming(false);
|
|
100
|
+
setErrorMessage(t("save_definitions_error"));
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const handleCancel = () => {
|
|
104
|
+
handleClose();
|
|
105
|
+
setGeneratedDefinitions([]);
|
|
106
|
+
setErrorMessage(null);
|
|
107
|
+
};
|
|
108
|
+
const handleDialogClose = () => {
|
|
109
|
+
setGeneratedDefinitions([]);
|
|
110
|
+
setErrorMessage(null);
|
|
111
|
+
handleClose();
|
|
112
|
+
};
|
|
113
|
+
return /*#__PURE__*/_jsxs(ResponsiveDialog, {
|
|
114
|
+
open: open,
|
|
115
|
+
closeText: hasResults ? t("cancel") : t("close"),
|
|
116
|
+
handleClose: hasResults ? handleCancel : handleDialogClose,
|
|
117
|
+
handleSubmit: hasResults ? handleConfirm : handleGenerate,
|
|
118
|
+
submitText: hasResults ? t("confirm") : t("generate_definitions"),
|
|
119
|
+
dialogTitle: isGenerating ? t("generating_definitions") : t("generate_definitions"),
|
|
120
|
+
isSubmitDisabled: isGenerating || isConfirming,
|
|
121
|
+
isExperimentalFeature: true,
|
|
122
|
+
startIcon: /*#__PURE__*/_jsx(AutoFixHigh, {}),
|
|
123
|
+
children: [/*#__PURE__*/_jsx(Grid, {
|
|
124
|
+
textAlign: "center",
|
|
125
|
+
sx: {
|
|
126
|
+
...(!isGenerating && {
|
|
127
|
+
display: "none"
|
|
128
|
+
})
|
|
129
|
+
},
|
|
130
|
+
mx: 2,
|
|
131
|
+
size: 12,
|
|
132
|
+
children: /*#__PURE__*/_jsx(NualaCreating, {})
|
|
133
|
+
}), /*#__PURE__*/_jsx(Grid, {
|
|
134
|
+
sx: {
|
|
135
|
+
...(!isGenerating && {
|
|
136
|
+
display: "none"
|
|
137
|
+
})
|
|
138
|
+
},
|
|
139
|
+
mt: 2,
|
|
140
|
+
size: 12,
|
|
141
|
+
children: /*#__PURE__*/_jsx(LinearProgress, {})
|
|
142
|
+
}), !isGenerating && /*#__PURE__*/_jsxs(_Fragment, {
|
|
143
|
+
children: [generatedDefinitions.length === 0 && !errorMessage && /*#__PURE__*/_jsx(Typography, {
|
|
144
|
+
variant: "body2",
|
|
145
|
+
color: "text.secondary",
|
|
146
|
+
children: t("generate_definitions_info")
|
|
147
|
+
}), hasResults && /*#__PURE__*/_jsxs(_Fragment, {
|
|
148
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
149
|
+
sx: {
|
|
150
|
+
mb: 1
|
|
151
|
+
},
|
|
152
|
+
children: t("review_generated_definitions")
|
|
153
|
+
}), generatedDefinitions.map(item => /*#__PURE__*/_jsxs(Card, {
|
|
154
|
+
variant: "outlined",
|
|
155
|
+
sx: {
|
|
156
|
+
mb: 2,
|
|
157
|
+
p: 2
|
|
158
|
+
},
|
|
159
|
+
children: [/*#__PURE__*/_jsx(Typography, {
|
|
160
|
+
variant: "subtitle1",
|
|
161
|
+
fontWeight: "bold",
|
|
162
|
+
children: item.phrase
|
|
163
|
+
}), item.oldDefinitions.length > 0 && /*#__PURE__*/_jsxs(Box, {
|
|
164
|
+
sx: {
|
|
165
|
+
mt: 1
|
|
166
|
+
},
|
|
167
|
+
children: [/*#__PURE__*/_jsxs(Typography, {
|
|
168
|
+
variant: "caption",
|
|
169
|
+
color: "text.secondary",
|
|
170
|
+
children: [t("current_definitions"), ":"]
|
|
171
|
+
}), item.oldDefinitions.map((def, i) => /*#__PURE__*/_jsx(Typography, {
|
|
172
|
+
variant: "body2",
|
|
173
|
+
sx: {
|
|
174
|
+
textDecoration: "line-through",
|
|
175
|
+
opacity: 0.6
|
|
176
|
+
},
|
|
177
|
+
children: def
|
|
178
|
+
}, i))]
|
|
179
|
+
}), /*#__PURE__*/_jsxs(Box, {
|
|
180
|
+
sx: {
|
|
181
|
+
mt: 1
|
|
182
|
+
},
|
|
183
|
+
children: [/*#__PURE__*/_jsxs(Typography, {
|
|
184
|
+
variant: "caption",
|
|
185
|
+
color: "success.main",
|
|
186
|
+
children: [t("new_definitions"), ":"]
|
|
187
|
+
}), item.newDefinitions.map((def, i) => /*#__PURE__*/_jsx(Typography, {
|
|
188
|
+
variant: "body2",
|
|
189
|
+
color: "success.main",
|
|
190
|
+
children: def
|
|
191
|
+
}, i))]
|
|
192
|
+
})]
|
|
193
|
+
}, item.phraseId))]
|
|
194
|
+
}), errorMessage && /*#__PURE__*/_jsx(Grid, {
|
|
195
|
+
mt: 2,
|
|
196
|
+
size: 12,
|
|
197
|
+
children: /*#__PURE__*/_jsx(Alert, {
|
|
198
|
+
severity: "warning",
|
|
199
|
+
children: errorMessage
|
|
200
|
+
})
|
|
201
|
+
})]
|
|
202
|
+
})]
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
export default GenerateDefinitions;
|
|
@@ -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, {
|