@nualang/nualang-ui-components 0.1.1362 → 0.1.1364

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.
@@ -0,0 +1,49 @@
1
+ import { useState } from "react";
2
+ import { TextField } from "@mui/material";
3
+ import ResponsiveDialog from "../ResponsiveDialog/ResponsiveDialog";
4
+ import { jsx as _jsx } from "react/jsx-runtime";
5
+ export default function CreatePhraseListFromSelectionDialog({
6
+ open,
7
+ handleClose,
8
+ phraseIds = [],
9
+ handleCreatePhraseListFromSelection,
10
+ t = text => text
11
+ }) {
12
+ const [phraseListName, setPhraseListName] = useState("");
13
+ const handleSubmit = async () => {
14
+ if (!phraseListName.trim()) return;
15
+ try {
16
+ await handleCreatePhraseListFromSelection(phraseIds, phraseListName.trim());
17
+ setPhraseListName("");
18
+ handleClose();
19
+ } catch (error) {
20
+ console.error("Failed to create phrase list:", error);
21
+ }
22
+ };
23
+ const handleCloseReset = () => {
24
+ setPhraseListName("");
25
+ handleClose();
26
+ };
27
+ return /*#__PURE__*/_jsx(ResponsiveDialog, {
28
+ open: open,
29
+ handleClose: handleCloseReset,
30
+ handleSubmit: handleSubmit,
31
+ dialogTitle: t("create_phrase_list_from_selection"),
32
+ submitText: t("create"),
33
+ isSubmitDisabled: !phraseListName.trim(),
34
+ t: t,
35
+ children: /*#__PURE__*/_jsx(TextField, {
36
+ autoFocus: true,
37
+ fullWidth: true,
38
+ label: t("phrase_list_name"),
39
+ value: phraseListName,
40
+ onChange: e => setPhraseListName(e.target.value),
41
+ onKeyDown: e => e.key === "Enter" && handleSubmit(),
42
+ variant: "outlined",
43
+ size: "small",
44
+ sx: {
45
+ mt: 1
46
+ }
47
+ })
48
+ });
49
+ }
@@ -0,0 +1,113 @@
1
+ import { useState } from "react";
2
+ import { Typography, Avatar, Checkbox, CircularProgress, List, ListItem, ListItemText, ListItemAvatar, ListItemButton, Box } from "@mui/material";
3
+ import ImageSearchIcon from "@mui/icons-material/ImageSearch";
4
+ import AddIcon from "@mui/icons-material/Add";
5
+ import ResponsiveDialog from "../ResponsiveDialog/ResponsiveDialog";
6
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
7
+ export default function TransferPhraseDialog({
8
+ open,
9
+ handleClose,
10
+ phraseIds = [],
11
+ otherPhraseLists = [],
12
+ otherPhraseListsLoading = false,
13
+ handleTransferPhrase,
14
+ handleCreateNew,
15
+ t = text => text
16
+ }) {
17
+ const [selectedIds, setSelectedIds] = useState([]);
18
+ const toggleId = id => setSelectedIds(prev => prev.includes(id) ? prev.filter(x => x !== id) : [...prev, id]);
19
+ const handleSubmit = async () => {
20
+ if (selectedIds.length === 0) return;
21
+ await handleTransferPhrase(phraseIds, selectedIds);
22
+ setSelectedIds([]);
23
+ handleClose();
24
+ };
25
+ const handleCloseReset = () => {
26
+ setSelectedIds([]);
27
+ handleClose();
28
+ };
29
+ return /*#__PURE__*/_jsx(ResponsiveDialog, {
30
+ open: open,
31
+ handleClose: handleCloseReset,
32
+ handleSubmit: handleSubmit,
33
+ dialogTitle: t("transfer_phrases"),
34
+ submitText: t("transfer"),
35
+ isSubmitDisabled: selectedIds.length === 0,
36
+ t: t,
37
+ children: /*#__PURE__*/_jsxs(Box, {
38
+ sx: {
39
+ mt: 1
40
+ },
41
+ children: [otherPhraseListsLoading ? /*#__PURE__*/_jsx(Box, {
42
+ sx: {
43
+ display: "flex",
44
+ justifyContent: "center",
45
+ py: 2
46
+ },
47
+ children: /*#__PURE__*/_jsx(CircularProgress, {
48
+ size: 24
49
+ })
50
+ }) : otherPhraseLists.length === 0 ? /*#__PURE__*/_jsx(Typography, {
51
+ color: "textSecondary",
52
+ sx: {
53
+ mb: 1
54
+ },
55
+ children: t("no_other_phrase_lists")
56
+ }) : /*#__PURE__*/_jsx(List, {
57
+ disablePadding: true,
58
+ children: otherPhraseLists.map(list => {
59
+ const checked = selectedIds.includes(list.phraseListId);
60
+ return /*#__PURE__*/_jsx(ListItem, {
61
+ disablePadding: true,
62
+ secondaryAction: /*#__PURE__*/_jsx(Checkbox, {
63
+ edge: "end",
64
+ checked: checked,
65
+ onChange: () => toggleId(list.phraseListId),
66
+ inputProps: {
67
+ "aria-label": list.phraseListName
68
+ }
69
+ }),
70
+ children: /*#__PURE__*/_jsxs(ListItemButton, {
71
+ onClick: () => toggleId(list.phraseListId),
72
+ children: [/*#__PURE__*/_jsx(ListItemAvatar, {
73
+ children: list.picture ? /*#__PURE__*/_jsx(Avatar, {
74
+ src: list.picture,
75
+ alt: list.phraseListName
76
+ }) : /*#__PURE__*/_jsx(Avatar, {
77
+ children: /*#__PURE__*/_jsx(ImageSearchIcon, {
78
+ fontSize: "small"
79
+ })
80
+ })
81
+ }), /*#__PURE__*/_jsx(ListItemText, {
82
+ primary: list.phraseListName
83
+ })]
84
+ })
85
+ }, list.phraseListId);
86
+ })
87
+ }), handleCreateNew && /*#__PURE__*/_jsx(List, {
88
+ disablePadding: true,
89
+ children: /*#__PURE__*/_jsx(ListItem, {
90
+ disablePadding: true,
91
+ children: /*#__PURE__*/_jsxs(ListItemButton, {
92
+ onClick: handleCreateNew,
93
+ children: [/*#__PURE__*/_jsx(ListItemAvatar, {
94
+ children: /*#__PURE__*/_jsx(Avatar, {
95
+ sx: {
96
+ backgroundColor: "transparent"
97
+ },
98
+ children: /*#__PURE__*/_jsx(AddIcon, {
99
+ fontSize: "small",
100
+ sx: {
101
+ color: "secondary.main"
102
+ }
103
+ })
104
+ })
105
+ }), /*#__PURE__*/_jsx(ListItemText, {
106
+ primary: t("create_phrase_list_from_selection")
107
+ })]
108
+ })
109
+ })
110
+ })]
111
+ })
112
+ });
113
+ }
@@ -1,15 +1,22 @@
1
- import { useState, useEffect } from "react";
1
+ import { useState, useRef } from "react";
2
2
  import useMediaQuery from "@mui/material/useMediaQuery";
3
3
  import { useTheme } from "@mui/material/styles";
4
4
  import { makeStyles } from "tss-react/mui";
5
- import { Slide, IconButton, Dialog, AppBar, Toolbar, Typography } from "@mui/material";
5
+ import { Slide, IconButton, Dialog, AppBar, Toolbar, Typography, Button, Paper, ToggleButton, ToggleButtonGroup } from "@mui/material";
6
6
  import CloseIcon from "@mui/icons-material/Close";
7
+ import PublishIcon from "@mui/icons-material/Publish";
8
+ import GetAppIcon from "@mui/icons-material/GetApp";
9
+ import AddIcon from "@mui/icons-material/Add";
10
+ import AutoFixHighIcon from "@mui/icons-material/AutoFixHigh";
11
+ import ListIcon from "@mui/icons-material/List";
12
+ import TableChartIcon from "@mui/icons-material/TableChart";
13
+ import DefaultButton from "../../Misc/DefaultColourButton/DefaultColourButton";
7
14
  import { Tabs, Tab, Box, Tooltip } from "@mui/material";
8
15
  import ArrowDropDownIcon from "@mui/icons-material/ArrowDropDown";
9
16
  import ArrowDropUpIcon from "@mui/icons-material/ArrowDropUp";
10
17
  import Share from "@mui/icons-material/Share";
11
18
  import PlayCircleFilledIcon from "@mui/icons-material/PlayCircleFilled";
12
- import PhrasesEditor from "../Phrases/Phrases";
19
+ import PhrasesEditor, { Modes } from "../Phrases/Phrases";
13
20
  import UpdatePhraseList from "../../Forms/UpdatePhraseList/UpdatePhraseList";
14
21
  import { randomId } from "../../utils/index";
15
22
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
@@ -27,18 +34,6 @@ const useStyles = makeStyles()(theme => ({
27
34
  button: {
28
35
  margin: theme.spacing(1)
29
36
  },
30
- pane: {
31
- height: "calc(100vh - 104px)",
32
- [theme.breakpoints.up("sm")]: {
33
- height: "calc(100vh - 112px)"
34
- }
35
- },
36
- paneFull: {
37
- height: "calc(100%)",
38
- [theme.breakpoints.up("sm")]: {
39
- height: "calc(100vh - 48px)"
40
- }
41
- },
42
37
  settings: {
43
38
  backgroundColor: theme.palette.background.default,
44
39
  overflow: "scroll"
@@ -95,7 +90,11 @@ function PhraseList({
95
90
  onSubmit,
96
91
  handleChange,
97
92
  handleUpdatePhrases,
98
- handlePlayPhraseList
93
+ handlePlayPhraseList,
94
+ otherPhraseLists,
95
+ otherPhraseListsLoading,
96
+ handleTransferPhrase,
97
+ handleCreatePhraseListFromSelection
99
98
  }) {
100
99
  const theme = useTheme();
101
100
  const {
@@ -105,9 +104,19 @@ function PhraseList({
105
104
  const isSmallScreen = useMediaQuery(theme.breakpoints.down("md"));
106
105
  const tabVariant = isSmallScreen ? "fullWidth" : null;
107
106
  const [tabValue, setTabValue] = useState(0);
108
- const handleChangeTab = (event, newValue) => {
107
+ const [isGeneratePhrasesDialogOpen, setIsGeneratePhrasesDialogOpen] = useState(false);
108
+ const [mode, setMode] = useState(Modes.NORMAL);
109
+ const handleCloseWithReset = () => {
110
+ setIsGeneratePhrasesDialogOpen(false);
111
+ handleClose();
112
+ };
113
+ const scriptDownloadRef = useRef(null);
114
+ const handleChangeTab = (_, newValue) => {
109
115
  setTabValue(newValue);
110
116
  };
117
+ const handleModeSelection = (_, newMode) => {
118
+ if (newMode) setMode(newMode);
119
+ };
111
120
  const updatePhrases = async phrases => {
112
121
  try {
113
122
  handleUpdatePhrases(courseId, sectionId, topicId, phrases);
@@ -124,12 +133,16 @@ function PhraseList({
124
133
  children: /*#__PURE__*/_jsxs(Dialog, {
125
134
  fullScreen: true,
126
135
  open: open,
127
- onClose: handleClose,
128
- TransitionComponent: Transition,
136
+ onClose: handleCloseWithReset,
137
+ slots: {
138
+ transition: Transition
139
+ },
129
140
  "data-cy": "bot-editor-dialog",
130
141
  sx: theme => ({
131
142
  "& .MuiDialog-paper": {
132
- backgroundColor: theme.palette.mode === "light" ? theme.palette.grey[200] : null
143
+ backgroundColor: theme.palette.mode === "light" ? theme.palette.grey[200] : null,
144
+ display: "flex",
145
+ flexDirection: "column"
133
146
  }
134
147
  }),
135
148
  children: [/*#__PURE__*/_jsxs(AppBar, {
@@ -140,7 +153,7 @@ function PhraseList({
140
153
  "data-cy": "close-dialog",
141
154
  edge: "start",
142
155
  color: "inherit",
143
- onClick: handleClose,
156
+ onClick: handleCloseWithReset,
144
157
  "aria-label": "Close",
145
158
  size: "large",
146
159
  children: /*#__PURE__*/_jsx(CloseIcon, {})
@@ -213,9 +226,91 @@ function PhraseList({
213
226
  })]
214
227
  })
215
228
  })]
229
+ }), isCreator && tabValue === 0 && /*#__PURE__*/_jsxs(Paper, {
230
+ elevation: 0,
231
+ sx: {
232
+ flexShrink: 0,
233
+ px: 2,
234
+ py: 1.5,
235
+ display: "flex",
236
+ alignItems: "center",
237
+ justifyContent: "space-between",
238
+ flexWrap: "wrap",
239
+ gap: 1,
240
+ borderRadius: 0,
241
+ borderBottom: 1,
242
+ borderColor: "divider"
243
+ },
244
+ children: [/*#__PURE__*/_jsx(Typography, {
245
+ variant: "subtitle1",
246
+ sx: {
247
+ fontWeight: "bold"
248
+ },
249
+ children: t("phrases")
250
+ }), /*#__PURE__*/_jsxs(Box, {
251
+ sx: {
252
+ display: "flex",
253
+ alignItems: "center",
254
+ gap: 1,
255
+ flexWrap: "wrap"
256
+ },
257
+ children: [subscription?.isPaidUser && /*#__PURE__*/_jsx(Button, {
258
+ color: "secondary",
259
+ variant: "contained",
260
+ size: "small",
261
+ startIcon: /*#__PURE__*/_jsx(AutoFixHighIcon, {}),
262
+ onClick: () => setIsGeneratePhrasesDialogOpen(true),
263
+ children: t("generate_phrases")
264
+ }), /*#__PURE__*/_jsx(Button, {
265
+ color: "primary",
266
+ variant: "contained",
267
+ size: "small",
268
+ startIcon: /*#__PURE__*/_jsx(AddIcon, {}),
269
+ onClick: () => setIsCreatePhraseDialogOpen(true),
270
+ children: t("add_phrase")
271
+ }), /*#__PURE__*/_jsx(DefaultButton, {
272
+ variant: "contained",
273
+ size: "small",
274
+ onClick: () => setIsUploadPhrasesDialogOpen(true),
275
+ endIcon: /*#__PURE__*/_jsx(PublishIcon, {}),
276
+ children: t("upload")
277
+ }), /*#__PURE__*/_jsx(DefaultButton, {
278
+ variant: "contained",
279
+ size: "small",
280
+ onClick: () => scriptDownloadRef.current?.(),
281
+ endIcon: /*#__PURE__*/_jsx(GetAppIcon, {}),
282
+ children: t("download")
283
+ }), /*#__PURE__*/_jsxs(ToggleButtonGroup, {
284
+ size: "small",
285
+ value: mode,
286
+ exclusive: true,
287
+ onChange: handleModeSelection,
288
+ "aria-label": t("editor_mode"),
289
+ children: [/*#__PURE__*/_jsx(ToggleButton, {
290
+ value: Modes.NORMAL,
291
+ "aria-label": t("normal_mode"),
292
+ children: /*#__PURE__*/_jsx(Tooltip, {
293
+ title: t("normal_mode"),
294
+ children: /*#__PURE__*/_jsx(ListIcon, {
295
+ fontSize: "small"
296
+ })
297
+ })
298
+ }), /*#__PURE__*/_jsx(ToggleButton, {
299
+ value: Modes.SPREADSHEET,
300
+ "data-cy": "spreadsheet-toggle",
301
+ "aria-label": t("spreadsheet_mode"),
302
+ children: /*#__PURE__*/_jsx(Tooltip, {
303
+ title: t("spreadsheet_mode"),
304
+ children: /*#__PURE__*/_jsx(TableChartIcon, {
305
+ fontSize: "small"
306
+ })
307
+ })
308
+ })]
309
+ })]
310
+ })]
216
311
  }), tabValue === 0 ? /*#__PURE__*/_jsx(Box, {
217
- className: hidden ? classes.paneFull : classes.pane,
218
312
  sx: {
313
+ flex: 1,
219
314
  overflowY: "auto"
220
315
  },
221
316
  children: /*#__PURE__*/_jsx(PhrasesEditor, {
@@ -250,13 +345,22 @@ function PhraseList({
250
345
  subscription: subscription,
251
346
  topicGoal: topicGoal,
252
347
  verificationStatus: verificationStatus,
253
- phraseListName: initialValues.phraseListName
348
+ phraseListName: initialValues.phraseListName,
349
+ otherPhraseLists: otherPhraseLists,
350
+ otherPhraseListsLoading: otherPhraseListsLoading,
351
+ handleTransferPhrase: handleTransferPhrase,
352
+ handleCreatePhraseListFromSelection: handleCreatePhraseListFromSelection,
353
+ isGeneratePhrasesDialogOpen: isGeneratePhrasesDialogOpen,
354
+ setIsGeneratePhrasesDialogOpen: setIsGeneratePhrasesDialogOpen,
355
+ mode: mode,
356
+ scriptDownloadRef: scriptDownloadRef
254
357
  })
255
358
  }) : null, tabValue === 1 ? /*#__PURE__*/_jsx(Box, {
256
- className: hidden ? classes.paneFull : classes.pane,
257
359
  sx: {
360
+ flex: 1,
258
361
  overflowY: "auto"
259
362
  },
363
+ className: classes.settings,
260
364
  children: /*#__PURE__*/_jsx(UpdatePhraseList, {
261
365
  t: t,
262
366
  touched: touched,