Skip to content

Commit 5b51d52

Browse files
binodpantmanny-mlong74100adamstankiewiczkiram15
authored
feat: Bulk enrollment now in modal dialog launched from license manage page (#641)
* feat: Show user count in Enroll button ENT-5000 * feat: enroll button is disabled if revoked users are present * feat: Add dialog if invalid enroll case, but still allow enrolling. dialog shows if at least one revoked user is present. also enroll button is now shown in main table, enroll button also in dialog * feat: enroll button does not enable if no enrollable learners * feat: text change in dialog to be fyi-only * feat: Cleanup code to show/hide modal and bulk enroll dialog * refactor: new components to represent bulk enrollment warning, and button * feat: dialog is now its own component * feat: optimize logic a bit * feat: wrap all of bulk enrollment flow into the modal * feat: remove stepper, and introduce steps in the dialog itself * feat: table actions clear selection (#640) * feat: implement subscription page using paragon components * fix: re-add segment events for legacy table pagination/sort and csv download (#644) * fix: re-add segment events for legacy table pagination/sort and csv download * docs: add info to readme about start:with-theme * fix: needed to mock frontend-platform/analytics * fix: assert on event dispatch for csv button clicks * feat: add popover to course search title (#646) * fix: hide table selection if subscription is expired (#647) * fix: hide table selection if subscription is expired * fix: use .find instead of .filter * refactor: new components to represent bulk enrollment warning, and button * feat: dialog is now its own component * feat: restore lost changes in bulk actions file * feat: Bring stepper back, using fullscreen modal * style: lint * feat: Use modal dialog with min height, update stepper component * fix: re-introduce lost SCSS changes in sub management card * test: fix bulk actions tests, small other fixes * test: add tests for bulk enroll actions, one more to fix * test: fix submit test * test: test fix for course search results * feat: invoke clear all after bulk enroll complets * test: test data update * test: test fix for addCoursesStep * test: AddcoursesStep is mocked for now * test: don't need the searchcontext mock anymore Co-authored-by: Manny <[email protected]> Co-authored-by: Long Lin <[email protected]> Co-authored-by: Adam Stankiewicz <[email protected]> Co-authored-by: Kira Miller <[email protected]>
1 parent fa921aa commit 5b51d52

27 files changed

+457
-1071
lines changed
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
4+
import { Button } from '@edx/paragon';
5+
import { BookOpen } from '@edx/paragon/icons';
6+
7+
/**
8+
* Bulk action button, meant to be used in License management page to initiate Bulk Enrollment Dialog
9+
* @param {object} args Arguments
10+
* @param {array<string>} args.learners set of learners being enrolled
11+
* @param {Function} args.handleEnrollment function to invoke to enroll
12+
* @param {string} args.buttonType type (to distinguish buttons in the dom)
13+
*/
14+
const BulkEnrollButton = ({ learners, handleEnrollment, buttonType }) => (
15+
<Button
16+
variant="primary"
17+
onClick={handleEnrollment}
18+
iconBefore={BookOpen}
19+
disabled={learners.length < 1}
20+
data-testid={buttonType}
21+
>
22+
Enroll ({learners.length })
23+
</Button>
24+
);
25+
26+
BulkEnrollButton.defaultProps = {
27+
buttonType: 'BULK_ENROLL_DEFAULT',
28+
};
29+
30+
BulkEnrollButton.propTypes = {
31+
handleEnrollment: PropTypes.func.isRequired,
32+
learners: PropTypes.arrayOf(PropTypes.string).isRequired,
33+
buttonType: PropTypes.string,
34+
};
35+
36+
export default BulkEnrollButton;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import React from 'react';
2+
import { connect } from 'react-redux';
3+
import PropTypes from 'prop-types';
4+
5+
import BulkEnrollmentStepper from './stepper/BulkEnrollmentStepper';
6+
import BulkEnrollContextProvider from './BulkEnrollmentContext';
7+
8+
/**
9+
* @param {object} props Props
10+
* @param {array<string>} props.learners learner email list to enroll
11+
*/
12+
const BulkEnrollDialog = (props) => {
13+
const { learners } = props;
14+
return (
15+
<BulkEnrollContextProvider initialEmailsList={learners}>
16+
<BulkEnrollmentStepper {...props} />
17+
</BulkEnrollContextProvider>
18+
);
19+
};
20+
21+
const mapStateToProps = state => ({
22+
enterpriseSlug: state.portalConfiguration.enterpriseSlug,
23+
enterpriseId: state.portalConfiguration.enterpriseId,
24+
});
25+
26+
BulkEnrollDialog.propTypes = {
27+
learners: PropTypes.arrayOf(PropTypes.string).isRequired,
28+
};
29+
30+
export default connect(mapStateToProps)(BulkEnrollDialog);

src/components/BulkEnrollmentPage/BulkEnrollment.scss

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
.card-body {
2424
@extend .py-2;
25-
@extend .px-3
25+
@extend .px-3;
2626
}
2727

2828
.list-item {
@@ -62,28 +62,6 @@
6262
}
6363
}
6464

65-
.popover {
66-
max-width: 400px;
65+
.bulk-enroll-modal {
66+
min-height: 83vh;
6767
}
68-
69-
.popover .popover-header {
70-
border-bottom: 2px solid $gray-100;
71-
font-weight: 700;
72-
font-size: 15px;
73-
padding-left: 0;
74-
margin: 0 16px 0 16px;
75-
}
76-
77-
.popover-body {
78-
padding: 8px 16px 16px 16px;
79-
}
80-
81-
.popover-body p {
82-
margin: 0 0 8px 0;
83-
}
84-
85-
.popover-body hr {
86-
width: 30%;
87-
border-top: 2px solid $gray-100;
88-
margin: 8px 0 8px 0;
89-
}

src/components/BulkEnrollmentPage/BulkEnrollmentContext.jsx

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,17 @@ import selectedRowsReducer from './data/reducer';
55

66
export const BulkEnrollContext = createContext({});
77

8-
const BulkEnrollContextProvider = ({ children }) => {
8+
const BulkEnrollContextProvider = ({ children, initialEmailsList }) => {
99
const [selectedCourses, coursesDispatch] = useReducer(selectedRowsReducer, []);
10-
const [selectedEmails, emailsDispatch] = useReducer(selectedRowsReducer, []);
10+
// this format is to make this consistent with the format used by ReviewStep components
11+
// similar to DataTable row objects, but not exactly
12+
const formattedEmailsList = initialEmailsList.map(email => ({
13+
id: email,
14+
values: {
15+
userEmail: email,
16+
},
17+
}));
18+
const [selectedEmails, emailsDispatch] = useReducer(selectedRowsReducer, formattedEmailsList);
1119
const [selectedSubscription, setSelectedSubscription] = useState({});
1220

1321
const value = {
@@ -19,8 +27,13 @@ const BulkEnrollContextProvider = ({ children }) => {
1927
return <BulkEnrollContext.Provider value={value}>{children}</BulkEnrollContext.Provider>;
2028
};
2129

30+
BulkEnrollContextProvider.defaultProps = {
31+
initialEmailsList: [],
32+
};
33+
2234
BulkEnrollContextProvider.propTypes = {
2335
children: PropTypes.node.isRequired,
36+
initialEmailsList: PropTypes.arrayOf(PropTypes.string),
2437
};
2538

2639
export default BulkEnrollContextProvider;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import React from 'react';
2+
import PropTypes from 'prop-types';
3+
import classNames from 'classnames';
4+
5+
import {
6+
ActionRow, AlertModal, Button, Icon,
7+
} from '@edx/paragon';
8+
import { Error } from '@edx/paragon/icons';
9+
10+
import BulkEnrollButton from './BulkEnrollButton';
11+
12+
const BulkEnrollWarningModal = ({
13+
learners, isDialogOpen, onClose, onEnroll,
14+
}) => (
15+
<AlertModal
16+
title={(
17+
<>
18+
<Icon className={classNames('enroll-header', 'mr-1')} src={Error} />Revoked Learners Selected
19+
</>
20+
)}
21+
isOpen={isDialogOpen}
22+
footerNode={(
23+
<ActionRow>
24+
<Button variant="link" onClick={onClose}>Close</Button>
25+
<BulkEnrollButton
26+
learners={learners}
27+
handleEnrollment={onEnroll}
28+
buttonType="ENROLL_BTN_IN_WARNING_MODAL"
29+
/>
30+
</ActionRow>
31+
)}
32+
>
33+
Any learners with revoked licenses are not included. Click &quot;Enroll&quot; to enroll
34+
active and pending learners only
35+
</AlertModal>
36+
);
37+
38+
BulkEnrollWarningModal.defaultProps = {
39+
learners: [],
40+
isDialogOpen: false,
41+
};
42+
43+
BulkEnrollWarningModal.propTypes = {
44+
isDialogOpen: PropTypes.bool,
45+
learners: PropTypes.arrayOf(PropTypes.shape({})),
46+
onEnroll: PropTypes.func.isRequired,
47+
onClose: PropTypes.func.isRequired,
48+
};
49+
50+
export default BulkEnrollWarningModal;

src/components/BulkEnrollmentPage/CourseSearch.jsx

Lines changed: 0 additions & 71 deletions
This file was deleted.

src/components/BulkEnrollmentPage/CourseSearch.test.jsx

Lines changed: 0 additions & 44 deletions
This file was deleted.

src/components/BulkEnrollmentPage/index.jsx

Lines changed: 0 additions & 69 deletions
This file was deleted.

0 commit comments

Comments
 (0)