@nualang/nualang-ui-components 0.1.1251 → 0.1.1253

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.
@@ -224,7 +224,7 @@ function AssignmentCard({
224
224
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
225
225
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_Typography.default, {
226
226
  variant: "h4",
227
- children: assignment.assignedStudents.length
227
+ children: assignment.assignedStudents?.length
228
228
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Typography.default, {
229
229
  variant: "body2",
230
230
  color: "text.secondary",
@@ -51,7 +51,8 @@ function OverflowMenu({
51
51
  handleDuplicateCourse,
52
52
  course,
53
53
  classroomId,
54
- isClassroomArchived = false
54
+ isClassroomArchived = false,
55
+ canDuplicateCourse
55
56
  }) {
56
57
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Menu, {
57
58
  id: `card-menu-${courseId}`,
@@ -86,19 +87,26 @@ function OverflowMenu({
86
87
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, {
87
88
  children: t("leave_course")
88
89
  })]
89
- }), isCreator && !isClassroomArchived && /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
90
- children: handleDuplicateCourse && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.MenuItem, {
91
- onClick: async () => {
92
- handleClose();
93
- await handleDuplicateCourse(course);
94
- },
95
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_CardElements.CardMenuIcon, {
96
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_FileCopy.default, {
97
- fontSize: "small"
90
+ }), !isClassroomArchived && /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
91
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Tooltip, {
92
+ title: !canDuplicateCourse ? t("duplicate_disabled_tooltip") : "",
93
+ disableHoverListener: canDuplicateCourse,
94
+ arrow: true,
95
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)("span", {
96
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.MenuItem, {
97
+ onClick: async () => {
98
+ await handleDuplicateCourse(course, classroomId);
99
+ },
100
+ disabled: !canDuplicateCourse || course.duplicated,
101
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_CardElements.CardMenuIcon, {
102
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_FileCopy.default, {
103
+ fontSize: "small"
104
+ })
105
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, {
106
+ children: t("duplicate_course")
107
+ })]
98
108
  })
99
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, {
100
- children: t("duplicate_course")
101
- })]
109
+ })
102
110
  })
103
111
  }), handleRemoveCourse && !isClassroomArchived && /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.MenuItem, {
104
112
  onClick: () => handleRemoveCourse(courseId),
@@ -209,7 +217,8 @@ function CourseCard({
209
217
  ariaText = "",
210
218
  classroomId,
211
219
  cardTitleComponent = "h2",
212
- isClassroomArchived = false
220
+ isClassroomArchived = false,
221
+ email
213
222
  }) {
214
223
  const [placeholderRef, visible] = (0, _reactIntersectionObserver.useInView)({
215
224
  rootMargin: "320px",
@@ -314,6 +323,8 @@ function CourseCard({
314
323
  // const memberCount = membersQuery.isSuccess ? membersQuery.data.Items.length : 0;
315
324
 
316
325
  const isPlaysVisible = !disablePlays && exerciseStartTotal > -1;
326
+ const isCollaborator = Array.isArray(course.collaborators) && course.collaborators.some(collab => (typeof collab === 'string' ? collab : collab?.S) === email);
327
+ const canDuplicateCourse = isCreator || course.isShareable === 'everyone' || course.isShareable === 'collaborators' && isCollaborator;
317
328
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_CardElements.CardContainer, {
318
329
  sx: disableRaised ? {} : _CardElements.classes.hover,
319
330
  raised: disableRaised ? false : anchorEl || isOutlineOpen,
@@ -526,7 +537,8 @@ function CourseCard({
526
537
  handleRemoveCourse: handleRemoveCourse,
527
538
  handleDuplicateCourse: handleDuplicateCourse,
528
539
  course: course,
529
- isClassroomArchived: isClassroomArchived
540
+ isClassroomArchived: isClassroomArchived,
541
+ canDuplicateCourse: canDuplicateCourse
530
542
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_Members.default, {
531
543
  getMemberDetails: getMemberDetails,
532
544
  t: t,
@@ -4,25 +4,62 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.default = void 0;
7
+ var _react = require("react");
7
8
  var _CustomChipInput = _interopRequireDefault(require("../../Misc/CustomChipInput/CustomChipInput"));
8
9
  var _jsxRuntime = require("react/jsx-runtime");
9
10
  function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
11
+ // already present, just ensure it's used
12
+
10
13
  function ChipInput(props) {
11
14
  const {
15
+ t,
12
16
  name = "",
13
17
  value = [],
14
18
  handleChange = () => {},
15
19
  label,
16
20
  delimiter = /[\s,]+/
17
21
  } = props;
22
+ const [errorMessage, setErrorMessage] = (0, _react.useState)("");
23
+ const genericDomains = ['gmail.com', 'yahoo.com', 'hotmail.com', 'outlook.com', 'icloud.com'];
18
24
  const handleAddChip = chip => {
19
25
  const newChips = chip.split(delimiter).map(c => c.trim().toLowerCase()).filter(Boolean);
20
- handleChange({
21
- target: {
22
- name: name,
23
- value: [...value, ...newChips]
24
- }
25
- });
26
+ if (props.restrictCollaboratorDomains) {
27
+ const allowedDomain = props.userEmail?.split('@')[1];
28
+ const validChips = newChips.filter(c => {
29
+ const emailPattern = /^[^@]+@[^@]+\.[^@]+$/;
30
+ if (!emailPattern.test(c)) {
31
+ setErrorMessage(t("invalid_email_format"));
32
+ return false;
33
+ }
34
+ const domain = c.split('@')[1];
35
+ if (genericDomains.includes(domain)) {
36
+ setErrorMessage(t("generic_email_domain_error"));
37
+ return false;
38
+ }
39
+ if (domain !== allowedDomain) {
40
+ setErrorMessage(t("email_domain_mismatch_error", {
41
+ domain: allowedDomain
42
+ }));
43
+ return false;
44
+ }
45
+ return true;
46
+ });
47
+ if (validChips.length === 0) return;
48
+ setErrorMessage("");
49
+ handleChange({
50
+ target: {
51
+ name: name,
52
+ value: [...value, ...validChips]
53
+ }
54
+ });
55
+ } else {
56
+ handleChange({
57
+ target: {
58
+ name: name,
59
+ value: [...value, ...newChips]
60
+ }
61
+ });
62
+ }
26
63
  };
27
64
  const deleteChip = chip => {
28
65
  handleChange({
@@ -37,7 +74,9 @@ function ChipInput(props) {
37
74
  ...props,
38
75
  onAdd: handleAddChip,
39
76
  onDelete: deleteChip,
40
- delimiter: delimiter
77
+ delimiter: delimiter,
78
+ helperText: errorMessage || props.helperText,
79
+ error: !!errorMessage || props.error
41
80
  });
42
81
  }
43
82
  var _default = exports.default = ChipInput;
@@ -49,7 +49,9 @@ function CreateCourse({
49
49
  tags: [],
50
50
  collaborators: [],
51
51
  picture: _index.default.course,
52
- userImage: userImage
52
+ userImage: userImage,
53
+ isShareable: "collaborators",
54
+ duplicated: false
53
55
  },
54
56
  getSteps: getSteps,
55
57
  onSubmit: onSubmit,
@@ -20,14 +20,20 @@ function CourseSettings({
20
20
  errors,
21
21
  handleChange,
22
22
  handleBlur,
23
- subscription
23
+ subscription,
24
+ attributes
24
25
  }) {
25
26
  const {
26
27
  visibility,
27
28
  enrolmentKey,
28
29
  allowedDomains,
29
- collaborators
30
+ collaborators,
31
+ isShareable,
32
+ duplicated
30
33
  } = values;
34
+ const {
35
+ email
36
+ } = attributes || {};
31
37
  const generateRandomEnrolmentKey = (0, _react.useCallback)(() => {
32
38
  return String(Math.floor(Math.random() * 90000) + 10000);
33
39
  }, []);
@@ -85,11 +91,44 @@ function CourseSettings({
85
91
  }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormControlLabel, {
86
92
  value: "public",
87
93
  control: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Radio, {}),
88
- label: t("public")
94
+ label: t("public"),
95
+ disabled: duplicated
89
96
  }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormControlLabel, {
90
97
  value: "private",
91
98
  control: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Radio, {}),
92
- label: t("private")
99
+ label: t("private"),
100
+ disabled: duplicated
101
+ })]
102
+ })]
103
+ })
104
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Grid, {
105
+ size: {
106
+ xs: 12,
107
+ md: 12
108
+ },
109
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.FormControl, {
110
+ component: "fieldset",
111
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormLabel, {
112
+ component: "legend",
113
+ children: t("duplication")
114
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.RadioGroup, {
115
+ "aria-label": "duplication",
116
+ name: "isShareable",
117
+ value: isShareable,
118
+ onChange: handleChange,
119
+ onBlur: handleBlur,
120
+ helperText: touched.isShareable ? errors.isShareable : "",
121
+ error: touched.isShareable && Boolean(errors.isShareable),
122
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormControlLabel, {
123
+ value: "everyone",
124
+ control: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Radio, {}),
125
+ label: t("everyone"),
126
+ disabled: duplicated
127
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.FormControlLabel, {
128
+ value: "collaborators",
129
+ control: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Radio, {}),
130
+ label: t("collaborators"),
131
+ disabled: duplicated
93
132
  })]
94
133
  })]
95
134
  })
@@ -176,7 +215,9 @@ function CourseSettings({
176
215
  margin: "normal",
177
216
  variant: "outlined",
178
217
  multiline: true,
179
- fullWidth: true
218
+ fullWidth: true,
219
+ restrictCollaboratorDomains: duplicated,
220
+ userEmail: email
180
221
  }), !subscription.isPaidUser && /*#__PURE__*/(0, _jsxRuntime.jsx)(_Tooltip.default, {
181
222
  title: subscription.isUpgradePossible ? t("upgrade_plan_explanation") : t("upgrade_subscription_student"),
182
223
  placement: "top",
@@ -195,7 +236,9 @@ function CourseSettings({
195
236
  margin: "normal",
196
237
  variant: "outlined",
197
238
  multiline: true,
198
- fullWidth: true
239
+ fullWidth: true,
240
+ restrictCollaboratorDomains: duplicated,
241
+ userEmail: email
199
242
  })
200
243
  })]
201
244
  })
@@ -212,6 +255,8 @@ CourseSettings.validationSchema = Yup.object().shape({
212
255
  otherwise: () => Yup.string().nullable()
213
256
  }),
214
257
  allowedDomains: Yup.array(Yup.string().test("noSpecialChars", "no_special_characters", value => !(0, _index.containsInvalidSymbols)(value)).matches(_index.domainPattern, "Must be a valid domain e.g 'schooldomain.com'")).nullable(),
215
- collaborators: Yup.array(Yup.string().test("noSpecialChars", "no_special_characters", value => !(0, _index.containsInvalidSymbols)(value))).nullable()
258
+ collaborators: Yup.array(Yup.string().test("noSpecialChars", "no_special_characters", value => !(0, _index.containsInvalidSymbols)(value))).nullable(),
259
+ isShareable: Yup.string().required("required"),
260
+ duplicated: Yup.bool()
216
261
  });
217
262
  var _default = exports.default = CourseSettings;
@@ -31,7 +31,9 @@ function UpdateCourse({
31
31
  allowedDomains: [],
32
32
  tags: [],
33
33
  collaborators: [],
34
- picture: null
34
+ picture: null,
35
+ isShareable: "",
36
+ duplicated: false
35
37
  },
36
38
  difficulties = ["A1", "A2", "B1", "B2", "C1", "C2"],
37
39
  onSubmit,
@@ -40,7 +42,8 @@ function UpdateCourse({
40
42
  forLanguages,
41
43
  fileSizeLimit,
42
44
  subscription,
43
- verificationStatus
45
+ verificationStatus,
46
+ attributes
44
47
  }) {
45
48
  const difficultyOptions = difficulties.map((d, idx) => {
46
49
  const keyId = `crearolax${(0, _index.randomId)()}${idx}`;
@@ -64,7 +67,9 @@ function UpdateCourse({
64
67
  picture,
65
68
  banner,
66
69
  tags,
67
- collaborators
70
+ collaborators,
71
+ isShareable,
72
+ duplicated
68
73
  } = initialValues;
69
74
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
70
75
  children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_formik.Formik, {
@@ -137,7 +142,9 @@ function UpdateCourse({
137
142
  visibility,
138
143
  enrolmentKey,
139
144
  allowedDomains,
140
- collaborators
145
+ collaborators,
146
+ isShareable,
147
+ duplicated
141
148
  },
142
149
  enableReinitialize: enableReinitialize,
143
150
  validationSchema: _Steps.CourseSettings.validationSchema,
@@ -171,6 +178,7 @@ function UpdateCourse({
171
178
  className: classes.formContent,
172
179
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Steps.CourseSettings, {
173
180
  t: t,
181
+ attributes: attributes,
174
182
  subscription: subscription,
175
183
  handleChange: handleChange,
176
184
  handleSubmit: handleSubmit,
@@ -32,6 +32,7 @@ function CourseList({
32
32
  disableActions,
33
33
  classroomId,
34
34
  isClassroomArchived = false,
35
+ email,
35
36
  ...otherProps
36
37
  }) {
37
38
  const [items, ref] = (0, _useListScroll.default)({
@@ -96,6 +97,7 @@ function CourseList({
96
97
  handleDuplicateCourse: handleDuplicateCourse,
97
98
  classroomId: classroomId,
98
99
  isClassroomArchived: isClassroomArchived,
100
+ email: email,
99
101
  ...otherProps
100
102
  }, course.courseId)
101
103
  }, course.courseId);
@@ -324,6 +324,111 @@ function OverflowMenu({
324
324
  }, "UpgradeSubscription")]
325
325
  });
326
326
  }
327
+ function ClassroomLoading({
328
+ t = text => text
329
+ }) {
330
+ const {
331
+ classes
332
+ } = useStyles();
333
+ return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
334
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
335
+ className: classes.cardTop,
336
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Skeleton.default, {
337
+ variant: "rectangular",
338
+ sx: theme => ({
339
+ [theme.breakpoints.up("md")]: {
340
+ paddingTop: "15%"
341
+ },
342
+ height: 0,
343
+ paddingTop: "25%",
344
+ borderRadius: theme.spacing(2),
345
+ marginBottom: theme.spacing(4)
346
+ })
347
+ })
348
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
349
+ className: classes.header,
350
+ children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
351
+ className: classes.headerText,
352
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, {
353
+ variant: "h5",
354
+ component: "h1",
355
+ gutterBottom: true,
356
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Skeleton.default, {
357
+ variant: "rectangular",
358
+ height: 32,
359
+ width: 150
360
+ })
361
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, {
362
+ variant: "body2",
363
+ color: "textSecondary",
364
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Skeleton.default, {
365
+ variant: "rectangular",
366
+ height: 20,
367
+ width: 250
368
+ })
369
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
370
+ display: "flex",
371
+ alignItems: "center",
372
+ mt: 2,
373
+ mb: 1,
374
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Box, {
375
+ mr: 1,
376
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, {
377
+ variant: "contained",
378
+ color: "primary",
379
+ startIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_PersonAdd.default, {}),
380
+ size: "small",
381
+ disabled: true,
382
+ children: t("join")
383
+ })
384
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_DefaultColourButton.default, {
385
+ startIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MoreVert.default, {}),
386
+ size: "small",
387
+ className: classes.moreOptions,
388
+ disabled: true,
389
+ children: t("more_options")
390
+ })]
391
+ })]
392
+ })
393
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_ResponsiveTabs.default, {
394
+ t: t,
395
+ tabs: [{
396
+ label: t("courses"),
397
+ id: "Courses"
398
+ }, {
399
+ label: t("assignments"),
400
+ id: "Assignments"
401
+ }, {
402
+ label: t("progress"),
403
+ id: "Discuss"
404
+ }, {
405
+ label: t("members"),
406
+ id: "Members"
407
+ }, {
408
+ label: t("settings"),
409
+ id: "Settings",
410
+ disabled: true
411
+ }],
412
+ centered: true,
413
+ children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Box, {
414
+ py: 1,
415
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Courses.default, {
416
+ t: t,
417
+ courses: null
418
+ })
419
+ }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
420
+ py: 1,
421
+ children: [t("loading"), "..."]
422
+ }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Box, {
423
+ py: 1,
424
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Members.default, {
425
+ t: t,
426
+ members: null
427
+ })
428
+ })]
429
+ })]
430
+ });
431
+ }
327
432
  function Classroom({
328
433
  t = text => text,
329
434
  classroom,
@@ -395,6 +500,7 @@ function Classroom({
395
500
  preferred_username,
396
501
  isUserInternal,
397
502
  assignedCourses,
503
+ email,
398
504
  ...otherProps
399
505
  }) {
400
506
  const {
@@ -658,99 +764,6 @@ function Classroom({
658
764
  setIsCreatorNotSubscribedUpgradeOpen(false);
659
765
  };
660
766
  const [avatarOptionClicked, setAvatarOptionClicked] = (0, _react.useState)(false);
661
- if (!classroom) {
662
- return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
663
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
664
- className: classes.cardTop,
665
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Skeleton.default, {
666
- variant: "rectangular",
667
- sx: theme => ({
668
- [theme.breakpoints.up("md")]: {
669
- paddingTop: "15%"
670
- },
671
- height: 0,
672
- paddingTop: "25%",
673
- borderRadius: theme.spacing(2),
674
- marginBottom: theme.spacing(4)
675
- })
676
- })
677
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
678
- className: classes.header,
679
- children: /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
680
- className: classes.headerText,
681
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, {
682
- variant: "h5",
683
- component: "h1",
684
- gutterBottom: true,
685
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Skeleton.default, {
686
- variant: "rectangular",
687
- height: 32,
688
- width: 150
689
- })
690
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Typography, {
691
- variant: "body2",
692
- color: "textSecondary",
693
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Skeleton.default, {
694
- variant: "rectangular",
695
- height: 20,
696
- width: 250
697
- })
698
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
699
- display: "flex",
700
- alignItems: "center",
701
- mt: 2,
702
- mb: 1,
703
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Box, {
704
- mr: 1,
705
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Button, {
706
- variant: "contained",
707
- color: "primary",
708
- startIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_PersonAdd.default, {}),
709
- size: "small",
710
- disabled: true,
711
- children: t("join")
712
- })
713
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_DefaultColourButton.default, {
714
- startIcon: /*#__PURE__*/(0, _jsxRuntime.jsx)(_MoreVert.default, {}),
715
- size: "small",
716
- className: classes.moreOptions,
717
- disabled: true,
718
- children: t("more_options")
719
- })]
720
- })]
721
- })
722
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_ResponsiveTabs.default, {
723
- t: t,
724
- tabs: [{
725
- label: t("courses")
726
- }, {
727
- label: t("progress")
728
- }, {
729
- label: t("members")
730
- }, {
731
- label: t("settings"),
732
- disabled: true
733
- }],
734
- centered: true,
735
- children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Box, {
736
- py: 1,
737
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Courses.default, {
738
- t: t,
739
- courses: null
740
- })
741
- }), /*#__PURE__*/(0, _jsxRuntime.jsxs)(_material.Box, {
742
- py: 1,
743
- children: [t("loading"), "..."]
744
- }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Box, {
745
- py: 1,
746
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Members.default, {
747
- t: t,
748
- members: null
749
- })
750
- })]
751
- })]
752
- });
753
- }
754
767
  const {
755
768
  classroomId,
756
769
  classroomName,
@@ -794,6 +807,12 @@ function Classroom({
794
807
  handleRemoveCourse(course);
795
808
  }
796
809
  };
810
+ const duplicateCourse = async course => {
811
+ const confirmed = await confirm(t("duplicate_course"), t("duplicate_course_confirmation"));
812
+ if (confirmed) {
813
+ handleDuplicateCourse(course);
814
+ }
815
+ };
797
816
  const deleteClassroom = async id => {
798
817
  const confirmed = await confirm(t("delete_classroom"), t("delete_classroom_confirmation"), "", true, t("classroom_deletion_note", {
799
818
  courseCount: courses ? courses.length : 0
@@ -858,9 +877,10 @@ function Classroom({
858
877
  memberId: memberId,
859
878
  username: username,
860
879
  disableActions: !(isMember || isCreator),
861
- handleDuplicateCourse: handleDuplicateCourse,
880
+ handleDuplicateCourse: duplicateCourse,
862
881
  classroomId: classroomId,
863
- isClassroomArchived: isArchived
882
+ isClassroomArchived: isArchived,
883
+ email: email
864
884
  }) : /*#__PURE__*/(0, _jsxRuntime.jsx)("div", {
865
885
  id: "add-courses-fab",
866
886
  children: isCreator ? /*#__PURE__*/(0, _jsxRuntime.jsx)(_ClassCourses.default, {
@@ -1159,19 +1179,20 @@ function Classroom({
1159
1179
  tabIndex: tabs.findIndex(tab => tab.id === "Courses"),
1160
1180
  variant: "circular"
1161
1181
  }, vchatFab, assignmentsFab]];
1162
- let initialTabValue;
1163
- if (window.location.hash) {
1164
- const selectedTab = window.location.hash;
1165
- initialTabValue = tabs.findIndex(tab => tab.id === selectedTab.replace("#", ""));
1166
- if (initialTabValue === 3 || initialTabValue === 4 && !isCreator) {
1167
- initialTabValue = 0;
1168
- }
1169
- if (initialTabValue < 0) {
1170
- initialTabValue = 0;
1182
+ const initialTabValue = (0, _react.useMemo)(() => {
1183
+ if (window.location.hash) {
1184
+ const selectedTabId = window.location.hash.slice(1);
1185
+ const index = tabs.findIndex(tab => tab.id === selectedTabId);
1186
+ const tab = tabs[index];
1187
+ const isDisabled = tab?.disabled;
1188
+ const isRestricted = (tab?.id === "Settings" || tab?.id === "Members") && !isCreator;
1189
+ if (index >= 0 && !isDisabled && !isRestricted) {
1190
+ return index;
1191
+ }
1171
1192
  }
1172
- } else {
1173
- initialTabValue = 0;
1174
- }
1193
+ return 0;
1194
+ }, [isCreator.tabs?.length]); // only recompute if isCreator or tabs change
1195
+
1175
1196
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, {
1176
1197
  children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
1177
1198
  className: classes.cardTop,
@@ -1535,7 +1556,7 @@ function ViewClassroom({
1535
1556
  return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", {
1536
1557
  className: classes.root,
1537
1558
  children: [isLoading && !classroom && /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
1538
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(Classroom, {
1559
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(ClassroomLoading, {
1539
1560
  t: t
1540
1561
  })
1541
1562
  }), !isLoading && classroom && Object.keys(classroom).length && /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, {
@@ -63,7 +63,8 @@ function Courses({
63
63
  getCourseMembers,
64
64
  username,
65
65
  memberId,
66
- handleDuplicateCourse
66
+ handleDuplicateCourse,
67
+ email
67
68
  }) {
68
69
  const {
69
70
  classes
@@ -136,7 +137,8 @@ function Courses({
136
137
  memberId: memberId,
137
138
  username: username,
138
139
  handleDuplicateCourse: handleDuplicateCourse,
139
- cardTitleComponent: "h2"
140
+ cardTitleComponent: "h2",
141
+ email: email
140
142
  }) : /*#__PURE__*/(0, _jsxRuntime.jsx)(_material.Box, {
141
143
  py: 2,
142
144
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Courses2.default, {
@@ -701,7 +701,8 @@ function Course({
701
701
  onSubmit: handleUpdateCourse,
702
702
  fileSizeLimit: fileSizeLimit,
703
703
  subscription: subscription,
704
- verificationStatus: verificationStatus
704
+ verificationStatus: verificationStatus,
705
+ attributes: attributes
705
706
  })
706
707
  }, `tab-content-settings`)
707
708
  }] : [])];
@@ -238,11 +238,16 @@ function Progress({
238
238
  (0, _react.useEffect)(() => {
239
239
  if ("URLSearchParams" in window) {
240
240
  const searchParams = (0, _utils2.searchStringToObj)(window.location.search);
241
- const courseId = searchParams.courseId;
242
- const assignmentId = searchParams.assignmentId;
243
- if (searchParams.reportType && !reportType && reportTypes.includes(searchParams.reportType) && reportType !== searchParams.reportType) {
241
+ if (searchParams.reportType && searchParams.reportType !== reportTypes[0] && reportTypes.includes(searchParams.reportType) && reportType !== searchParams.reportType) {
244
242
  setReportType(searchParams.reportType);
245
243
  }
244
+ }
245
+ }, []);
246
+ (0, _react.useEffect)(() => {
247
+ if ("URLSearchParams" in window) {
248
+ const searchParams = (0, _utils2.searchStringToObj)(window.location.search);
249
+ const courseId = searchParams.courseId;
250
+ const assignmentId = searchParams.assignmentId;
246
251
  if (assignmentId && !selectedAssignment && assignments && assignments.length) {
247
252
  const assignmentIndex = assignments.findIndex(a => a.assignmentId === assignmentId);
248
253
  const assignment = assignments[assignmentIndex];
@@ -598,7 +598,7 @@ function TableRow({
598
598
  fetchMemberCourseCompletions,
599
599
  filter,
600
600
  index,
601
- featureFlags,
601
+ featureFlags = {},
602
602
  selectedAssignment,
603
603
  reportType,
604
604
  tableExercises,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nualang/nualang-ui-components",
3
- "version": "0.1.1251",
3
+ "version": "0.1.1253",
4
4
  "main": "dist/index.js",
5
5
  "files": [
6
6
  "dist",