@openedx/frontend-app-instructor-dashboard 1.0.0-alpha.10 → 1.0.0-alpha.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/dist/certificates/CertificatesPage.d.ts +1 -0
  2. package/dist/certificates/CertificatesPage.js +162 -2
  3. package/dist/certificates/CertificatesPage.js.map +1 -1
  4. package/dist/certificates/CertificatesPage.scss +81 -0
  5. package/dist/certificates/components/CertificateTable.d.ts +14 -0
  6. package/dist/certificates/components/CertificateTable.js +85 -0
  7. package/dist/certificates/components/CertificateTable.js.map +1 -0
  8. package/dist/certificates/components/CertificatesPageHeader.d.ts +7 -0
  9. package/dist/certificates/components/CertificatesPageHeader.js +11 -0
  10. package/dist/certificates/components/CertificatesPageHeader.js.map +1 -0
  11. package/dist/certificates/components/CertificatesToolbar.d.ts +10 -0
  12. package/dist/certificates/components/CertificatesToolbar.js +12 -0
  13. package/dist/certificates/components/CertificatesToolbar.js.map +1 -0
  14. package/dist/certificates/components/DisableCertificatesModal.d.ts +9 -0
  15. package/dist/certificates/components/DisableCertificatesModal.js +16 -0
  16. package/dist/certificates/components/DisableCertificatesModal.js.map +1 -0
  17. package/dist/certificates/components/FilterDropdown.d.ts +8 -0
  18. package/dist/certificates/components/FilterDropdown.js +50 -0
  19. package/dist/certificates/components/FilterDropdown.js.map +1 -0
  20. package/dist/certificates/components/GenerationHistoryTable.d.ts +11 -0
  21. package/dist/certificates/components/GenerationHistoryTable.js +40 -0
  22. package/dist/certificates/components/GenerationHistoryTable.js.map +1 -0
  23. package/dist/certificates/components/GrantExceptionsModal.d.ts +8 -0
  24. package/dist/certificates/components/GrantExceptionsModal.js +10 -0
  25. package/dist/certificates/components/GrantExceptionsModal.js.map +1 -0
  26. package/dist/certificates/components/InvalidateCertificateModal.d.ts +8 -0
  27. package/dist/certificates/components/InvalidateCertificateModal.js +10 -0
  28. package/dist/certificates/components/InvalidateCertificateModal.js.map +1 -0
  29. package/dist/certificates/components/IssuedCertificatesTab.d.ts +18 -0
  30. package/dist/certificates/components/IssuedCertificatesTab.js +6 -0
  31. package/dist/certificates/components/IssuedCertificatesTab.js.map +1 -0
  32. package/dist/certificates/components/LearnerActionModal.d.ts +16 -0
  33. package/dist/certificates/components/LearnerActionModal.js +22 -0
  34. package/dist/certificates/components/LearnerActionModal.js.map +1 -0
  35. package/dist/certificates/components/RemoveInvalidationModal.d.ts +9 -0
  36. package/dist/certificates/components/RemoveInvalidationModal.js +10 -0
  37. package/dist/certificates/components/RemoveInvalidationModal.js.map +1 -0
  38. package/dist/certificates/constants.d.ts +15 -0
  39. package/dist/certificates/constants.js +16 -0
  40. package/dist/certificates/constants.js.map +1 -0
  41. package/dist/certificates/data/api.d.ts +9 -0
  42. package/dist/certificates/data/api.js +63 -0
  43. package/dist/certificates/data/api.js.map +1 -0
  44. package/dist/certificates/data/apiHook.d.ts +30 -0
  45. package/dist/certificates/data/apiHook.js +90 -0
  46. package/dist/certificates/data/apiHook.js.map +1 -0
  47. package/dist/certificates/data/dummyData.d.ts +2 -0
  48. package/dist/certificates/data/dummyData.js +234 -0
  49. package/dist/certificates/data/dummyData.js.map +1 -0
  50. package/dist/certificates/data/queryKeys.d.ts +8 -0
  51. package/dist/certificates/data/queryKeys.js +8 -0
  52. package/dist/certificates/data/queryKeys.js.map +1 -0
  53. package/dist/certificates/messages.d.ts +313 -0
  54. package/dist/certificates/messages.js +315 -0
  55. package/dist/certificates/messages.js.map +1 -0
  56. package/dist/certificates/types.d.ts +61 -0
  57. package/dist/certificates/types.js +26 -0
  58. package/dist/certificates/types.js.map +1 -0
  59. package/dist/certificates/utils/errorHandling.d.ts +10 -0
  60. package/dist/certificates/utils/errorHandling.js +3 -0
  61. package/dist/certificates/utils/errorHandling.js.map +1 -0
  62. package/dist/certificates/utils/filterUtils.d.ts +4 -0
  63. package/dist/certificates/utils/filterUtils.js +31 -0
  64. package/dist/certificates/utils/filterUtils.js.map +1 -0
  65. package/dist/certificates/utils/index.d.ts +2 -0
  66. package/dist/certificates/utils/index.js +2 -0
  67. package/dist/certificates/utils/index.js.map +1 -0
  68. package/dist/courseTeam/CourseTeamPage.js +5 -2
  69. package/dist/courseTeam/CourseTeamPage.js.map +1 -1
  70. package/dist/courseTeam/components/AddTeamMemberModal.d.ts +6 -0
  71. package/dist/courseTeam/components/AddTeamMemberModal.js +60 -0
  72. package/dist/courseTeam/components/AddTeamMemberModal.js.map +1 -0
  73. package/dist/courseTeam/data/api.d.ts +2 -1
  74. package/dist/courseTeam/data/api.js +4 -0
  75. package/dist/courseTeam/data/api.js.map +1 -1
  76. package/dist/courseTeam/data/apiHook.d.ts +4 -0
  77. package/dist/courseTeam/data/apiHook.js +11 -2
  78. package/dist/courseTeam/data/apiHook.js.map +1 -1
  79. package/dist/courseTeam/messages.d.ts +60 -0
  80. package/dist/courseTeam/messages.js +61 -1
  81. package/dist/courseTeam/messages.js.map +1 -1
  82. package/dist/courseTeam/types.d.ts +6 -0
  83. package/dist/courseTeam/types.js.map +1 -1
  84. package/dist/enrollments/EnrollmentsPage.js +21 -2
  85. package/dist/enrollments/EnrollmentsPage.js.map +1 -1
  86. package/dist/enrollments/components/AddBetaTestersModal.d.ts +6 -0
  87. package/dist/enrollments/components/AddBetaTestersModal.js +61 -0
  88. package/dist/enrollments/components/AddBetaTestersModal.js.map +1 -0
  89. package/dist/enrollments/components/EnrollLearnersModal.d.ts +1 -2
  90. package/dist/enrollments/components/EnrollLearnersModal.js +15 -6
  91. package/dist/enrollments/components/EnrollLearnersModal.js.map +1 -1
  92. package/dist/enrollments/components/EnrollmentsList.d.ts +2 -1
  93. package/dist/enrollments/components/EnrollmentsList.js +9 -10
  94. package/dist/enrollments/components/EnrollmentsList.js.map +1 -1
  95. package/dist/enrollments/components/UnenrollModal.d.ts +1 -2
  96. package/dist/enrollments/components/UnenrollModal.js +1 -2
  97. package/dist/enrollments/components/UnenrollModal.js.map +1 -1
  98. package/dist/enrollments/components/UpdateBetaTesterModal.d.ts +8 -0
  99. package/dist/enrollments/components/UpdateBetaTesterModal.js +52 -0
  100. package/dist/enrollments/components/UpdateBetaTesterModal.js.map +1 -0
  101. package/dist/enrollments/data/api.d.ts +3 -2
  102. package/dist/enrollments/data/api.js +7 -1
  103. package/dist/enrollments/data/api.js.map +1 -1
  104. package/dist/enrollments/data/apiHook.d.ts +3 -2
  105. package/dist/enrollments/data/apiHook.js +10 -1
  106. package/dist/enrollments/data/apiHook.js.map +1 -1
  107. package/dist/enrollments/messages.d.ts +55 -0
  108. package/dist/enrollments/messages.js +56 -1
  109. package/dist/enrollments/messages.js.map +1 -1
  110. package/dist/enrollments/types.d.ts +18 -0
  111. package/dist/enrollments/types.js.map +1 -1
  112. package/dist/testUtils.js +1 -1
  113. package/dist/testUtils.js.map +1 -1
  114. package/dist/utils/formatters.d.ts +5 -0
  115. package/dist/utils/formatters.js +10 -0
  116. package/dist/utils/formatters.js.map +1 -1
  117. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import { useParams } from 'react-router-dom';
4
4
  import { isAxiosError } from 'axios';
@@ -7,29 +7,38 @@ import { Button, FormControl, ModalDialog, Form } from '@openedx/paragon';
7
7
  import { useUpdateEnrollments } from '../../enrollments/data/apiHook';
8
8
  import messages from '../../enrollments/messages';
9
9
  import { useAlert } from '../../providers/AlertProvider';
10
- const EnrollLearnersModal = ({ isOpen, onClose, onSuccess }) => {
10
+ const EnrollLearnersModal = ({ isOpen, onClose }) => {
11
11
  const intl = useIntl();
12
12
  const { courseId = '' } = useParams();
13
13
  const [emails, setEmails] = useState('');
14
14
  const [autoEnroll, setAutoEnroll] = useState(true);
15
15
  const [emailStudents, setEmailStudents] = useState(true);
16
16
  const { mutate: enrollLearners } = useUpdateEnrollments(courseId);
17
- const { showModal } = useAlert();
17
+ const { showModal, addAlert } = useAlert();
18
18
  const handleSave = () => {
19
19
  const identifier = emails.split(',').map(email => email.trim()).filter(email => email);
20
20
  enrollLearners({ identifier, action: 'enroll', autoEnroll, emailStudents }, {
21
- onSuccess: () => {
21
+ onSuccess: (data) => {
22
+ var _a;
23
+ const failedUsernames = ((_a = data.results) === null || _a === void 0 ? void 0 : _a.filter(user => user.invalidIdentifier).map(user => user.identifier)) || [];
24
+ if (failedUsernames.length > 0) {
25
+ addAlert({
26
+ type: 'danger',
27
+ message: intl.formatMessage(messages.failedEnrollLearners),
28
+ extraContent: (failedUsernames.map((learner) => (_jsxs("p", { className: "mb-0", children: ["\u2022 ", intl.formatMessage(messages.unknownLearner, { learner })] }, learner))))
29
+ });
30
+ }
22
31
  setEmails('');
23
32
  setAutoEnroll(true);
24
33
  setEmailStudents(true);
25
- onSuccess();
34
+ onClose();
26
35
  },
27
36
  onError: (error) => {
28
37
  var _a;
29
38
  const notFound = isAxiosError(error) && ((_a = error.response) === null || _a === void 0 ? void 0 : _a.status) === 404;
30
39
  const errorMessage = notFound
31
40
  ? intl.formatMessage(messages.enrollLearnerNotFoundError)
32
- : error.message || intl.formatMessage(messages.enrollLearnerError);
41
+ : intl.formatMessage(messages.enrollLearnerError);
33
42
  showModal({
34
43
  message: errorMessage,
35
44
  variant: 'danger',
@@ -1 +1 @@
1
- {"version":3,"file":"EnrollLearnersModal.js","sourceRoot":"","sources":["../../../src/enrollments/components/EnrollLearnersModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { isAxiosError } from 'axios';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Button, FormControl, ModalDialog, Form } from '@openedx/paragon';\nimport { useUpdateEnrollments } from '@src/enrollments/data/apiHook';\nimport messages from '@src/enrollments/messages';\nimport { useAlert } from '@src/providers/AlertProvider';\n\nexport interface EnrollLearnersModalProps {\n isOpen: boolean,\n onClose: () => void,\n onSuccess: () => void,\n}\n\nconst EnrollLearnersModal = ({\n isOpen,\n onClose,\n onSuccess\n}: EnrollLearnersModalProps) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const [emails, setEmails] = useState('');\n const [autoEnroll, setAutoEnroll] = useState(true);\n const [emailStudents, setEmailStudents] = useState(true);\n const { mutate: enrollLearners } = useUpdateEnrollments(courseId);\n const { showModal } = useAlert();\n\n const handleSave = () => {\n const identifier = emails.split(',').map(email => email.trim()).filter(email => email);\n enrollLearners({ identifier, action: 'enroll', autoEnroll, emailStudents }, {\n onSuccess: () => {\n setEmails('');\n setAutoEnroll(true);\n setEmailStudents(true);\n onSuccess();\n },\n onError: (error) => {\n const notFound = isAxiosError(error) && error.response?.status === 404;\n const errorMessage = notFound\n ? intl.formatMessage(messages.enrollLearnerNotFoundError)\n : error.message || intl.formatMessage(messages.enrollLearnerError);\n showModal({\n message: errorMessage,\n variant: 'danger',\n confirmText: intl.formatMessage(messages.closeButton),\n });\n }\n });\n };\n\n return (\n <ModalDialog isOpen={isOpen} onClose={onClose} isOverflowVisible={false} title={intl.formatMessage(messages.enrollLearners)}>\n <ModalDialog.Header className=\"border-light-700 border-bottom\">\n <h3 className=\"text-primary-500\">{intl.formatMessage(messages.enrollLearners)}</h3>\n </ModalDialog.Header>\n <div className=\"position-relative overflow-auto\">\n <ModalDialog.Body className=\"py-4\">\n <p className=\"text-gray-700 x-small mb-2\">{intl.formatMessage(messages.addLearnerInstructions)}</p>\n <FormControl\n name=\"identifier\"\n as=\"textarea\"\n rows={4}\n placeholder={intl.formatMessage(messages.userIdentifierPlaceholder)}\n onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setEmails(e.target.value)}\n />\n <div className=\"d-flex mt-3 text-primary-500\">\n <Form.Checkbox\n controlClassName=\"border-primary-500\"\n checked={autoEnroll}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setAutoEnroll(e.target.checked)}\n >{intl.formatMessage(messages.autoEnrollCheckbox)}\n </Form.Checkbox>\n <Form.Checkbox\n controlClassName=\"border-primary-500\"\n className=\"ml-4\"\n checked={emailStudents}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setEmailStudents(e.target.checked)}\n >{intl.formatMessage(messages.notifyUsersCheckbox)}\n </Form.Checkbox>\n </div>\n </ModalDialog.Body>\n </div>\n <ModalDialog.Footer className=\"border-light-700 border-top\">\n <Button variant=\"tertiary\" onClick={onClose}>\n {intl.formatMessage(messages.cancelButton)}\n </Button>\n <Button className=\"ml-2\" variant=\"primary\" onClick={handleSave} disabled={emails.trim().length === 0}>\n {intl.formatMessage(messages.saveButton)}\n </Button>\n </ModalDialog.Footer>\n </ModalDialog>\n );\n};\n\nexport default EnrollLearnersModal;\n"]}
1
+ {"version":3,"file":"EnrollLearnersModal.js","sourceRoot":"","sources":["../../../src/enrollments/components/EnrollLearnersModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { isAxiosError } from 'axios';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Button, FormControl, ModalDialog, Form } from '@openedx/paragon';\nimport { useUpdateEnrollments } from '@src/enrollments/data/apiHook';\nimport messages from '@src/enrollments/messages';\nimport { useAlert } from '@src/providers/AlertProvider';\n\nexport interface EnrollLearnersModalProps {\n isOpen: boolean,\n onClose: () => void,\n}\n\nconst EnrollLearnersModal = ({\n isOpen,\n onClose\n}: EnrollLearnersModalProps) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const [emails, setEmails] = useState('');\n const [autoEnroll, setAutoEnroll] = useState(true);\n const [emailStudents, setEmailStudents] = useState(true);\n const { mutate: enrollLearners } = useUpdateEnrollments(courseId);\n const { showModal, addAlert } = useAlert();\n\n const handleSave = () => {\n const identifier = emails.split(',').map(email => email.trim()).filter(email => email);\n enrollLearners({ identifier, action: 'enroll', autoEnroll, emailStudents }, {\n onSuccess: (data) => {\n const failedUsernames = data.results?.filter(user => user.invalidIdentifier).map(user => user.identifier) || [];\n if (failedUsernames.length > 0) {\n addAlert({\n type: 'danger',\n message: intl.formatMessage(messages.failedEnrollLearners),\n extraContent: (\n failedUsernames.map((learner: string) => (\n <p key={learner} className=\"mb-0\">• {intl.formatMessage(messages.unknownLearner, { learner })}</p>\n ))\n )\n });\n }\n setEmails('');\n setAutoEnroll(true);\n setEmailStudents(true);\n onClose();\n },\n onError: (error) => {\n const notFound = isAxiosError(error) && error.response?.status === 404;\n const errorMessage = notFound\n ? intl.formatMessage(messages.enrollLearnerNotFoundError)\n : intl.formatMessage(messages.enrollLearnerError);\n showModal({\n message: errorMessage,\n variant: 'danger',\n confirmText: intl.formatMessage(messages.closeButton),\n });\n }\n });\n };\n\n return (\n <ModalDialog isOpen={isOpen} onClose={onClose} isOverflowVisible={false} title={intl.formatMessage(messages.enrollLearners)}>\n <ModalDialog.Header className=\"border-light-700 border-bottom\">\n <h3 className=\"text-primary-500\">{intl.formatMessage(messages.enrollLearners)}</h3>\n </ModalDialog.Header>\n <div className=\"position-relative overflow-auto\">\n <ModalDialog.Body className=\"py-4\">\n <p className=\"text-gray-700 x-small mb-2\">{intl.formatMessage(messages.addLearnerInstructions)}</p>\n <FormControl\n name=\"identifier\"\n as=\"textarea\"\n rows={4}\n placeholder={intl.formatMessage(messages.userIdentifierPlaceholder)}\n onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => setEmails(e.target.value)}\n />\n <div className=\"d-flex mt-3 text-primary-500\">\n <Form.Checkbox\n controlClassName=\"border-primary-500\"\n checked={autoEnroll}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setAutoEnroll(e.target.checked)}\n >{intl.formatMessage(messages.autoEnrollCheckbox)}\n </Form.Checkbox>\n <Form.Checkbox\n controlClassName=\"border-primary-500\"\n className=\"ml-4\"\n checked={emailStudents}\n onChange={(e: React.ChangeEvent<HTMLInputElement>) => setEmailStudents(e.target.checked)}\n >{intl.formatMessage(messages.notifyUsersCheckbox)}\n </Form.Checkbox>\n </div>\n </ModalDialog.Body>\n </div>\n <ModalDialog.Footer className=\"border-light-700 border-top\">\n <Button variant=\"tertiary\" onClick={onClose}>\n {intl.formatMessage(messages.cancelButton)}\n </Button>\n <Button className=\"ml-2\" variant=\"primary\" onClick={handleSave} disabled={emails.trim().length === 0}>\n {intl.formatMessage(messages.saveButton)}\n </Button>\n </ModalDialog.Footer>\n </ModalDialog>\n );\n};\n\nexport default EnrollLearnersModal;\n"]}
@@ -1,6 +1,7 @@
1
1
  import { EnrolledLearner } from '../../enrollments/types';
2
2
  interface EnrollmentsListProps {
3
3
  onUnenroll: (learner: EnrolledLearner) => void;
4
+ onBetaTesterChange: (learner: EnrolledLearner) => void;
4
5
  }
5
- declare const EnrollmentsList: ({ onUnenroll }: EnrollmentsListProps) => import("react/jsx-runtime").JSX.Element;
6
+ declare const EnrollmentsList: ({ onUnenroll, onBetaTesterChange }: EnrollmentsListProps) => import("react/jsx-runtime").JSX.Element;
6
7
  export default EnrollmentsList;
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useState } from 'react';
3
3
  import { useParams } from 'react-router-dom';
4
4
  import { useIntl } from '@openedx/frontend-base';
5
- import { ActionRow, Button, DataTable, FormControl, Icon, IconButton } from '@openedx/paragon';
5
+ import { ActionRow, Button, DataTable, FormControl, Icon, IconButton, OverlayTrigger, Popover } from '@openedx/paragon';
6
6
  import { FilterList, MoreVert, Search } from '@openedx/paragon/icons';
7
7
  import messages from '../../enrollments/messages';
8
8
  import { useEnrollments } from '../../enrollments/data/apiHook';
@@ -31,7 +31,7 @@ const BetaTesterFilter = ({ column: { filterValue, setFilter } }) => {
31
31
  };
32
32
  return (_jsx(FormControl, { as: "select", className: "mb-0", name: "isBetaTester", size: "md", value: filterValue, onChange: handleSelectChange, leadingElement: _jsx(Icon, { src: FilterList }), children: betaTesterOptions.map((option) => (_jsx("option", { value: option.value, children: intl.formatMessage(option.label) }, option.value))) }));
33
33
  };
34
- const EnrollmentsList = ({ onUnenroll }) => {
34
+ const EnrollmentsList = ({ onUnenroll, onBetaTesterChange }) => {
35
35
  const intl = useIntl();
36
36
  const { courseId = '' } = useParams();
37
37
  const [filters, setFilters] = useState({ page: 0, username: '', isBetaTester: '' });
@@ -56,10 +56,6 @@ const EnrollmentsList = ({ onUnenroll }) => {
56
56
  setFilters((prevFilters) => (Object.assign(Object.assign({}, prevFilters), { page: data.pageIndex })));
57
57
  }
58
58
  };
59
- const handleMoreButton = () => {
60
- // Handle more button click
61
- console.log('More button clicked');
62
- };
63
59
  const tableColumns = [
64
60
  { accessor: 'username', Header: intl.formatMessage(messages.username), Filter: UsernameFilter },
65
61
  { accessor: 'fullName', Header: intl.formatMessage(messages.fullName), disableFilters: true },
@@ -77,14 +73,17 @@ const EnrollmentsList = ({ onUnenroll }) => {
77
73
  Filter: BetaTesterFilter
78
74
  },
79
75
  ];
80
- const actionCustomCell = useCallback(({ row: { original } }) => {
81
- return (_jsxs(ActionRow, { className: "justify-content-start", children: [_jsx(Button, { className: "pl-0", onClick: () => onUnenroll(original), variant: "link", children: intl.formatMessage(messages.unenrollButton) }), _jsx(IconButton, { alt: intl.formatMessage(messages.changeBetaTesterStatus), className: "lead", iconAs: MoreVert, onClick: handleMoreButton })] }));
82
- }, [onUnenroll, intl]);
76
+ const ActionCustomCell = useCallback(({ row: { original } }) => {
77
+ const popoverContent = (_jsx(Popover, { id: `popover-${original.username}`, className: "border-0 shadow-sm", children: _jsx(Popover.Content, { className: "p-0 border-0", children: _jsx("div", { className: "dropdown-menu show position-static border shadow-sm", children: _jsx("button", { type: "button", className: "dropdown-item", onClick: () => onBetaTesterChange(original), children: original.isBetaTester
78
+ ? intl.formatMessage(messages.revokeBetaTester)
79
+ : intl.formatMessage(messages.grantBetaTester) }) }) }) }));
80
+ return (_jsxs(ActionRow, { className: "justify-content-start", children: [_jsx(Button, { className: "pl-0", onClick: () => onUnenroll(original), variant: "link", children: intl.formatMessage(messages.unenrollButton) }), _jsx(OverlayTrigger, { trigger: "click", placement: "bottom-end", overlay: popoverContent, rootClose: true, children: _jsx(IconButton, { alt: intl.formatMessage(messages.changeBetaTesterStatus), className: "lead", iconAs: MoreVert }) })] }));
81
+ }, [intl, onBetaTesterChange, onUnenroll]);
83
82
  return (_jsxs(DataTable, { className: "mt-3", columns: tableColumns, additionalColumns: [
84
83
  {
85
84
  id: 'actions',
86
85
  Header: intl.formatMessage(messages.actions),
87
- Cell: actionCustomCell,
86
+ Cell: ActionCustomCell,
88
87
  }
89
88
  ], data: data.results, fetchData: handleFetchData, state: {
90
89
  pageIndex: filters.page,
@@ -1 +1 @@
1
- {"version":3,"file":"EnrollmentsList.js","sourceRoot":"","sources":["../../../src/enrollments/components/EnrollmentsList.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { ActionRow, Button, DataTable, FormControl, Icon, IconButton } from '@openedx/paragon';\nimport { FilterList, MoreVert, Search } from '@openedx/paragon/icons';\nimport messages from '@src/enrollments/messages';\nimport { useEnrollments } from '@src/enrollments/data/apiHook';\nimport { EnrolledLearner } from '@src/enrollments/types';\nimport { useDebouncedFilter } from '@src/hooks/useDebouncedFilter';\nimport { DataTableFetchDataProps, TableCellValue } from '@src/types';\n\nconst ENROLLMENTS_PAGE_SIZE = 25;\n\nconst betaTesterOptions = [\n { value: '', label: messages.allEnrollees },\n { value: 'true', label: messages.betaTesters },\n { value: 'false', label: messages.nonBetaTesters },\n];\n\ninterface EnrollmentsListProps {\n onUnenroll: (learner: EnrolledLearner) => void,\n}\n\nconst UsernameFilter = ({ column: { filterValue, setFilter } }: { column: { filterValue: string, setFilter: (value: string) => void } }) => {\n const intl = useIntl();\n const { inputValue, handleChange } = useDebouncedFilter({\n filterValue,\n setFilter,\n });\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n handleChange(e.target.value);\n };\n\n return (\n <FormControl\n className=\"mb-0\"\n onChange={handleInputChange}\n placeholder={intl.formatMessage(messages.searchPlaceholder)}\n trailingElement={<Icon src={Search} />}\n value={inputValue}\n />\n );\n};\n\nconst BetaTesterFilter = ({ column: { filterValue, setFilter } }: { column: { filterValue: string, setFilter: (value: string) => void } }) => {\n const intl = useIntl();\n\n const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {\n setFilter(e.target.value);\n };\n\n return (\n <FormControl\n as=\"select\"\n className=\"mb-0\"\n name=\"isBetaTester\"\n size=\"md\"\n value={filterValue}\n onChange={handleSelectChange}\n leadingElement={<Icon src={FilterList} />}\n >\n {\n betaTesterOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {intl.formatMessage(option.label)}\n </option>\n ))\n }\n </FormControl>\n );\n};\n\nconst EnrollmentsList = ({ onUnenroll }: EnrollmentsListProps) => {\n const intl = useIntl();\n const { courseId = '' } = useParams();\n const [filters, setFilters] = useState({ page: 0, username: '', isBetaTester: '' });\n const { data = { count: 0, results: [], numPages: 0 }, isLoading } = useEnrollments(courseId, {\n page: filters.page,\n pageSize: ENROLLMENTS_PAGE_SIZE,\n emailOrUsername: filters.username,\n isBetaTester: filters.isBetaTester,\n });\n\n const handleFetchData = (data: DataTableFetchDataProps) => {\n const usernameFilter = data.filters?.find((f) => f.id === 'username');\n const newEmailOrUsername = usernameFilter ? usernameFilter.value : '';\n const betaTesterFilter = data.filters?.find((f) => f.id === 'isBetaTester');\n const newIsBetaTester = betaTesterFilter ? betaTesterFilter.value : '';\n const filtersChanged = (newEmailOrUsername !== filters.username) || (newIsBetaTester !== filters.isBetaTester);\n\n if (filtersChanged) {\n setFilters((prevFilters) => ({\n ...prevFilters,\n username: newEmailOrUsername,\n isBetaTester: newIsBetaTester,\n page: 0,\n }));\n return;\n }\n\n if (data.pageIndex !== filters.page) {\n setFilters((prevFilters) => ({ ...prevFilters, page: data.pageIndex }));\n }\n };\n\n const handleMoreButton = () => {\n // Handle more button click\n console.log('More button clicked');\n };\n\n const tableColumns = [\n { accessor: 'username', Header: intl.formatMessage(messages.username), Filter: UsernameFilter },\n { accessor: 'fullName', Header: intl.formatMessage(messages.fullName), disableFilters: true },\n { accessor: 'email', Header: intl.formatMessage(messages.email), disableFilters: true },\n {\n accessor: 'mode',\n Header: intl.formatMessage(messages.track),\n Cell: ({ value }: { value: string }) => (\n <span className=\"text-capitalize\">{value || 'N/A'}</span>\n ),\n disableFilters: true,\n },\n {\n accessor: 'isBetaTester',\n Header: intl.formatMessage(messages.betaTester),\n Cell: ({ value }: { value: string }) => (value ? intl.formatMessage(messages.trueLabel) : ''),\n Filter: BetaTesterFilter\n },\n ];\n\n const actionCustomCell = useCallback(({ row: { original } }: TableCellValue<EnrolledLearner>) => {\n return (\n <ActionRow className=\"justify-content-start\">\n <Button className=\"pl-0\" onClick={() => onUnenroll(original)} variant=\"link\">\n {intl.formatMessage(messages.unenrollButton)}\n </Button>\n <IconButton\n alt={intl.formatMessage(messages.changeBetaTesterStatus)}\n className=\"lead\"\n iconAs={MoreVert}\n onClick={handleMoreButton}\n />\n </ActionRow>\n );\n }, [onUnenroll, intl]);\n\n return (\n <DataTable\n className=\"mt-3\"\n columns={tableColumns}\n additionalColumns={[\n {\n id: 'actions',\n Header: intl.formatMessage(messages.actions),\n Cell: actionCustomCell,\n }\n ]}\n data={data.results}\n fetchData={handleFetchData}\n state={{\n pageIndex: filters.page,\n pageSize: ENROLLMENTS_PAGE_SIZE,\n filters: [\n { id: 'username', value: filters.username },\n { id: 'isBetaTester', value: filters.isBetaTester },\n ]\n }}\n isFilterable\n isLoading={isLoading}\n isPaginated\n itemCount={data.count}\n manualFilters\n manualPagination\n numBreakoutFilters={2}\n pageSize={ENROLLMENTS_PAGE_SIZE}\n pageCount={data.numPages}\n FilterStatusComponent={() => null}\n >\n <DataTable.TableControlBar className=\"px-3 pt-3 pb-2\" />\n <DataTable.Table />\n <DataTable.EmptyTable content={intl.formatMessage(messages.noEnrollments)} />\n <DataTable.TableFooter />\n </DataTable>\n );\n};\n\nexport default EnrollmentsList;\n"]}
1
+ {"version":3,"file":"EnrollmentsList.js","sourceRoot":"","sources":["../../../src/enrollments/components/EnrollmentsList.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useCallback, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { ActionRow, Button, DataTable, FormControl, Icon, IconButton, OverlayTrigger, Popover } from '@openedx/paragon';\nimport { FilterList, MoreVert, Search } from '@openedx/paragon/icons';\nimport messages from '@src/enrollments/messages';\nimport { useEnrollments } from '@src/enrollments/data/apiHook';\nimport { EnrolledLearner } from '@src/enrollments/types';\nimport { useDebouncedFilter } from '@src/hooks/useDebouncedFilter';\nimport { DataTableFetchDataProps, TableCellValue } from '@src/types';\n\nconst ENROLLMENTS_PAGE_SIZE = 25;\n\nconst betaTesterOptions = [\n { value: '', label: messages.allEnrollees },\n { value: 'true', label: messages.betaTesters },\n { value: 'false', label: messages.nonBetaTesters },\n];\n\ninterface EnrollmentsListProps {\n onUnenroll: (learner: EnrolledLearner) => void,\n onBetaTesterChange: (learner: EnrolledLearner) => void,\n}\n\nconst UsernameFilter = ({ column: { filterValue, setFilter } }: { column: { filterValue: string, setFilter: (value: string) => void } }) => {\n const intl = useIntl();\n const { inputValue, handleChange } = useDebouncedFilter({\n filterValue,\n setFilter,\n });\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n handleChange(e.target.value);\n };\n\n return (\n <FormControl\n className=\"mb-0\"\n onChange={handleInputChange}\n placeholder={intl.formatMessage(messages.searchPlaceholder)}\n trailingElement={<Icon src={Search} />}\n value={inputValue}\n />\n );\n};\n\nconst BetaTesterFilter = ({ column: { filterValue, setFilter } }: { column: { filterValue: string, setFilter: (value: string) => void } }) => {\n const intl = useIntl();\n\n const handleSelectChange = (e: React.ChangeEvent<HTMLSelectElement>) => {\n setFilter(e.target.value);\n };\n\n return (\n <FormControl\n as=\"select\"\n className=\"mb-0\"\n name=\"isBetaTester\"\n size=\"md\"\n value={filterValue}\n onChange={handleSelectChange}\n leadingElement={<Icon src={FilterList} />}\n >\n {\n betaTesterOptions.map((option) => (\n <option key={option.value} value={option.value}>\n {intl.formatMessage(option.label)}\n </option>\n ))\n }\n </FormControl>\n );\n};\n\nconst EnrollmentsList = ({ onUnenroll, onBetaTesterChange }: EnrollmentsListProps) => {\n const intl = useIntl();\n const { courseId = '' } = useParams();\n const [filters, setFilters] = useState({ page: 0, username: '', isBetaTester: '' });\n const { data = { count: 0, results: [], numPages: 0 }, isLoading } = useEnrollments(courseId, {\n page: filters.page,\n pageSize: ENROLLMENTS_PAGE_SIZE,\n emailOrUsername: filters.username,\n isBetaTester: filters.isBetaTester,\n });\n\n const handleFetchData = (data: DataTableFetchDataProps) => {\n const usernameFilter = data.filters?.find((f) => f.id === 'username');\n const newEmailOrUsername = usernameFilter ? usernameFilter.value : '';\n const betaTesterFilter = data.filters?.find((f) => f.id === 'isBetaTester');\n const newIsBetaTester = betaTesterFilter ? betaTesterFilter.value : '';\n const filtersChanged = (newEmailOrUsername !== filters.username) || (newIsBetaTester !== filters.isBetaTester);\n\n if (filtersChanged) {\n setFilters((prevFilters) => ({\n ...prevFilters,\n username: newEmailOrUsername,\n isBetaTester: newIsBetaTester,\n page: 0,\n }));\n return;\n }\n\n if (data.pageIndex !== filters.page) {\n setFilters((prevFilters) => ({ ...prevFilters, page: data.pageIndex }));\n }\n };\n\n const tableColumns = [\n { accessor: 'username', Header: intl.formatMessage(messages.username), Filter: UsernameFilter },\n { accessor: 'fullName', Header: intl.formatMessage(messages.fullName), disableFilters: true },\n { accessor: 'email', Header: intl.formatMessage(messages.email), disableFilters: true },\n {\n accessor: 'mode',\n Header: intl.formatMessage(messages.track),\n Cell: ({ value }: { value: string }) => (\n <span className=\"text-capitalize\">{value || 'N/A'}</span>\n ),\n disableFilters: true,\n },\n {\n accessor: 'isBetaTester',\n Header: intl.formatMessage(messages.betaTester),\n Cell: ({ value }: { value: string }) => (value ? intl.formatMessage(messages.trueLabel) : ''),\n Filter: BetaTesterFilter\n },\n ];\n\n const ActionCustomCell = useCallback(({ row: { original } }: TableCellValue<EnrolledLearner>) => {\n const popoverContent = (\n <Popover\n id={`popover-${original.username}`}\n className=\"border-0 shadow-sm\"\n >\n <Popover.Content className=\"p-0 border-0\">\n <div className=\"dropdown-menu show position-static border shadow-sm\">\n <button\n type=\"button\"\n className=\"dropdown-item\"\n onClick={() => onBetaTesterChange(original)}\n >\n {original.isBetaTester\n ? intl.formatMessage(messages.revokeBetaTester)\n : intl.formatMessage(messages.grantBetaTester)}\n </button>\n </div>\n </Popover.Content>\n </Popover>\n );\n\n return (\n <ActionRow className=\"justify-content-start\">\n <Button className=\"pl-0\" onClick={() => onUnenroll(original)} variant=\"link\">\n {intl.formatMessage(messages.unenrollButton)}\n </Button>\n <OverlayTrigger\n trigger=\"click\"\n placement=\"bottom-end\"\n overlay={popoverContent}\n rootClose\n >\n <IconButton\n alt={intl.formatMessage(messages.changeBetaTesterStatus)}\n className=\"lead\"\n iconAs={MoreVert}\n />\n </OverlayTrigger>\n </ActionRow>\n );\n }, [intl, onBetaTesterChange, onUnenroll]);\n\n return (\n <DataTable\n className=\"mt-3\"\n columns={tableColumns}\n additionalColumns={[\n {\n id: 'actions',\n Header: intl.formatMessage(messages.actions),\n Cell: ActionCustomCell,\n }\n ]}\n data={data.results}\n fetchData={handleFetchData}\n state={{\n pageIndex: filters.page,\n pageSize: ENROLLMENTS_PAGE_SIZE,\n filters: [\n { id: 'username', value: filters.username },\n { id: 'isBetaTester', value: filters.isBetaTester },\n ]\n }}\n isFilterable\n isLoading={isLoading}\n isPaginated\n itemCount={data.count}\n manualFilters\n manualPagination\n numBreakoutFilters={2}\n pageSize={ENROLLMENTS_PAGE_SIZE}\n pageCount={data.numPages}\n FilterStatusComponent={() => null}\n >\n <DataTable.TableControlBar className=\"px-3 pt-3 pb-2\" />\n <DataTable.Table />\n <DataTable.EmptyTable content={intl.formatMessage(messages.noEnrollments)} />\n <DataTable.TableFooter />\n </DataTable>\n );\n};\n\nexport default EnrollmentsList;\n"]}
@@ -3,7 +3,6 @@ interface UnenrollModalProps {
3
3
  learner: EnrolledLearner;
4
4
  isOpen: boolean;
5
5
  onClose: () => void;
6
- onSuccess: () => void;
7
6
  }
8
- declare const UnenrollModal: ({ learner, isOpen, onClose, onSuccess }: UnenrollModalProps) => import("react/jsx-runtime").JSX.Element;
7
+ declare const UnenrollModal: ({ learner, isOpen, onClose }: UnenrollModalProps) => import("react/jsx-runtime").JSX.Element;
9
8
  export default UnenrollModal;
@@ -5,7 +5,7 @@ import { Button, ModalDialog } from '@openedx/paragon';
5
5
  import { useAlert } from '../../providers/AlertProvider';
6
6
  import { useUpdateEnrollments } from '../../enrollments/data/apiHook';
7
7
  import messages from '../../enrollments/messages';
8
- const UnenrollModal = ({ learner, isOpen, onClose, onSuccess }) => {
8
+ const UnenrollModal = ({ learner, isOpen, onClose }) => {
9
9
  const intl = useIntl();
10
10
  const { courseId = '' } = useParams();
11
11
  const { mutate: unenrollLearners, isPending } = useUpdateEnrollments(courseId);
@@ -16,7 +16,6 @@ const UnenrollModal = ({ learner, isOpen, onClose, onSuccess }) => {
16
16
  action: 'unenroll',
17
17
  }, {
18
18
  onSuccess: () => {
19
- onSuccess();
20
19
  onClose();
21
20
  },
22
21
  onError: (error) => {
@@ -1 +1 @@
1
- {"version":3,"file":"UnenrollModal.js","sourceRoot":"","sources":["../../../src/enrollments/components/UnenrollModal.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,QAAQ,MAAM,2BAA2B,CAAC;AAUjD,MAAM,aAAa,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAsB,EAAE,EAAE;IACpF,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,GAAG,SAAS,EAAwB,CAAC;IAC5D,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC/E,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,EAAE,CAAC;IAEjC,MAAM,cAAc,GAAG,GAAG,EAAE;QAC1B,gBAAgB,CAAC;YACf,UAAU,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC;YAC9B,MAAM,EAAE,UAAU;SACnB,EAAE;YACD,SAAS,EAAE,GAAG,EAAE;gBACd,SAAS,EAAE,CAAC;gBACZ,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACjB,SAAS,CAAC;oBACR,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC;oBAC3E,OAAO,EAAE,QAAQ;oBACjB,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC;iBACtD,CAAC,CAAC;YACL,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,CACL,MAAC,WAAW,IAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,iBAAiB,EAAE,KAAK,aAC3H,KAAC,WAAW,CAAC,MAAM,cACjB,aAAI,SAAS,EAAC,kBAAkB,YAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC,GAAM,GACtE,EACrB,KAAC,WAAW,CAAC,IAAI,IAAC,SAAS,EAAC,MAAM,YAChC,sBAAI,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,4BAA4B,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAK,GAC7E,EACnB,MAAC,WAAW,CAAC,MAAM,eACjB,KAAC,MAAM,IAAC,OAAO,EAAC,UAAU,EAAC,OAAO,EAAE,OAAO,YAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,YAAY,CAAC,GAAU,EACjG,KAAC,MAAM,IACL,SAAS,EAAC,MAAM,EAChB,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,SAAS,YAElB,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,GACrC,IACU,IACT,CACf,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,aAAa,CAAC","sourcesContent":["import { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Button, ModalDialog } from '@openedx/paragon';\nimport { useAlert } from '@src/providers/AlertProvider';\nimport { useUpdateEnrollments } from '@src/enrollments/data/apiHook';\nimport messages from '@src/enrollments/messages';\nimport { EnrolledLearner } from '@src/enrollments/types';\n\ninterface UnenrollModalProps {\n learner: EnrolledLearner,\n isOpen: boolean,\n onClose: () => void,\n onSuccess: () => void,\n}\n\nconst UnenrollModal = ({ learner, isOpen, onClose, onSuccess }: UnenrollModalProps) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const { mutate: unenrollLearners, isPending } = useUpdateEnrollments(courseId);\n const { showModal } = useAlert();\n\n const handleUnenroll = () => {\n unenrollLearners({\n identifier: [learner.username],\n action: 'unenroll',\n }, {\n onSuccess: () => {\n onSuccess();\n onClose();\n },\n onError: (error) => {\n showModal({\n message: error.message || intl.formatMessage(messages.unenrollLearnerError),\n variant: 'danger',\n confirmText: intl.formatMessage(messages.closeButton),\n });\n }\n });\n };\n\n return (\n <ModalDialog isOpen={isOpen} onClose={onClose} title={intl.formatMessage(messages.unenrollLearners)} isOverflowVisible={false}>\n <ModalDialog.Header>\n <h3 className=\"text-primary-500\">{intl.formatMessage(messages.unenrollLearnerTitle)}</h3>\n </ModalDialog.Header>\n <ModalDialog.Body className=\"py-4\">\n <p>{intl.formatMessage(messages.unenrollLearnersConfirmation, { name: learner.fullName })}</p>\n </ModalDialog.Body>\n <ModalDialog.Footer>\n <Button variant=\"tertiary\" onClick={onClose}>{intl.formatMessage(messages.cancelButton)}</Button>\n <Button\n className=\"ml-2\"\n onClick={handleUnenroll}\n disabled={isPending}\n >\n {intl.formatMessage(messages.unenrollButton)}\n </Button>\n </ModalDialog.Footer>\n </ModalDialog>\n );\n};\n\nexport default UnenrollModal;\n"]}
1
+ {"version":3,"file":"UnenrollModal.js","sourceRoot":"","sources":["../../../src/enrollments/components/UnenrollModal.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,QAAQ,MAAM,2BAA2B,CAAC;AASjD,MAAM,aAAa,GAAG,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAsB,EAAE,EAAE;IACzE,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,EAAE,QAAQ,GAAG,EAAE,EAAE,GAAG,SAAS,EAAwB,CAAC;IAC5D,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,SAAS,EAAE,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAC/E,MAAM,EAAE,SAAS,EAAE,GAAG,QAAQ,EAAE,CAAC;IAEjC,MAAM,cAAc,GAAG,GAAG,EAAE;QAC1B,gBAAgB,CAAC;YACf,UAAU,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC;YAC9B,MAAM,EAAE,UAAU;SACnB,EAAE;YACD,SAAS,EAAE,GAAG,EAAE;gBACd,OAAO,EAAE,CAAC;YACZ,CAAC;YACD,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACjB,SAAS,CAAC;oBACR,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC;oBAC3E,OAAO,EAAE,QAAQ;oBACjB,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC;iBACtD,CAAC,CAAC;YACL,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAC;IAEF,OAAO,CACL,MAAC,WAAW,IAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,iBAAiB,EAAE,KAAK,aAC3H,KAAC,WAAW,CAAC,MAAM,cACjB,aAAI,SAAS,EAAC,kBAAkB,YAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC,GAAM,GACtE,EACrB,KAAC,WAAW,CAAC,IAAI,IAAC,SAAS,EAAC,MAAM,YAChC,sBAAI,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,4BAA4B,EAAE,EAAE,IAAI,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,GAAK,GAC7E,EACnB,MAAC,WAAW,CAAC,MAAM,eACjB,KAAC,MAAM,IAAC,OAAO,EAAC,UAAU,EAAC,OAAO,EAAE,OAAO,YAAG,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,YAAY,CAAC,GAAU,EACjG,KAAC,MAAM,IACL,SAAS,EAAC,MAAM,EAChB,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,SAAS,YAElB,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,GACrC,IACU,IACT,CACf,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,aAAa,CAAC","sourcesContent":["import { useParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Button, ModalDialog } from '@openedx/paragon';\nimport { useAlert } from '@src/providers/AlertProvider';\nimport { useUpdateEnrollments } from '@src/enrollments/data/apiHook';\nimport messages from '@src/enrollments/messages';\nimport { EnrolledLearner } from '@src/enrollments/types';\n\ninterface UnenrollModalProps {\n learner: EnrolledLearner,\n isOpen: boolean,\n onClose: () => void,\n}\n\nconst UnenrollModal = ({ learner, isOpen, onClose }: UnenrollModalProps) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const { mutate: unenrollLearners, isPending } = useUpdateEnrollments(courseId);\n const { showModal } = useAlert();\n\n const handleUnenroll = () => {\n unenrollLearners({\n identifier: [learner.username],\n action: 'unenroll',\n }, {\n onSuccess: () => {\n onClose();\n },\n onError: (error) => {\n showModal({\n message: error.message || intl.formatMessage(messages.unenrollLearnerError),\n variant: 'danger',\n confirmText: intl.formatMessage(messages.closeButton),\n });\n }\n });\n };\n\n return (\n <ModalDialog isOpen={isOpen} onClose={onClose} title={intl.formatMessage(messages.unenrollLearners)} isOverflowVisible={false}>\n <ModalDialog.Header>\n <h3 className=\"text-primary-500\">{intl.formatMessage(messages.unenrollLearnerTitle)}</h3>\n </ModalDialog.Header>\n <ModalDialog.Body className=\"py-4\">\n <p>{intl.formatMessage(messages.unenrollLearnersConfirmation, { name: learner.fullName })}</p>\n </ModalDialog.Body>\n <ModalDialog.Footer>\n <Button variant=\"tertiary\" onClick={onClose}>{intl.formatMessage(messages.cancelButton)}</Button>\n <Button\n className=\"ml-2\"\n onClick={handleUnenroll}\n disabled={isPending}\n >\n {intl.formatMessage(messages.unenrollButton)}\n </Button>\n </ModalDialog.Footer>\n </ModalDialog>\n );\n};\n\nexport default UnenrollModal;\n"]}
@@ -0,0 +1,8 @@
1
+ import { EnrolledLearner } from '../../enrollments/types';
2
+ interface UpdateBetaTesterModalProps {
3
+ learner: EnrolledLearner;
4
+ isOpen: boolean;
5
+ onClose: () => void;
6
+ }
7
+ declare const UpdateBetaTesterModal: ({ learner, isOpen, onClose }: UpdateBetaTesterModalProps) => import("react/jsx-runtime").JSX.Element | null;
8
+ export default UpdateBetaTesterModal;
@@ -0,0 +1,52 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { useParams } from 'react-router-dom';
3
+ import { useCallback, useEffect } from 'react';
4
+ import { useIntl } from '@openedx/frontend-base';
5
+ import { Button, ModalDialog } from '@openedx/paragon';
6
+ import { useAlert } from '../../providers/AlertProvider';
7
+ import { useUpdateBetaTesters } from '../../enrollments/data/apiHook';
8
+ import messages from '../../enrollments/messages';
9
+ const UpdateBetaTesterModal = ({ learner, isOpen, onClose }) => {
10
+ const intl = useIntl();
11
+ const { courseId = '' } = useParams();
12
+ const { mutate: updateBetaTester, isPending } = useUpdateBetaTesters(courseId);
13
+ const { addAlert, showModal } = useAlert();
14
+ const handleUpdateBetaTester = useCallback(() => {
15
+ updateBetaTester({
16
+ identifier: [learner.username],
17
+ action: learner.isBetaTester ? 'remove' : 'add',
18
+ }, {
19
+ onSuccess: (data) => {
20
+ var _a;
21
+ const failedUsernames = ((_a = data.results) === null || _a === void 0 ? void 0 : _a.filter(user => user.userDoesNotExist).map(user => user.identifier)) || [];
22
+ if (failedUsernames.length > 0) {
23
+ addAlert({
24
+ type: 'danger',
25
+ message: intl.formatMessage(messages.failedBetaTesters),
26
+ extraContent: (failedUsernames.map((learner) => (_jsxs("p", { className: "mb-0", children: ["\u2022 ", intl.formatMessage(messages.unknownLearner, { learner })] }, learner))))
27
+ });
28
+ }
29
+ },
30
+ onError: () => {
31
+ showModal({
32
+ message: learner.isBetaTester ? intl.formatMessage(messages.removeBetaTesterError) : intl.formatMessage(messages.addBetaTesterError),
33
+ variant: 'danger',
34
+ confirmText: intl.formatMessage(messages.closeButton),
35
+ });
36
+ }
37
+ });
38
+ onClose();
39
+ }, [updateBetaTester, learner.username, learner.isBetaTester, addAlert, intl, showModal, onClose]);
40
+ useEffect(() => {
41
+ if (isOpen && !learner.isBetaTester) {
42
+ handleUpdateBetaTester();
43
+ }
44
+ }, [handleUpdateBetaTester, isOpen, learner.isBetaTester]);
45
+ // Only show modal for removing beta testers (requires confirmation)
46
+ if (!isOpen || !learner.isBetaTester) {
47
+ return null;
48
+ }
49
+ return (_jsxs(ModalDialog, { isOpen: isOpen, onClose: onClose, title: intl.formatMessage(messages.removeBetaTesterTitle), isOverflowVisible: false, children: [_jsx(ModalDialog.Header, { children: _jsx("h3", { className: "text-primary-500", children: intl.formatMessage(messages.removeBetaTesterTitle) }) }), _jsx(ModalDialog.Body, { className: "py-4", children: _jsx("p", { children: intl.formatMessage(messages.removeBetaTesterDescription) }) }), _jsxs(ModalDialog.Footer, { children: [_jsx(Button, { variant: "tertiary", onClick: onClose, children: intl.formatMessage(messages.cancelButton) }), _jsx(Button, { className: "ml-2", onClick: handleUpdateBetaTester, disabled: isPending, children: intl.formatMessage(messages.revoke) })] })] }));
50
+ };
51
+ export default UpdateBetaTesterModal;
52
+ //# sourceMappingURL=UpdateBetaTesterModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UpdateBetaTesterModal.js","sourceRoot":"","sources":["../../../src/enrollments/components/UpdateBetaTesterModal.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useParams } from 'react-router-dom';\nimport { useCallback, useEffect } from 'react';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Button, ModalDialog } from '@openedx/paragon';\nimport { useAlert } from '@src/providers/AlertProvider';\nimport { useUpdateBetaTesters } from '@src/enrollments/data/apiHook';\nimport messages from '@src/enrollments/messages';\nimport { EnrolledLearner } from '@src/enrollments/types';\n\ninterface UpdateBetaTesterModalProps {\n learner: EnrolledLearner,\n isOpen: boolean,\n onClose: () => void,\n}\n\nconst UpdateBetaTesterModal = ({ learner, isOpen, onClose }: UpdateBetaTesterModalProps) => {\n const intl = useIntl();\n const { courseId = '' } = useParams<{ courseId: string }>();\n const { mutate: updateBetaTester, isPending } = useUpdateBetaTesters(courseId);\n const { addAlert, showModal } = useAlert();\n\n const handleUpdateBetaTester = useCallback(() => {\n updateBetaTester({\n identifier: [learner.username],\n action: learner.isBetaTester ? 'remove' : 'add',\n }, {\n onSuccess: (data) => {\n const failedUsernames = data.results?.filter(user => user.userDoesNotExist).map(user => user.identifier) || [];\n if (failedUsernames.length > 0) {\n addAlert({\n type: 'danger',\n message: intl.formatMessage(messages.failedBetaTesters),\n extraContent: (\n failedUsernames.map((learner: string) => (\n <p key={learner} className=\"mb-0\">• {intl.formatMessage(messages.unknownLearner, { learner })}</p>\n ))\n )\n });\n }\n },\n onError: () => {\n showModal({\n message: learner.isBetaTester ? intl.formatMessage(messages.removeBetaTesterError) : intl.formatMessage(messages.addBetaTesterError),\n variant: 'danger',\n confirmText: intl.formatMessage(messages.closeButton),\n });\n }\n });\n\n onClose();\n }, [updateBetaTester, learner.username, learner.isBetaTester, addAlert, intl, showModal, onClose]);\n\n useEffect(() => {\n if (isOpen && !learner.isBetaTester) {\n handleUpdateBetaTester();\n }\n }, [handleUpdateBetaTester, isOpen, learner.isBetaTester]);\n\n // Only show modal for removing beta testers (requires confirmation)\n if (!isOpen || !learner.isBetaTester) {\n return null;\n }\n\n return (\n <ModalDialog isOpen={isOpen} onClose={onClose} title={intl.formatMessage(messages.removeBetaTesterTitle)} isOverflowVisible={false}>\n <ModalDialog.Header>\n <h3 className=\"text-primary-500\">{intl.formatMessage(messages.removeBetaTesterTitle)}</h3>\n </ModalDialog.Header>\n <ModalDialog.Body className=\"py-4\">\n <p>{intl.formatMessage(messages.removeBetaTesterDescription)}</p>\n </ModalDialog.Body>\n <ModalDialog.Footer>\n <Button variant=\"tertiary\" onClick={onClose}>{intl.formatMessage(messages.cancelButton)}</Button>\n <Button\n className=\"ml-2\"\n onClick={handleUpdateBetaTester}\n disabled={isPending}\n >\n {intl.formatMessage(messages.revoke)}\n </Button>\n </ModalDialog.Footer>\n </ModalDialog>\n );\n};\n\nexport default UpdateBetaTesterModal;\n"]}
@@ -1,5 +1,6 @@
1
- import { EnrollmentsParams, EnrollmentStatusResponse, EnrolledLearner, UpdateEnrollmentsParams } from '../../enrollments/types';
1
+ import { EnrollmentsParams, EnrollmentStatusResponse, EnrolledLearner, UpdateEnrollmentsParams, UpdateBetaTestersParams, UpdateEnrollmentsResponse, UpdateBetaTestersResponse } from '../../enrollments/types';
2
2
  import { DataList } from '../../types';
3
3
  export declare const getEnrollments: (courseId: string, params: EnrollmentsParams) => Promise<DataList<EnrolledLearner>>;
4
4
  export declare const getEnrollmentStatus: (courseId: string, userIdentifier: string) => Promise<EnrollmentStatusResponse>;
5
- export declare const updateEnrollments: (courseId: string, params: UpdateEnrollmentsParams) => Promise<void>;
5
+ export declare const updateEnrollments: (courseId: string, params: UpdateEnrollmentsParams) => Promise<UpdateEnrollmentsResponse>;
6
+ export declare const updateBetaTesters: (courseId: string, params: UpdateBetaTestersParams) => Promise<UpdateBetaTestersResponse>;
@@ -31,6 +31,12 @@ export const getEnrollmentStatus = (courseId, userIdentifier) => __awaiter(void
31
31
  });
32
32
  export const updateEnrollments = (courseId, params) => __awaiter(void 0, void 0, void 0, function* () {
33
33
  const snakeCaseParams = snakeCaseObject(params);
34
- yield getAuthenticatedHttpClient().post(`${getApiBaseUrl()}/api/instructor/v2/courses/${courseId}/enrollments/modify`, snakeCaseParams);
34
+ const { data } = yield getAuthenticatedHttpClient().post(`${getApiBaseUrl()}/api/instructor/v2/courses/${courseId}/enrollments/modify`, snakeCaseParams);
35
+ return camelCaseObject(data);
36
+ });
37
+ export const updateBetaTesters = (courseId, params) => __awaiter(void 0, void 0, void 0, function* () {
38
+ const snakeCaseParams = snakeCaseObject(params);
39
+ const { data } = yield getAuthenticatedHttpClient().post(`${getApiBaseUrl()}/api/instructor/v2/courses/${courseId}/beta_testers/modify`, snakeCaseParams);
40
+ return camelCaseObject(data);
35
41
  });
36
42
  //# sourceMappingURL=api.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/enrollments/data/api.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,eAAe,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACtG,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAI9C,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,QAAgB,EAChB,MAAyB,EACW,EAAE;IACtC,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC;QACtC,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE;QAClC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE;KACtC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;QAC3B,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACxB,WAAW,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,0BAA0B,EAAE,CAAC,GAAG,CACrD,GAAG,aAAa,EAAE,8BAA8B,QAAQ,gBAAgB,WAAW,CAAC,QAAQ,EAAE,EAAE,CACjG,CAAC;IACF,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC,CAAA,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,QAAgB,EAChB,cAAsB,EACa,EAAE;IACrC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,0BAA0B,EAAE,CAAC,IAAI,CACtD,GAAG,aAAa,EAAE,YAAY,QAAQ,+CAA+C,EAAE;QACrF,yBAAyB,EAAE,cAAc;KAC1C,CACF,CAAC;IACF,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC,CAAA,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,QAAgB,EAChB,MAA+B,EAChB,EAAE;IACjB,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,0BAA0B,EAAE,CAAC,IAAI,CACrC,GAAG,aAAa,EAAE,8BAA8B,QAAQ,qBAAqB,EAC7E,eAAe,CAChB,CAAC;AACJ,CAAC,CAAA,CAAC","sourcesContent":["import { camelCaseObject, getAuthenticatedHttpClient, snakeCaseObject } from '@openedx/frontend-base';\nimport { getApiBaseUrl } from '@src/data/api';\nimport { EnrollmentsParams, EnrollmentStatusResponse, EnrolledLearner, UpdateEnrollmentsParams } from '@src/enrollments/types';\nimport { DataList } from '@src/types';\n\nexport const getEnrollments = async (\n courseId: string,\n params: EnrollmentsParams\n): Promise<DataList<EnrolledLearner>> => {\n const queryParams = new URLSearchParams({\n page: (params.page + 1).toString(),\n page_size: params.pageSize.toString(),\n });\n\n if (params.emailOrUsername) {\n queryParams.append('search', params.emailOrUsername);\n }\n\n if (params.isBetaTester) {\n queryParams.append('is_beta_tester', params.isBetaTester);\n }\n\n const { data } = await getAuthenticatedHttpClient().get(\n `${getApiBaseUrl()}/api/instructor/v2/courses/${courseId}/enrollments?${queryParams.toString()}`\n );\n return camelCaseObject(data);\n};\n\nexport const getEnrollmentStatus = async (\n courseId: string,\n userIdentifier: string\n): Promise<EnrollmentStatusResponse> => {\n const { data } = await getAuthenticatedHttpClient().post(\n `${getApiBaseUrl()}/courses/${courseId}/instructor/api/get_student_enrollment_status`, {\n unique_student_identifier: userIdentifier,\n }\n );\n return camelCaseObject(data);\n};\n\nexport const updateEnrollments = async (\n courseId: string,\n params: UpdateEnrollmentsParams\n): Promise<void> => {\n const snakeCaseParams = snakeCaseObject(params);\n await getAuthenticatedHttpClient().post(\n `${getApiBaseUrl()}/api/instructor/v2/courses/${courseId}/enrollments/modify`,\n snakeCaseParams\n );\n};\n"]}
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../src/enrollments/data/api.ts"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,eAAe,EAAE,0BAA0B,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACtG,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAI9C,MAAM,CAAC,MAAM,cAAc,GAAG,CAC5B,QAAgB,EAChB,MAAyB,EACW,EAAE;IACtC,MAAM,WAAW,GAAG,IAAI,eAAe,CAAC;QACtC,IAAI,EAAE,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE;QAClC,SAAS,EAAE,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE;KACtC,CAAC,CAAC;IAEH,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;QAC3B,WAAW,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,eAAe,CAAC,CAAC;IACvD,CAAC;IAED,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;QACxB,WAAW,CAAC,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC;IAC5D,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,0BAA0B,EAAE,CAAC,GAAG,CACrD,GAAG,aAAa,EAAE,8BAA8B,QAAQ,gBAAgB,WAAW,CAAC,QAAQ,EAAE,EAAE,CACjG,CAAC;IACF,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC,CAAA,CAAC;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,QAAgB,EAChB,cAAsB,EACa,EAAE;IACrC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,0BAA0B,EAAE,CAAC,IAAI,CACtD,GAAG,aAAa,EAAE,YAAY,QAAQ,+CAA+C,EAAE;QACrF,yBAAyB,EAAE,cAAc;KAC1C,CACF,CAAC;IACF,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC,CAAA,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,QAAgB,EAChB,MAA+B,EACK,EAAE;IACtC,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,0BAA0B,EAAE,CAAC,IAAI,CACtD,GAAG,aAAa,EAAE,8BAA8B,QAAQ,qBAAqB,EAC7E,eAAe,CAChB,CAAC;IACF,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC,CAAA,CAAC;AAEF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAC/B,QAAgB,EAChB,MAA+B,EACK,EAAE;IACtC,MAAM,eAAe,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IAChD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,0BAA0B,EAAE,CAAC,IAAI,CACtD,GAAG,aAAa,EAAE,8BAA8B,QAAQ,sBAAsB,EAC9E,eAAe,CAChB,CAAC;IACF,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC;AAC/B,CAAC,CAAA,CAAC","sourcesContent":["import { camelCaseObject, getAuthenticatedHttpClient, snakeCaseObject } from '@openedx/frontend-base';\nimport { getApiBaseUrl } from '@src/data/api';\nimport { EnrollmentsParams, EnrollmentStatusResponse, EnrolledLearner, UpdateEnrollmentsParams, UpdateBetaTestersParams, UpdateEnrollmentsResponse, UpdateBetaTestersResponse } from '@src/enrollments/types';\nimport { DataList } from '@src/types';\n\nexport const getEnrollments = async (\n courseId: string,\n params: EnrollmentsParams\n): Promise<DataList<EnrolledLearner>> => {\n const queryParams = new URLSearchParams({\n page: (params.page + 1).toString(),\n page_size: params.pageSize.toString(),\n });\n\n if (params.emailOrUsername) {\n queryParams.append('search', params.emailOrUsername);\n }\n\n if (params.isBetaTester) {\n queryParams.append('is_beta_tester', params.isBetaTester);\n }\n\n const { data } = await getAuthenticatedHttpClient().get(\n `${getApiBaseUrl()}/api/instructor/v2/courses/${courseId}/enrollments?${queryParams.toString()}`\n );\n return camelCaseObject(data);\n};\n\nexport const getEnrollmentStatus = async (\n courseId: string,\n userIdentifier: string\n): Promise<EnrollmentStatusResponse> => {\n const { data } = await getAuthenticatedHttpClient().post(\n `${getApiBaseUrl()}/courses/${courseId}/instructor/api/get_student_enrollment_status`, {\n unique_student_identifier: userIdentifier,\n }\n );\n return camelCaseObject(data);\n};\n\nexport const updateEnrollments = async (\n courseId: string,\n params: UpdateEnrollmentsParams\n): Promise<UpdateEnrollmentsResponse> => {\n const snakeCaseParams = snakeCaseObject(params);\n const { data } = await getAuthenticatedHttpClient().post(\n `${getApiBaseUrl()}/api/instructor/v2/courses/${courseId}/enrollments/modify`,\n snakeCaseParams\n );\n return camelCaseObject(data);\n};\n\nexport const updateBetaTesters = async (\n courseId: string,\n params: UpdateBetaTestersParams\n): Promise<UpdateBetaTestersResponse> => {\n const snakeCaseParams = snakeCaseObject(params);\n const { data } = await getAuthenticatedHttpClient().post(\n `${getApiBaseUrl()}/api/instructor/v2/courses/${courseId}/beta_testers/modify`,\n snakeCaseParams\n );\n return camelCaseObject(data);\n};\n"]}
@@ -1,4 +1,5 @@
1
- import { EnrollmentsParams, UpdateEnrollmentsParams } from '../../enrollments/types';
1
+ import { EnrollmentsParams, UpdateBetaTestersParams, UpdateEnrollmentsParams } from '../../enrollments/types';
2
2
  export declare const useEnrollments: (courseId: string, params: EnrollmentsParams) => import("@tanstack/react-query").UseQueryResult<import("../../types").DataList<import("../../enrollments/types").EnrolledLearner>, Error>;
3
3
  export declare const useEnrollmentByUserId: (courseId: string, userIdentifier: string) => import("@tanstack/react-query").UseQueryResult<import("../../enrollments/types").EnrollmentStatusResponse, Error>;
4
- export declare const useUpdateEnrollments: (courseId: string) => import("@tanstack/react-query").UseMutationResult<void, Error, UpdateEnrollmentsParams, unknown>;
4
+ export declare const useUpdateEnrollments: (courseId: string) => import("@tanstack/react-query").UseMutationResult<import("../../enrollments/types").UpdateEnrollmentsResponse, Error, UpdateEnrollmentsParams, unknown>;
5
+ export declare const useUpdateBetaTesters: (courseId: string) => import("@tanstack/react-query").UseMutationResult<import("../../enrollments/types").UpdateBetaTestersResponse, Error, UpdateBetaTestersParams, unknown>;
@@ -1,5 +1,5 @@
1
1
  import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
2
- import { getEnrollments, getEnrollmentStatus, updateEnrollments } from '../../enrollments/data/api';
2
+ import { getEnrollments, getEnrollmentStatus, updateBetaTesters, updateEnrollments } from '../../enrollments/data/api';
3
3
  import { enrollmentsQueryKeys } from '../../enrollments/data/queryKeys';
4
4
  export const useEnrollments = (courseId, params) => (useQuery({
5
5
  queryKey: enrollmentsQueryKeys.byCoursePaginated(courseId, params),
@@ -20,4 +20,13 @@ export const useUpdateEnrollments = (courseId) => {
20
20
  },
21
21
  }));
22
22
  };
23
+ export const useUpdateBetaTesters = (courseId) => {
24
+ const queryClient = useQueryClient();
25
+ return (useMutation({
26
+ mutationFn: (params) => updateBetaTesters(courseId, params),
27
+ onSuccess: () => {
28
+ queryClient.invalidateQueries({ queryKey: enrollmentsQueryKeys.byCourse(courseId) });
29
+ },
30
+ }));
31
+ };
23
32
  //# sourceMappingURL=apiHook.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"apiHook.js","sourceRoot":"","sources":["../../../src/enrollments/data/apiHook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACnG,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAGvE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAgB,EAAE,MAAyB,EAAE,EAAE,CAAC,CAC7E,QAAQ,CAAC;IACP,QAAQ,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC;IAClE,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC/C,OAAO,EAAE,CAAC,CAAC,QAAQ;CACpB,CAAC,CACH,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,QAAgB,EAAE,cAAsB,EAAE,EAAE,CAAC,CACjF,QAAQ,CAAC;IACP,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IACjE,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,cAAc,CAAC;IAC5D,OAAO,EAAE,KAAK;CACf,CAAC,CACH,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,QAAgB,EAAE,EAAE;IACvD,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,OAAO,CAAC,WAAW,CAAC;QAClB,UAAU,EAAE,CAAC,MAA+B,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC;QACpF,SAAS,EAAE,GAAG,EAAE;YACd,WAAW,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACvF,CAAC;KACF,CAAC,CAAC,CAAC;AACN,CAAC,CAAC","sourcesContent":["import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { getEnrollments, getEnrollmentStatus, updateEnrollments } from '@src/enrollments/data/api';\nimport { enrollmentsQueryKeys } from '@src/enrollments/data/queryKeys';\nimport { EnrollmentsParams, UpdateEnrollmentsParams } from '@src/enrollments/types';\n\nexport const useEnrollments = (courseId: string, params: EnrollmentsParams) => (\n useQuery({\n queryKey: enrollmentsQueryKeys.byCoursePaginated(courseId, params),\n queryFn: () => getEnrollments(courseId, params),\n enabled: !!courseId,\n })\n);\n\nexport const useEnrollmentByUserId = (courseId: string, userIdentifier: string) => (\n useQuery({\n queryKey: enrollmentsQueryKeys.byUserId(courseId, userIdentifier),\n queryFn: () => getEnrollmentStatus(courseId, userIdentifier),\n enabled: false,\n })\n);\n\nexport const useUpdateEnrollments = (courseId: string) => {\n const queryClient = useQueryClient();\n return (useMutation({\n mutationFn: (params: UpdateEnrollmentsParams) => updateEnrollments(courseId, params),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: enrollmentsQueryKeys.byCourse(courseId) });\n },\n }));\n};\n"]}
1
+ {"version":3,"file":"apiHook.js","sourceRoot":"","sources":["../../../src/enrollments/data/apiHook.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AACtH,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAGvE,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,QAAgB,EAAE,MAAyB,EAAE,EAAE,CAAC,CAC7E,QAAQ,CAAC;IACP,QAAQ,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC;IAClE,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC/C,OAAO,EAAE,CAAC,CAAC,QAAQ;CACpB,CAAC,CACH,CAAC;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,QAAgB,EAAE,cAAsB,EAAE,EAAE,CAAC,CACjF,QAAQ,CAAC;IACP,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IACjE,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,CAAC,QAAQ,EAAE,cAAc,CAAC;IAC5D,OAAO,EAAE,KAAK;CACf,CAAC,CACH,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,QAAgB,EAAE,EAAE;IACvD,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,OAAO,CAAC,WAAW,CAAC;QAClB,UAAU,EAAE,CAAC,MAA+B,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC;QACpF,SAAS,EAAE,GAAG,EAAE;YACd,WAAW,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACvF,CAAC;KACF,CAAC,CAAC,CAAC;AACN,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,QAAgB,EAAE,EAAE;IACvD,MAAM,WAAW,GAAG,cAAc,EAAE,CAAC;IACrC,OAAO,CAAC,WAAW,CAAC;QAClB,UAAU,EAAE,CAAC,MAA+B,EAAE,EAAE,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC;QACpF,SAAS,EAAE,GAAG,EAAE;YACd,WAAW,CAAC,iBAAiB,CAAC,EAAE,QAAQ,EAAE,oBAAoB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;QACvF,CAAC;KACF,CAAC,CAAC,CAAC;AACN,CAAC,CAAC","sourcesContent":["import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';\nimport { getEnrollments, getEnrollmentStatus, updateBetaTesters, updateEnrollments } from '@src/enrollments/data/api';\nimport { enrollmentsQueryKeys } from '@src/enrollments/data/queryKeys';\nimport { EnrollmentsParams, UpdateBetaTestersParams, UpdateEnrollmentsParams } from '@src/enrollments/types';\n\nexport const useEnrollments = (courseId: string, params: EnrollmentsParams) => (\n useQuery({\n queryKey: enrollmentsQueryKeys.byCoursePaginated(courseId, params),\n queryFn: () => getEnrollments(courseId, params),\n enabled: !!courseId,\n })\n);\n\nexport const useEnrollmentByUserId = (courseId: string, userIdentifier: string) => (\n useQuery({\n queryKey: enrollmentsQueryKeys.byUserId(courseId, userIdentifier),\n queryFn: () => getEnrollmentStatus(courseId, userIdentifier),\n enabled: false,\n })\n);\n\nexport const useUpdateEnrollments = (courseId: string) => {\n const queryClient = useQueryClient();\n return (useMutation({\n mutationFn: (params: UpdateEnrollmentsParams) => updateEnrollments(courseId, params),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: enrollmentsQueryKeys.byCourse(courseId) });\n },\n }));\n};\n\nexport const useUpdateBetaTesters = (courseId: string) => {\n const queryClient = useQueryClient();\n return (useMutation({\n mutationFn: (params: UpdateBetaTestersParams) => updateBetaTesters(courseId, params),\n onSuccess: () => {\n queryClient.invalidateQueries({ queryKey: enrollmentsQueryKeys.byCourse(courseId) });\n },\n }));\n};\n"]}
@@ -89,6 +89,16 @@ declare const messages: {
89
89
  defaultMessage: string;
90
90
  description: string;
91
91
  };
92
+ grantBetaTester: {
93
+ id: string;
94
+ defaultMessage: string;
95
+ description: string;
96
+ };
97
+ revokeBetaTester: {
98
+ id: string;
99
+ defaultMessage: string;
100
+ description: string;
101
+ };
92
102
  allEnrollees: {
93
103
  id: string;
94
104
  defaultMessage: string;
@@ -169,5 +179,50 @@ declare const messages: {
169
179
  defaultMessage: string;
170
180
  description: string;
171
181
  };
182
+ addBetaTestersInstructions: {
183
+ id: string;
184
+ defaultMessage: string;
185
+ description: string;
186
+ };
187
+ failedEnrollLearners: {
188
+ id: string;
189
+ defaultMessage: string;
190
+ description: string;
191
+ };
192
+ unknownLearner: {
193
+ id: string;
194
+ defaultMessage: string;
195
+ description: string;
196
+ };
197
+ removeBetaTesterError: {
198
+ id: string;
199
+ defaultMessage: string;
200
+ description: string;
201
+ };
202
+ failedBetaTesters: {
203
+ id: string;
204
+ defaultMessage: string;
205
+ description: string;
206
+ };
207
+ addBetaTesterError: {
208
+ id: string;
209
+ defaultMessage: string;
210
+ description: string;
211
+ };
212
+ removeBetaTesterTitle: {
213
+ id: string;
214
+ defaultMessage: string;
215
+ description: string;
216
+ };
217
+ removeBetaTesterDescription: {
218
+ id: string;
219
+ defaultMessage: string;
220
+ description: string;
221
+ };
222
+ revoke: {
223
+ id: string;
224
+ defaultMessage: string;
225
+ description: string;
226
+ };
172
227
  };
173
228
  export default messages;
@@ -62,7 +62,7 @@ const messages = defineMessages({
62
62
  },
63
63
  addLearnerInstructions: {
64
64
  id: 'instruct.enrollments.modals.checkEnrollmentStatus.addLearnerInstructions',
65
- defaultMessage: 'Learner\'s My Open edX email address or username',
65
+ defaultMessage: 'Enter email addresses and/or usernames separated by new lines or commas. You will not get notification for emails that bounce, so please double-check spelling.',
66
66
  description: 'Instructions for enroll learners to the course',
67
67
  },
68
68
  enrollmentStatusPlaceholder: {
@@ -90,6 +90,16 @@ const messages = defineMessages({
90
90
  defaultMessage: 'Change Beta Tester Status',
91
91
  description: 'Alt text for change beta tester status icon button',
92
92
  },
93
+ grantBetaTester: {
94
+ id: 'instruct.enrollments.grantBetaTester',
95
+ defaultMessage: 'Grant Beta Tester Role',
96
+ description: 'Menu option to grant beta tester status',
97
+ },
98
+ revokeBetaTester: {
99
+ id: 'instruct.enrollments.revokeBetaTester',
100
+ defaultMessage: 'Remove Beta Tester Role',
101
+ description: 'Menu option to revoke beta tester status',
102
+ },
93
103
  allEnrollees: {
94
104
  id: 'instruct.enrollments.allEnrollees',
95
105
  defaultMessage: 'All Enrollees',
@@ -169,6 +179,51 @@ const messages = defineMessages({
169
179
  id: 'instruct.enrollments.modals.enrollLearnerNotFoundError',
170
180
  defaultMessage: 'One or more learners were not found. Please check the email addresses or usernames and try again.',
171
181
  description: 'Error message displayed when enrolling learners fails due to learner not found',
182
+ },
183
+ addBetaTestersInstructions: {
184
+ id: 'instruct.enrollments.modals.addBetaTesters.addBetaTestersInstructions',
185
+ defaultMessage: 'Enter email addresses and/or usernames separated by new lines or commas. Note: Users must have an activated My Open edX account before they can be enrolled as beta testers.',
186
+ description: 'Instructions for adding beta testers to the course',
187
+ },
188
+ failedEnrollLearners: {
189
+ id: 'instruct.enrollments.modals.enrollLearners.failedEnrollLearners',
190
+ defaultMessage: 'The following usernames and/or email addresses are invalid. All other learners have been enrolled.',
191
+ description: 'Message displaying the learners that could not be enrolled',
192
+ },
193
+ unknownLearner: {
194
+ id: 'instruct.enrollments.unknownLearner',
195
+ defaultMessage: 'Unknown learner: {learner}',
196
+ description: 'Displayed when a learner does not have a full name or username available',
197
+ },
198
+ removeBetaTesterError: {
199
+ id: 'instruct.enrollments.modals.removeBetaTesters.removeBetaTesterError',
200
+ defaultMessage: 'Error removing user as beta tester.',
201
+ description: 'Error message displayed when removing beta testers fails',
202
+ },
203
+ failedBetaTesters: {
204
+ id: 'instruct.enrollments.modals.addBetaTesters.failedBetaTesters',
205
+ defaultMessage: 'The following usernames and/or email addresses are invalid. All other beta testers have been added.',
206
+ description: 'Message displaying the learners that could not be added as beta testers',
207
+ },
208
+ addBetaTesterError: {
209
+ id: 'instruct.enrollments.modals.addBetaTesters.addBetaTesterError',
210
+ defaultMessage: 'Error adding users as beta testers.',
211
+ description: 'Error message displayed when adding beta testers fails',
212
+ },
213
+ removeBetaTesterTitle: {
214
+ id: 'instruct.enrollments.modals.removeBetaTester',
215
+ defaultMessage: 'Revoke access?',
216
+ description: 'Title for remove beta tester modal',
217
+ },
218
+ removeBetaTesterDescription: {
219
+ id: 'instruct.enrollments.modals.removeBetaTesterDescription',
220
+ defaultMessage: 'Revoke Beta Tester access?',
221
+ description: 'Description for remove beta tester modal',
222
+ },
223
+ revoke: {
224
+ id: 'instruct.enrollments.revoke',
225
+ defaultMessage: 'Revoke',
226
+ description: 'Button label for revoking access',
172
227
  }
173
228
  });
174
229
  export default messages;