Skip to content

Commit

Permalink
feat: Bulk enrollment now in modal dialog launched from license manag…
Browse files Browse the repository at this point in the history
…e 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]>
  • Loading branch information
5 people authored Nov 11, 2021
1 parent fa921aa commit 5b51d52
Show file tree
Hide file tree
Showing 27 changed files with 457 additions and 1,071 deletions.
36 changes: 36 additions & 0 deletions src/components/BulkEnrollmentPage/BulkEnrollButton.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import React from 'react';
import PropTypes from 'prop-types';

import { Button } from '@edx/paragon';
import { BookOpen } from '@edx/paragon/icons';

/**
* Bulk action button, meant to be used in License management page to initiate Bulk Enrollment Dialog
* @param {object} args Arguments
* @param {array<string>} args.learners set of learners being enrolled
* @param {Function} args.handleEnrollment function to invoke to enroll
* @param {string} args.buttonType type (to distinguish buttons in the dom)
*/
const BulkEnrollButton = ({ learners, handleEnrollment, buttonType }) => (
<Button
variant="primary"
onClick={handleEnrollment}
iconBefore={BookOpen}
disabled={learners.length < 1}
data-testid={buttonType}
>
Enroll ({learners.length })
</Button>
);

BulkEnrollButton.defaultProps = {
buttonType: 'BULK_ENROLL_DEFAULT',
};

BulkEnrollButton.propTypes = {
handleEnrollment: PropTypes.func.isRequired,
learners: PropTypes.arrayOf(PropTypes.string).isRequired,
buttonType: PropTypes.string,
};

export default BulkEnrollButton;
30 changes: 30 additions & 0 deletions src/components/BulkEnrollmentPage/BulkEnrollDialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';

import BulkEnrollmentStepper from './stepper/BulkEnrollmentStepper';
import BulkEnrollContextProvider from './BulkEnrollmentContext';

/**
* @param {object} props Props
* @param {array<string>} props.learners learner email list to enroll
*/
const BulkEnrollDialog = (props) => {
const { learners } = props;
return (
<BulkEnrollContextProvider initialEmailsList={learners}>
<BulkEnrollmentStepper {...props} />
</BulkEnrollContextProvider>
);
};

const mapStateToProps = state => ({
enterpriseSlug: state.portalConfiguration.enterpriseSlug,
enterpriseId: state.portalConfiguration.enterpriseId,
});

BulkEnrollDialog.propTypes = {
learners: PropTypes.arrayOf(PropTypes.string).isRequired,
};

export default connect(mapStateToProps)(BulkEnrollDialog);
28 changes: 3 additions & 25 deletions src/components/BulkEnrollmentPage/BulkEnrollment.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

.card-body {
@extend .py-2;
@extend .px-3
@extend .px-3;
}

.list-item {
Expand Down Expand Up @@ -62,28 +62,6 @@
}
}

.popover {
max-width: 400px;
.bulk-enroll-modal {
min-height: 83vh;
}

.popover .popover-header {
border-bottom: 2px solid $gray-100;
font-weight: 700;
font-size: 15px;
padding-left: 0;
margin: 0 16px 0 16px;
}

.popover-body {
padding: 8px 16px 16px 16px;
}

.popover-body p {
margin: 0 0 8px 0;
}

.popover-body hr {
width: 30%;
border-top: 2px solid $gray-100;
margin: 8px 0 8px 0;
}
17 changes: 15 additions & 2 deletions src/components/BulkEnrollmentPage/BulkEnrollmentContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,17 @@ import selectedRowsReducer from './data/reducer';

export const BulkEnrollContext = createContext({});

const BulkEnrollContextProvider = ({ children }) => {
const BulkEnrollContextProvider = ({ children, initialEmailsList }) => {
const [selectedCourses, coursesDispatch] = useReducer(selectedRowsReducer, []);
const [selectedEmails, emailsDispatch] = useReducer(selectedRowsReducer, []);
// this format is to make this consistent with the format used by ReviewStep components
// similar to DataTable row objects, but not exactly
const formattedEmailsList = initialEmailsList.map(email => ({
id: email,
values: {
userEmail: email,
},
}));
const [selectedEmails, emailsDispatch] = useReducer(selectedRowsReducer, formattedEmailsList);
const [selectedSubscription, setSelectedSubscription] = useState({});

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

BulkEnrollContextProvider.defaultProps = {
initialEmailsList: [],
};

BulkEnrollContextProvider.propTypes = {
children: PropTypes.node.isRequired,
initialEmailsList: PropTypes.arrayOf(PropTypes.string),
};

export default BulkEnrollContextProvider;
50 changes: 50 additions & 0 deletions src/components/BulkEnrollmentPage/BulkEnrollmentWarningModal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';

import {
ActionRow, AlertModal, Button, Icon,
} from '@edx/paragon';
import { Error } from '@edx/paragon/icons';

import BulkEnrollButton from './BulkEnrollButton';

const BulkEnrollWarningModal = ({
learners, isDialogOpen, onClose, onEnroll,
}) => (
<AlertModal
title={(
<>
<Icon className={classNames('enroll-header', 'mr-1')} src={Error} />Revoked Learners Selected
</>
)}
isOpen={isDialogOpen}
footerNode={(
<ActionRow>
<Button variant="link" onClick={onClose}>Close</Button>
<BulkEnrollButton
learners={learners}
handleEnrollment={onEnroll}
buttonType="ENROLL_BTN_IN_WARNING_MODAL"
/>
</ActionRow>
)}
>
Any learners with revoked licenses are not included. Click &quot;Enroll&quot; to enroll
active and pending learners only
</AlertModal>
);

BulkEnrollWarningModal.defaultProps = {
learners: [],
isDialogOpen: false,
};

BulkEnrollWarningModal.propTypes = {
isDialogOpen: PropTypes.bool,
learners: PropTypes.arrayOf(PropTypes.shape({})),
onEnroll: PropTypes.func.isRequired,
onClose: PropTypes.func.isRequired,
};

export default BulkEnrollWarningModal;
71 changes: 0 additions & 71 deletions src/components/BulkEnrollmentPage/CourseSearch.jsx

This file was deleted.

44 changes: 0 additions & 44 deletions src/components/BulkEnrollmentPage/CourseSearch.test.jsx

This file was deleted.

69 changes: 0 additions & 69 deletions src/components/BulkEnrollmentPage/index.jsx

This file was deleted.

Loading

0 comments on commit 5b51d52

Please sign in to comment.