From 37e853f212028077e6cc308d5140229871031776 Mon Sep 17 00:00:00 2001 From: Lael Birch Date: Fri, 19 Mar 2021 09:50:09 -0400 Subject: [PATCH] Create linked learners when assigning codes (#506) --- src/components/CodeAssignmentModal/index.jsx | 27 +++++++++-- src/containers/CodeAssignmentModal/index.jsx | 11 +++++ .../actions/createPendingEnterpriseUsers.js | 46 +++++++++++++++++++ src/data/constants/createPendingEntUser.js | 9 ++++ src/data/services/LmsApiService.js | 6 +++ 5 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 src/data/actions/createPendingEnterpriseUsers.js create mode 100644 src/data/constants/createPendingEntUser.js diff --git a/src/components/CodeAssignmentModal/index.jsx b/src/components/CodeAssignmentModal/index.jsx index b59e3cc7f2..d2a8e199ab 100644 --- a/src/components/CodeAssignmentModal/index.jsx +++ b/src/components/CodeAssignmentModal/index.jsx @@ -310,8 +310,10 @@ class BaseCodeAssignmentModal extends React.Component { hasAllCodesSelected, }, sendCodeAssignment, + createPendingEnterpriseUsers, enableLearnerPortal, enterpriseSlug, + enterpriseUuid, } = this.props; this.setMode('assign'); @@ -336,10 +338,11 @@ class BaseCodeAssignmentModal extends React.Component { options.template_id = formData['template-id']; } + const hasTextAreaEmails = !!formData[EMAIL_ADDRESS_TEXT_FORM_DATA]; + const emails = hasTextAreaEmails ? formData[EMAIL_ADDRESS_TEXT_FORM_DATA].split(/\r\n|\n/) : formData[EMAIL_ADDRESS_CSV_FORM_DATA]; + const { validEmails } = this.validateEmailAddresses(emails, !hasTextAreaEmails); + if (isBulkAssign) { - const hasTextAreaEmails = !!formData[EMAIL_ADDRESS_TEXT_FORM_DATA]; - const emails = hasTextAreaEmails ? formData[EMAIL_ADDRESS_TEXT_FORM_DATA].split(/\r\n|\n/) : formData[EMAIL_ADDRESS_CSV_FORM_DATA]; - const { validEmails } = this.validateEmailAddresses(emails, !hasTextAreaEmails); options.emails = validEmails; // Only includes `codes` in `options` if not all codes are selected. @@ -351,7 +354,21 @@ class BaseCodeAssignmentModal extends React.Component { options.codes = [code.code]; } - return sendCodeAssignment(couponId, options) + let pendingEnterpriseUserData; + if (hasTextAreaEmails) { + pendingEnterpriseUserData = validEmails.map((email) => ({ + user_email: email, + enterprise_customer: enterpriseUuid, + })); + } else { + pendingEnterpriseUserData = { + user_email: formData['email-address'], + enterprise_customer: enterpriseUuid, + }; + } + + return createPendingEnterpriseUsers(pendingEnterpriseUserData) + .then(() => sendCodeAssignment(couponId, options)) .then((response) => { this.props.onSuccess(response); }) @@ -547,6 +564,7 @@ BaseCodeAssignmentModal.defaultProps = { BaseCodeAssignmentModal.propTypes = { // props from redux enterpriseSlug: PropTypes.string.isRequired, + enterpriseUuid: PropTypes.string.isRequired, currentEmail: PropTypes.string, enableLearnerPortal: PropTypes.bool.isRequired, // props From redux-form @@ -562,6 +580,7 @@ BaseCodeAssignmentModal.propTypes = { onClose: PropTypes.func.isRequired, onSuccess: PropTypes.func.isRequired, sendCodeAssignment: PropTypes.func.isRequired, + createPendingEnterpriseUsers: PropTypes.func.isRequired, setEmailAddress: PropTypes.func.isRequired, couponDetailsTable: PropTypes.shape({ data: PropTypes.shape({ diff --git a/src/containers/CodeAssignmentModal/index.jsx b/src/containers/CodeAssignmentModal/index.jsx index 2fac4094bb..c991be1ffd 100644 --- a/src/containers/CodeAssignmentModal/index.jsx +++ b/src/containers/CodeAssignmentModal/index.jsx @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import CodeAssignmentModal from '../../components/CodeAssignmentModal'; import sendCodeAssignment from '../../data/actions/codeAssignment'; +import createPendingEnterpriseUsers from '../../data/actions/createPendingEnterpriseUsers'; import { EMAIL_TEMPLATE_SOURCE_NEW_EMAIL } from '../../data/constants/emailTemplate'; import { setEmailAddress } from '../../data/actions/emailTemplate'; @@ -18,6 +19,9 @@ const mapStateToProps = (state) => { couponDetailsTable: state.table['coupon-details'], initialValues, enableReinitialize: true, + enterpriseSlug: state.portalConfiguration.enterpriseSlug, + enterpriseUuid: state.portalConfiguration.enterpriseId, + enableLearnerPortal: state.portalConfiguration.enableLearnerPortal, }; }; @@ -30,6 +34,13 @@ const mapDispatchToProps = dispatch => ({ onError: (error) => { reject(error); }, })); }), + createPendingEnterpriseUsers: (users) => new Promise((resolve, reject) => { + dispatch(createPendingEnterpriseUsers({ + users, + onSuccess: (response) => { resolve(response); }, + onError: (error) => { reject(error); }, + })); + }), setEmailAddress: (emailAddress, emailType) => dispatch(setEmailAddress(emailAddress, emailType)), }); diff --git a/src/data/actions/createPendingEnterpriseUsers.js b/src/data/actions/createPendingEnterpriseUsers.js new file mode 100644 index 0000000000..4049a8067f --- /dev/null +++ b/src/data/actions/createPendingEnterpriseUsers.js @@ -0,0 +1,46 @@ +import { + PENDING_ENT_USER_REQUEST, + PENDING_ENT_USER_SUCCESS, + PENDING_ENT_USER_FAILURE, +} from '../constants/createPendingEntUser'; + +import LmsApiService from '../services/LmsApiService'; + +const createPendingUsersRequest = () => ({ + type: PENDING_ENT_USER_REQUEST, +}); + +const createPendingUsersSuccess = data => ({ + type: PENDING_ENT_USER_SUCCESS, + payload: { + data, + }, +}); + +const createPendingUsersFailure = error => ({ + type: PENDING_ENT_USER_FAILURE, + payload: { + error, + }, +}); + +const createPendingEnterpriseUsers = ({ + users, + onSuccess = () => {}, + onError = () => {}, +}) => ( + (dispatch) => { + dispatch(createPendingUsersRequest()); + return LmsApiService.createPendingEnterpriseUsers(users) + .then((response) => { + dispatch(createPendingUsersSuccess(response.data)); + onSuccess(response.data); + }) + .catch((error) => { + dispatch(createPendingUsersFailure(error)); + onError(error); + }); + } +); + +export default createPendingEnterpriseUsers; diff --git a/src/data/constants/createPendingEntUser.js b/src/data/constants/createPendingEntUser.js new file mode 100644 index 0000000000..ca50486b01 --- /dev/null +++ b/src/data/constants/createPendingEntUser.js @@ -0,0 +1,9 @@ +const PENDING_ENT_USER_REQUEST = 'PENDING_ENT_USER_REQUEST'; +const PENDING_ENT_USER_SUCCESS = 'PENDING_ENT_USER_SUCCESS'; +const PENDING_ENT_USER_FAILURE = 'PENDING_ENT_USER_FAILURE'; + +export { + PENDING_ENT_USER_REQUEST, + PENDING_ENT_USER_SUCCESS, + PENDING_ENT_USER_FAILURE, +}; diff --git a/src/data/services/LmsApiService.js b/src/data/services/LmsApiService.js index a0f0c37a5e..66c1a91ab9 100644 --- a/src/data/services/LmsApiService.js +++ b/src/data/services/LmsApiService.js @@ -18,6 +18,8 @@ class LmsApiService { static lmsIntegrationUrl = `${LmsApiService.baseUrl}/integrated_channels/api/v1`; + static createPendingUsersUrl = `${LmsApiService.baseUrl}/enterprise/api/v1/pending-enterprise-learner/` + static fetchCourseOutline(courseId) { const options = { course_id: courseId, @@ -196,6 +198,10 @@ class LmsApiService { const url = `${LmsApiService.enterpriseCustomerUrl}${enterpriseId}/enterprise_learners/`; return LmsApiService.apiClient().post(url, options); } + + static createPendingEnterpriseUsers(formData) { + return LmsApiService.apiClient().post(LmsApiService.createPendingUsersUrl, formData); + } } export default LmsApiService;