diff --git a/src/users/UserPage.jsx b/src/users/UserPage.jsx index 82e71cf60..13f3beca9 100644 --- a/src/users/UserPage.jsx +++ b/src/users/UserPage.jsx @@ -11,7 +11,7 @@ import UserMessagesContext from '../user-messages/UserMessagesContext'; import { isEmail, isValidUsername } from '../utils/index'; import { getAllUserData } from './data/api'; import Enrollments from './Enrollments'; -import Entitlements from './Entitlements'; +import Entitlements from './entitlements/Entitlements'; import UserSearch from './UserSearch'; import UserSummary from './UserSummary'; diff --git a/src/users/data/api.js b/src/users/data/api.js index 436915fc2..dfb7ac7a2 100644 --- a/src/users/data/api.js +++ b/src/users/data/api.js @@ -221,24 +221,11 @@ export async function getCourseData(courseUUID) { } export async function patchEntitlement({ - uuid, - action, - unenrolledRun = null, - comments = null, + uuid, requestData, }) { try { const { data } = await getAuthenticatedHttpClient().patch( - `${getConfig().LMS_BASE_URL}/api/entitlements/v1/entitlements/${uuid}/`, - { - expired_at: null, - support_details: [ - { - unenrolled_run: unenrolledRun, - action, - comments, - }, - ], - }, + `${getConfig().LMS_BASE_URL}/api/entitlements/v1/entitlements/${uuid}/`, requestData, ); return data; } catch (error) { @@ -265,27 +252,11 @@ export async function patchEntitlement({ } export async function postEntitlement({ - user, - courseUuid, - mode, - action, - comments = null, + requestData, }) { try { const { data } = await getAuthenticatedHttpClient().post( - `${getConfig().LMS_BASE_URL}/api/entitlements/v1/entitlements/`, - { - course_uuid: courseUuid, - user, - mode, - refund_locked: true, - support_details: [ - { - action, - comments, - }, - ], - }, + `${getConfig().LMS_BASE_URL}/api/entitlements/v1/entitlements/`, requestData, ); return data; } catch (error) { diff --git a/src/users/EntitlementForm.jsx b/src/users/entitlements/CreateEntitlementForm.jsx similarity index 56% rename from src/users/EntitlementForm.jsx rename to src/users/entitlements/CreateEntitlementForm.jsx index 934489ab3..5ff351a8c 100644 --- a/src/users/EntitlementForm.jsx +++ b/src/users/entitlements/CreateEntitlementForm.jsx @@ -5,15 +5,13 @@ import { } from '@edx/paragon'; import classNames from 'classnames'; -import UserMessagesContext from '../user-messages/UserMessagesContext'; -import AlertList from '../user-messages/AlertList'; -import { postEntitlement, patchEntitlement } from './data/api'; +import UserMessagesContext from '../../user-messages/UserMessagesContext'; +import AlertList from '../../user-messages/AlertList'; +import { postEntitlement } from '../data/api'; +import { CREATE } from './EntitlementActions'; +import { EntitlementPropTypes, EntitlementDefaultProps } from './PropTypes'; -export const REISSUE = 'reissue'; -export const CREATE = 'create'; - -export default function EntitlementForm({ - formType, +export default function CreateEntitlementForm({ entitlement, changeHandler, closeHandler, @@ -27,50 +25,36 @@ export default function EntitlementForm({ const submit = useCallback(() => { clear('entitlements'); - if (formType === CREATE) { - postEntitlement({ + postEntitlement({ + requestData: { + course_uuid: courseUuid, user, - courseUuid, mode, - action: CREATE, - comments, - }).then((result) => { - if (result.errors !== undefined) { - result.errors.forEach(error => add(error)); - } else { - changeHandler(); - } - }); - } else if (formType === REISSUE) { - patchEntitlement({ - uuid: entitlement.uuid, - action: REISSUE, - unenrolledRun: entitlement.enrollmentCourseRun, - comments, - }).then((result) => { - if (result.errors !== undefined) { - result.errors.forEach(error => add(error)); - } else { - changeHandler(); - } - }); - } + refund_locked: true, + support_details: [{ + action: CREATE, + comments, + }], + }, + }).then((result) => { + if (result.errors !== undefined) { + result.errors.forEach(error => add(error)); + } else { + changeHandler(); + } + }); }); - const isReissue = formType === REISSUE; - const title = isReissue ? 'Re-issue Entitlement' : 'Create Entitlement'; - return (
-

{title}

+

Create Entitlement

All fields are required
Mode + ); + } if (formType === EXPIRE) { + return ( + + ); + } if (formType === REISSUE) { + return ( + + ); + } +} + +EntitlementForm.propTypes = { + formType: PropTypes.string.isRequired, + entitlement: EntitlementPropTypes, + user: PropTypes.string.isRequired, + changeHandler: PropTypes.func.isRequired, + closeHandler: PropTypes.func.isRequired, + forwardedRef: PropTypes.shape({ current: PropTypes.instanceOf(Element) }), +}; + +EntitlementForm.defaultProps = { + entitlement: EntitlementDefaultProps, + forwardedRef: null, +}; diff --git a/src/users/Entitlements.jsx b/src/users/entitlements/Entitlements.jsx similarity index 70% rename from src/users/Entitlements.jsx rename to src/users/entitlements/Entitlements.jsx index 136bed239..99cd8c7cf 100644 --- a/src/users/Entitlements.jsx +++ b/src/users/entitlements/Entitlements.jsx @@ -7,13 +7,14 @@ import { Button, Collapsible, TransitionReplace, } from '@edx/paragon'; import { camelCaseObject, getConfig } from '@edx/frontend-platform'; -import EntitlementForm, { CREATE, REISSUE } from './EntitlementForm'; -import sort from './sort'; -import Table from '../Table'; -import CourseSummary from './CourseSummary'; -import { getCourseData } from './data/api'; -import UserMessagesContext from '../user-messages/UserMessagesContext'; -import formatDate from '../dates/formatDate'; +import EntitlementForm from './EntitlementForm'; +import { CREATE, REISSUE, EXPIRE } from './EntitlementActions'; +import sort from '../sort'; +import Table from '../../Table'; +import CourseSummary from '../CourseSummary'; +import { getCourseData } from '../data/api'; +import UserMessagesContext from '../../user-messages/UserMessagesContext'; +import formatDate from '../../dates/formatDate'; export default function Entitlements({ data, changeHandler, user, expanded, @@ -22,7 +23,7 @@ export default function Entitlements({ const [sortColumn, setSortColumn] = useState('created'); const [sortDirection, setSortDirection] = useState('desc'); const [formType, setFormType] = useState(null); - const [entitlementToReissue, setEntitlementToReissue] = useState(undefined); + const [userEntitlement, setUserEntitlement] = useState(undefined); const [courseSummaryUUID, setCourseSummaryUUID] = useState(null); const [courseSummaryData, setCourseSummaryData] = useState(null); const [courseSummaryErrors, setCourseSummaryErrors] = useState(false); @@ -65,76 +66,90 @@ export default function Entitlements({ if (data === null) { return []; } - return data.results.map(result => ({ + return data.results.map(entitlement => ({ courseUuid: { displayValue: ( ), - value: result.courseUuid, + value: entitlement.courseUuid, }, mode: { - value: result.mode, + value: entitlement.mode, }, enrollment: { - displayValue: (result.enrollmentCourseRun ? ( + displayValue: (entitlement.enrollmentCourseRun ? ( - {result.enrollmentCourseRun} + {entitlement.enrollmentCourseRun} ) : 'Course Run Not Selected'), - value: result.enrollmentCourseRun, + value: entitlement.enrollmentCourseRun, }, expiredAt: { - displayValue: formatDate(result.expiredAt), - value: result.expiredAt, + displayValue: formatDate(entitlement.expiredAt), + value: entitlement.expiredAt, }, created: { - displayValue: formatDate(result.created), - value: result.created, + displayValue: formatDate(entitlement.created), + value: entitlement.created, }, modified: { - displayValue: formatDate(result.modified), - value: result.modified, + displayValue: formatDate(entitlement.modified), + value: entitlement.modified, }, orderNumber: { displayValue: ( - {result.orderNumber} + {entitlement.orderNumber} ), - value: result.orderNumber, + value: entitlement.orderNumber, }, actions: { displayValue: ( - +
+ + +
), value: 'Resissue', }, @@ -173,7 +188,7 @@ export default function Entitlements({ label: 'Order', key: 'orderNumber', columnSortable: true, onSort: () => setSort('orderNumber'), width: 'col-3', }, { - label: 'Actions', key: 'actions', columnSortable: true, onSort: () => {}, width: 'col-3', + label: 'Actions', key: 'actions', columnSortable: true, onSort: () => { }, width: 'col-3', }, ]; @@ -183,17 +198,17 @@ export default function Entitlements({
{!formType && ( - + )}
@@ -201,14 +216,14 @@ export default function Entitlements({ {}} + submitHandler={() => { }} closeHandler={() => setFormType(null)} forwardedRef={formRef} /> - ) : () } + ) : ()} {courseSummaryUUID !== null ? ( @@ -222,7 +237,7 @@ export default function Entitlements({ errors={courseSummaryErrors} forwardedRef={summaryRef} /> - ) : () } + ) : ()} { + const now = new Date().toISOString(); + clear('entitlements'); + patchEntitlement({ + uuid: entitlement.uuid, + requestData: makeRequestData({ + expiredAt: now, + enrollmentCourseRun: entitlement.enrollmentCourseRun, + action: EXPIRE, + comments, + }), + }).then((result) => { + if (result.errors !== undefined) { + result.errors.forEach(error => add(error)); + } else { + changeHandler(); + } + }); + }); + + return ( +
+ + +

Expire Entitlement

+
All fields are required
+
+ + setCourseUuid(event.target.value)} + disabled + /> +
+
+ + setMode(event.target.value)} + disabled + /> +
+
+ + setComments(event.target.value)} + ref={forwardedRef} + /> +
+
+ + +
+ +
+ ); +} + +ExpireEntitlementForm.propTypes = { + entitlement: EntitlementPropTypes, + changeHandler: PropTypes.func.isRequired, + closeHandler: PropTypes.func.isRequired, + forwardedRef: PropTypes.shape({ current: PropTypes.instanceOf(Element) }), +}; + +ExpireEntitlementForm.defaultProps = { + entitlement: EntitlementDefaultProps, + forwardedRef: null, +}; diff --git a/src/users/entitlements/PropTypes.jsx b/src/users/entitlements/PropTypes.jsx new file mode 100644 index 000000000..bcc1d10a4 --- /dev/null +++ b/src/users/entitlements/PropTypes.jsx @@ -0,0 +1,31 @@ +import PropTypes from 'prop-types'; + +export const EntitlementPropTypes = PropTypes.shape({ + uuid: PropTypes.string.isRequired, + courseUuid: PropTypes.string.isRequired, + enrollmentCourseRun: PropTypes.string, + created: PropTypes.string.isRequired, + modified: PropTypes.string.isRequired, + expiredAt: PropTypes.string, + mode: PropTypes.string.isRequired, + orderNumber: PropTypes.string, + supportDetails: PropTypes.arrayOf(PropTypes.shape({ + supportUser: PropTypes.string, + action: PropTypes.string, + comments: PropTypes.string, + unenrolledRun: PropTypes.string, + })), + user: PropTypes.string.isRequired, +}); + +export const EntitlementDefaultProps = { + uuid: '', + courseUuid: '', + created: '', + modified: '', + expiredAt: '', + mode: 'verified', + orderNumber: '', + supportDetails: [], + user: '', +}; diff --git a/src/users/entitlements/ReissueEntitlementForm.jsx b/src/users/entitlements/ReissueEntitlementForm.jsx new file mode 100644 index 000000000..62c989bee --- /dev/null +++ b/src/users/entitlements/ReissueEntitlementForm.jsx @@ -0,0 +1,115 @@ +import React, { useCallback, useState, useContext } from 'react'; +import PropTypes from 'prop-types'; +import { + Button, Input, +} from '@edx/paragon'; +import classNames from 'classnames'; + +import UserMessagesContext from '../../user-messages/UserMessagesContext'; +import AlertList from '../../user-messages/AlertList'; +import { patchEntitlement } from '../data/api'; +import { REISSUE } from './EntitlementActions'; +import { EntitlementPropTypes, EntitlementDefaultProps } from './PropTypes'; +import makeRequestData from './utils'; + +export default function ReissueEntitlementForm({ + entitlement, + changeHandler, + closeHandler, + forwardedRef, +}) { + const [courseUuid, setCourseUuid] = useState(entitlement.courseUuid); + const [mode, setMode] = useState(entitlement.mode); + const [comments, setComments] = useState(''); + const { add, clear } = useContext(UserMessagesContext); + + const submit = useCallback(() => { + clear('entitlements'); + patchEntitlement({ + uuid: entitlement.uuid, + requestData: makeRequestData({ + enrollmentCourseRun: entitlement.enrollmentCourseRun, + action: REISSUE, + comments, + }), + }).then((result) => { + if (result.errors !== undefined) { + result.errors.forEach(error => add(error)); + } else { + changeHandler(); + } + }); + }); + + return ( +
+
+ +

Reissue Entitlement

+
All fields are required
+
+ + setCourseUuid(event.target.value)} + disabled + /> +
+
+ + setMode(event.target.value)} + disabled + /> +
+
+ + setComments(event.target.value)} + ref={forwardedRef} + /> +
+
+ + +
+ +
+ ); +} + +ReissueEntitlementForm.propTypes = { + entitlement: EntitlementPropTypes, + changeHandler: PropTypes.func.isRequired, + closeHandler: PropTypes.func.isRequired, + forwardedRef: PropTypes.shape({ current: PropTypes.instanceOf(Element) }), +}; + +ReissueEntitlementForm.defaultProps = { + entitlement: EntitlementDefaultProps, + forwardedRef: null, +}; diff --git a/src/users/entitlements/utils.js b/src/users/entitlements/utils.js new file mode 100644 index 000000000..20beb255a --- /dev/null +++ b/src/users/entitlements/utils.js @@ -0,0 +1,12 @@ +export default function makeRequestData({ + expiredAt = null, enrollmentCourseRun, action, comments, +}) { + return { + expired_at: expiredAt, + support_details: [{ + unenrolled_run: enrollmentCourseRun, + action, + comments, + }], + }; +}