From 2a50205451ee01b6465e922f783e7d845fc53915 Mon Sep 17 00:00:00 2001 From: Ali Nawaz Date: Mon, 22 Apr 2024 22:28:11 +0500 Subject: [PATCH] feat: add Restriction Type in EditCoursePage --- .../EditCoursePage/CollapsibleCourseRun.jsx | 13 +- .../CollapsibleCourseRun.test.jsx | 30 + .../EditCoursePage/EditCoursePage.test.jsx | 46 +- .../CollapsibleCourseRun.test.jsx.snap | 1426 +++++++++++++++++ .../EditCoursePage.test.jsx.snap | 18 + src/components/EditCoursePage/index.jsx | 2 + src/helpText.jsx | 10 + src/utils/index.js | 7 + 8 files changed, 1549 insertions(+), 3 deletions(-) diff --git a/src/components/EditCoursePage/CollapsibleCourseRun.jsx b/src/components/EditCoursePage/CollapsibleCourseRun.jsx index 3104479e8..f58677c4e 100644 --- a/src/components/EditCoursePage/CollapsibleCourseRun.jsx +++ b/src/components/EditCoursePage/CollapsibleCourseRun.jsx @@ -19,6 +19,7 @@ import FieldLabel from '../FieldLabel'; import { courseRunIsArchived, localTimeZone, formatDate, isSafari, getDateWithDashes, getDateWithSlashes, isNonExemptChanged, isPristine, hasMastersTrack, jsonDeepEqual, utcTimeZone, isExternalCourse, + restrictionTypeOptions, } from '../../utils'; import Pill from '../Pill'; import RenderInputTextField from '../RenderInputTextField'; @@ -33,7 +34,7 @@ import { PUBLISHED, DATE_INPUT_PATTERN, FORMAT_DATE_MATCHER, NORMALIZE_DATE_MATCHER, REVIEWED, } from '../../data/constants'; import { - dateEditHelp, runTypeHelp, pacingEditHelp, publishDateHelp, courseRunVariantIdHelp, + dateEditHelp, runTypeHelp, pacingEditHelp, publishDateHelp, courseRunVariantIdHelp, courseRunRestrictionTypeHelp, } from '../../helpText'; import RichEditor from '../RichEditor'; import ListField from '../ListField'; @@ -301,6 +302,16 @@ class CollapsibleCourseRun extends React.Component { optional /> )} + {isExternalCourse(productSource, courseType) && ( + } + disabled={disabled} + props={{ name: `${courseId}.restriction_type` }} + /> + )} {/* TODO this should be refactored when paragon supports safari */} {/* text inputs for safari */} {isSafari diff --git a/src/components/EditCoursePage/CollapsibleCourseRun.test.jsx b/src/components/EditCoursePage/CollapsibleCourseRun.test.jsx index edc7956e8..5eb2945fd 100644 --- a/src/components/EditCoursePage/CollapsibleCourseRun.test.jsx +++ b/src/components/EditCoursePage/CollapsibleCourseRun.test.jsx @@ -138,6 +138,36 @@ describe('Collapsible Course Run', () => { expect(variantIdField.exists()).toBe(true); expect(shallowToJson(component)).toMatchSnapshot(); }); + it.each(['custom-b2c', ''])('renders correctly restriction type field for external course\'s course run', (restrictionType) => { + const courseInfo = { + data: { + product_source: { + slug: 'test-source', + name: 'Test Source', + description: 'Test Source Description', + }, + course_type: EXECUTIVE_EDUCATION_SLUG, + }, + }; + const courseRun = { ...unpublishedCourseRun, restriction_type: restrictionType }; + const component = shallow( + , + ); + const restrictionTypeField = component.find('Field[name="test-course.restriction_type"]'); + expect(restrictionTypeField.exists()).toBe(true); + expect(shallowToJson(component)).toMatchSnapshot(); + }); it('renders correctly with external key field enabled', () => { const runTypeModes = { diff --git a/src/components/EditCoursePage/EditCoursePage.test.jsx b/src/components/EditCoursePage/EditCoursePage.test.jsx index ae5710204..5bdd19cdf 100644 --- a/src/components/EditCoursePage/EditCoursePage.test.jsx +++ b/src/components/EditCoursePage/EditCoursePage.test.jsx @@ -6,7 +6,7 @@ import { shallowToJson } from 'enzyme-to-json'; import configureStore from 'redux-mock-store'; import { Alert } from '@edx/paragon'; import { IntlProvider } from '@edx/frontend-platform/i18n'; - +import { createStore } from 'redux'; import EditCoursePage from './index'; import ConfirmationModal from '../ConfirmationModal'; @@ -15,6 +15,7 @@ import { PUBLISHED, REVIEW_BY_INTERNAL, REVIEW_BY_LEGAL, UNPUBLISHED, EXECUTIVE_EDUCATION_SLUG, } from '../../data/constants'; import { courseOptions, courseRunOptions } from '../../data/constants/testData'; +import createRootReducer from '../../data/reducers'; import { jsonDeepCopy } from '../../utils'; // Need to mock the Editor as we don't want to test TinyMCE @@ -27,6 +28,7 @@ describe('EditCoursePage', () => { const defaultEnd = '2019-08-14T00:00:00Z'; const defaultUpgradeDeadlineOverride = '2019-09-14T00:00:00Z'; const variantId = '00000000-0000-0000-0000-000000000000'; + const restrictionType = 'custom-b2b-enterprise'; const watchers = ['test@test.com']; const courseInfo = { @@ -64,6 +66,7 @@ describe('EditCoursePage', () => { end: defaultEnd, upgrade_deadline_override: '2019-05-10T00:00:00Z', variant_id: null, + restriction_type: restrictionType, expected_program_type: 'micromasters', expected_program_name: 'Test Program Name', go_live_date: '2019-05-06T00:00:00Z', @@ -89,6 +92,7 @@ describe('EditCoursePage', () => { end: defaultEnd, upgrade_deadline_override: '2019-05-10T00:00:00Z', variant_id: null, + restriction_type: null, expected_program_type: null, expected_program_name: '', go_live_date: '2019-05-06T00:00:00Z', @@ -213,6 +217,42 @@ describe('EditCoursePage', () => { expect(shallowToJson(component)).toMatchSnapshot(); }); + it('renders course run restriction_type correctly for executive education course', () => { + const store = createStore(createRootReducer()); + const courseInfoExecEd = { + ...courseInfo, + data: { + ...courseInfo.data, + product_source: { + slug: 'test-source', + name: 'Test Source', + description: 'Test Source Description', + }, + course_type: EXECUTIVE_EDUCATION_SLUG, + }, + }; + const EditCoursePageWrapper = (props) => ( + + + + + + + + ); + + const wrapper = mount(EditCoursePageWrapper()); + const firstSelect = wrapper.find('select[name="course_runs[0].restriction_type"]'); + expect(firstSelect.props().value).toBe('custom-b2b-enterprise'); + const secondSelect = wrapper.find('select[name="course_runs[1].restriction_type"]'); + expect(secondSelect.props().value).toBe(''); + }); + it('renders page correctly with courseInfo error', () => { const component = shallow( { end: defaultEnd, upgrade_deadline_override: defaultUpgradeDeadlineOverride, variant_id: variantId, + restriction_type: restrictionType, expected_program_type: null, expected_program_name: '', go_live_date: '2019-05-06T00:00:00Z', @@ -474,6 +515,7 @@ describe('EditCoursePage', () => { weeks_to_complete: '100', upgrade_deadline_override: defaultUpgradeDeadlineOverride, variant_id: variantId, + restriction_type: restrictionType, }, { content_language: 'en-us', @@ -496,6 +538,7 @@ describe('EditCoursePage', () => { weeks_to_complete: '100', upgrade_deadline_override: defaultUpgradeDeadlineOverride, variant_id: variantId, + restriction_type: restrictionType, }, ]; @@ -933,7 +976,6 @@ describe('EditCoursePage', () => { .toEqual({}); }, 0); }); - const expectedSendCourseExEdCourses = { additional_information: '

Stuff

', additional_metadata: { diff --git a/src/components/EditCoursePage/__snapshots__/CollapsibleCourseRun.test.jsx.snap b/src/components/EditCoursePage/__snapshots__/CollapsibleCourseRun.test.jsx.snap index 70556203d..c62671e19 100644 --- a/src/components/EditCoursePage/__snapshots__/CollapsibleCourseRun.test.jsx.snap +++ b/src/components/EditCoursePage/__snapshots__/CollapsibleCourseRun.test.jsx.snap @@ -1,5 +1,1389 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Collapsible Course Run renders correctly restriction type field for external course's course run 1`] = ` + + + Course run starting on Jan 01, 2000 - Self Paced + + +
+ + Publish date is Dec 31, 1999 + +
+
+ + Studio URL -  + + edX101+DemoX + + + Copy course key + + } + placement="top" + popperConfig={Object {}} + trigger={ + Array [ + "hover", + "focus", + ] + } + > + + + + + +
+ + } +> +
+ + All fields are required for publication unless otherwise specified. + +
+ +

+ The identifier for a product variant. This is used to link a course run to a product variant for external LOBs (i.e; ExecEd & Bootcamps). +

+ + } + id="test-course.variant_id.label" + optional={false} + text="Variant Id" + /> + } + name="test-course.variant_id" + optional={true} + /> + +

+ The restriction type for custom runs. These runs are only exposed in APIs if the associated restriction type is passed in as a query param +

+ + } + id={null} + optional={false} + text="Restriction Type" + /> + } + name="test-course.restriction_type" + options={ + Array [ + Object { + "label": "--------", + "value": "", + }, + Object { + "label": "Custom Enterprise", + "value": "custom-b2b-enterprise", + }, + Object { + "label": "Custom B2C", + "value": "custom-b2c", + }, + ] + } + props={ + Object { + "name": "test-course.restriction_type", + } + } + /> +
+ +

+ Required Format: yyyy/mm/dd +

+

+ The scheduled date for when the course run will be live and published. +

+

+ To publish as soon as possible, set the publish date to today. Please note that changes may take 48 hours to go live. +

+

+ If you don’t have a publish date yet, set to 1 year in the future. +

+
+ } + id="test-course.go_live_date.label" + optional={false} + text="Publish date" + /> + } + maxLength="10" + name="test-course.go_live_date" + normalize={[Function]} + pattern="20[1-9][0-9]/(0[1-9]|1[012])/(0[1-9]|[12][0-9]|3[01])" + placeholder="yyyy/mm/dd" + required={false} + type="text" + /> + +

+ Course run dates are editable in Studio. +

+

+ Please note that changes in Studio may take up to a business day to be reflected here. For questions, contact your project coordinator. +

+

+ + Edit dates. + + . +

+ + } + name="test-course.start" + timeLabel="Start time (GMT)" + type="text" + /> + +

+ Course run dates are editable in Studio. +

+

+ Please note that changes in Studio may take up to a business day to be reflected here. For questions, contact your project coordinator. +

+

+ + Edit dates. + + . +

+ + } + name="test-course.end" + timeLabel="End time (GMT)" + type="text" + /> + +

+ Course run dates are editable in Studio. +

+

+ Please note that changes in Studio may take up to a business day to be reflected here. For questions, contact your project coordinator. +

+

+ + Edit dates. + + . +

+ + } + name="test-course.upgrade_deadline_override" + timeLabel="Upgrade deadline override time (UTC)" + type="date" + utcTimeZone={true} + /> + +
+ +

+ The enrollment track determines whether a course run offers a paid certificate and what sort of verification is required. +

+

+ + Learn more. + +

+ + } + id="test-course.run_type.label" + optional={false} + text="Course run enrollment track" + /> + } + name="test-course.run_type" + options={ + Array [ + Object { + "label": "Select enrollment track", + "value": "", + }, + Object { + "label": "Verified and Audit", + "value": "4e260c57-24ef-46c1-9a0d-5ec3a30f6b0c", + }, + Object { + "label": "Audit Only", + "value": "cfacfc62-54bd-4e1b-939a-5a94f12fbd8d", + }, + Object { + "label": "Masters, Verified, and Audit", + "value": "00000000-0000-4000-0000-000000000000", + }, + ] + } + required={true} + /> + +

+ Course pacing is editable in Studio. +

+

+ Please note that changes in Studio may take up to a business day to be reflected here. For questions, contact your project coordinator. +

+

+ + Edit course pacing. + + . +

+ + } + id="test-course.pacing_type.label" + optional={false} + text="Course pacing" + /> + } + name="test-course.pacing_type" + options={ + Array [ + Object { + "label": "Self-paced", + "value": "self_paced", + }, + ] + } + type="text" + /> + +

+ The primary instructor or instructors for the course. +

+

+ The order that instructors are listed here is the same order they will be displayed on course pages. You can drag and drop to reorder instructors. +

+ + } + id="test-course.staff.label" + optional={true} + text="Staff" + /> + +
+
+ +

+ The minimum number of hours per week the learner should expect to spend on the course. +

+
+ } + id="test-course.min_effort.label" + optional={false} + text="Minimum effort" + /> + } + name="test-course.min_effort" + required={false} + type="number" + /> +
+
+ +

+ The maximum number of hours per week the learner should expect to spend on the course. +

+
+ } + id="test-course.max_effort.label" + optional={false} + text="Maximum effort" + /> + } + name="test-course.max_effort" + required={false} + type="number" + /> + + + +

+ The estimated number of weeks the learner should expect to spend on the course, rounded to the nearest whole number. +

+ + } + id="test-course.weeks_to_complete.label" + optional={false} + text="Length" + /> + } + name="test-course.weeks_to_complete" + required={false} + type="number" + /> + + } + name="test-course.content_language" + options={ + Array [ + Object { + "label": "Arabic - United Arab Emirates", + "value": "ar-ae", + }, + ] + } + required={false} + type="text" + /> + + + +

+ If this Course Run will potentially be part of a Program, please set the expected program type here. +

+ + } + id="test-course.expected_program_type.label" + optional={true} + text="Expected Program Type" + /> + } + name="test-course.expected_program_type" + type="text" + /> + +

+ If this Course Run will potentially be part of a Program, please set the expected program name here. +

+ + } + id="test-course.expected_program_name.label" + optional={true} + text="Expected Program Name" + /> + } + name="test-course.expected_program_name" + type="text" + /> +
+ +

+ Course embargo status for OFAC is managed internally, please contact support with questions. +

+
+ } + id="ofac-notice-label" + optional={false} + text="Course Embargo (OFAC) Restriction text added to the FAQ section" + /> +
+ No +
+ + +
+`; + +exports[`Collapsible Course Run renders correctly restriction type field for external course's course run 2`] = ` + + + Course run starting on Jan 01, 2000 - Self Paced + + +
+ + Publish date is Dec 31, 1999 + +
+
+ + Studio URL -  + + edX101+DemoX + + + Copy course key + + } + placement="top" + popperConfig={Object {}} + trigger={ + Array [ + "hover", + "focus", + ] + } + > + + + + + +
+ + } +> +
+ + All fields are required for publication unless otherwise specified. + +
+ +

+ The identifier for a product variant. This is used to link a course run to a product variant for external LOBs (i.e; ExecEd & Bootcamps). +

+ + } + id="test-course.variant_id.label" + optional={false} + text="Variant Id" + /> + } + name="test-course.variant_id" + optional={true} + /> + +

+ The restriction type for custom runs. These runs are only exposed in APIs if the associated restriction type is passed in as a query param +

+ + } + id={null} + optional={false} + text="Restriction Type" + /> + } + name="test-course.restriction_type" + options={ + Array [ + Object { + "label": "--------", + "value": "", + }, + Object { + "label": "Custom Enterprise", + "value": "custom-b2b-enterprise", + }, + Object { + "label": "Custom B2C", + "value": "custom-b2c", + }, + ] + } + props={ + Object { + "name": "test-course.restriction_type", + } + } + /> +
+ +

+ Required Format: yyyy/mm/dd +

+

+ The scheduled date for when the course run will be live and published. +

+

+ To publish as soon as possible, set the publish date to today. Please note that changes may take 48 hours to go live. +

+

+ If you don’t have a publish date yet, set to 1 year in the future. +

+
+ } + id="test-course.go_live_date.label" + optional={false} + text="Publish date" + /> + } + maxLength="10" + name="test-course.go_live_date" + normalize={[Function]} + pattern="20[1-9][0-9]/(0[1-9]|1[012])/(0[1-9]|[12][0-9]|3[01])" + placeholder="yyyy/mm/dd" + required={false} + type="text" + /> + +

+ Course run dates are editable in Studio. +

+

+ Please note that changes in Studio may take up to a business day to be reflected here. For questions, contact your project coordinator. +

+

+ + Edit dates. + + . +

+ + } + name="test-course.start" + timeLabel="Start time (GMT)" + type="text" + /> + +

+ Course run dates are editable in Studio. +

+

+ Please note that changes in Studio may take up to a business day to be reflected here. For questions, contact your project coordinator. +

+

+ + Edit dates. + + . +

+ + } + name="test-course.end" + timeLabel="End time (GMT)" + type="text" + /> + +

+ Course run dates are editable in Studio. +

+

+ Please note that changes in Studio may take up to a business day to be reflected here. For questions, contact your project coordinator. +

+

+ + Edit dates. + + . +

+ + } + name="test-course.upgrade_deadline_override" + timeLabel="Upgrade deadline override time (UTC)" + type="date" + utcTimeZone={true} + /> + +
+ +

+ The enrollment track determines whether a course run offers a paid certificate and what sort of verification is required. +

+

+ + Learn more. + +

+ + } + id="test-course.run_type.label" + optional={false} + text="Course run enrollment track" + /> + } + name="test-course.run_type" + options={ + Array [ + Object { + "label": "Select enrollment track", + "value": "", + }, + Object { + "label": "Verified and Audit", + "value": "4e260c57-24ef-46c1-9a0d-5ec3a30f6b0c", + }, + Object { + "label": "Audit Only", + "value": "cfacfc62-54bd-4e1b-939a-5a94f12fbd8d", + }, + Object { + "label": "Masters, Verified, and Audit", + "value": "00000000-0000-4000-0000-000000000000", + }, + ] + } + required={true} + /> + +

+ Course pacing is editable in Studio. +

+

+ Please note that changes in Studio may take up to a business day to be reflected here. For questions, contact your project coordinator. +

+

+ + Edit course pacing. + + . +

+ + } + id="test-course.pacing_type.label" + optional={false} + text="Course pacing" + /> + } + name="test-course.pacing_type" + options={ + Array [ + Object { + "label": "Self-paced", + "value": "self_paced", + }, + ] + } + type="text" + /> + +

+ The primary instructor or instructors for the course. +

+

+ The order that instructors are listed here is the same order they will be displayed on course pages. You can drag and drop to reorder instructors. +

+ + } + id="test-course.staff.label" + optional={true} + text="Staff" + /> + +
+
+ +

+ The minimum number of hours per week the learner should expect to spend on the course. +

+
+ } + id="test-course.min_effort.label" + optional={false} + text="Minimum effort" + /> + } + name="test-course.min_effort" + required={false} + type="number" + /> +
+
+ +

+ The maximum number of hours per week the learner should expect to spend on the course. +

+
+ } + id="test-course.max_effort.label" + optional={false} + text="Maximum effort" + /> + } + name="test-course.max_effort" + required={false} + type="number" + /> + + + +

+ The estimated number of weeks the learner should expect to spend on the course, rounded to the nearest whole number. +

+ + } + id="test-course.weeks_to_complete.label" + optional={false} + text="Length" + /> + } + name="test-course.weeks_to_complete" + required={false} + type="number" + /> + + } + name="test-course.content_language" + options={ + Array [ + Object { + "label": "Arabic - United Arab Emirates", + "value": "ar-ae", + }, + ] + } + required={false} + type="text" + /> + + + +

+ If this Course Run will potentially be part of a Program, please set the expected program type here. +

+ + } + id="test-course.expected_program_type.label" + optional={true} + text="Expected Program Type" + /> + } + name="test-course.expected_program_type" + type="text" + /> + +

+ If this Course Run will potentially be part of a Program, please set the expected program name here. +

+ + } + id="test-course.expected_program_name.label" + optional={true} + text="Expected Program Name" + /> + } + name="test-course.expected_program_name" + type="text" + /> +
+ +

+ Course embargo status for OFAC is managed internally, please contact support with questions. +

+
+ } + id="ofac-notice-label" + optional={false} + text="Course Embargo (OFAC) Restriction text added to the FAQ section" + /> +
+ No +
+ + +
+`; + exports[`Collapsible Course Run renders correctly variant_id field for external course's course run 1`] = ` + +

+ The restriction type for custom runs. These runs are only exposed in APIs if the associated restriction type is passed in as a query param +

+ + } + id={null} + optional={false} + text="Restriction Type" + /> + } + name="test-course.restriction_type" + options={ + Array [ + Object { + "label": "--------", + "value": "", + }, + Object { + "label": "Custom Enterprise", + "value": "custom-b2b-enterprise", + }, + Object { + "label": "Custom B2C", + "value": "custom-b2c", + }, + ] + } + props={ + Object { + "name": "test-course.restriction_type", + } + } + />
0 ? getUpgradeDeadlineOverride(courseRun.seats) diff --git a/src/helpText.jsx b/src/helpText.jsx index 26ad396b5..3760d87e8 100644 --- a/src/helpText.jsx +++ b/src/helpText.jsx @@ -30,6 +30,15 @@ const courseRunVariantIdHelp = (
); +const courseRunRestrictionTypeHelp = ( +
+

+ The restriction type for custom runs. These runs are only exposed in APIs if + the associated restriction type is passed in as a query param +

+
+); + function dateEditHelp(courseRun) { return (
@@ -262,5 +271,6 @@ export { oldUrlSlugExample, subdirectoryUrlSlugExample, courseRunVariantIdHelp, + courseRunRestrictionTypeHelp, keyHelp, }; diff --git a/src/utils/index.js b/src/utils/index.js index 41dbe470d..a1f90ac5a 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -369,6 +369,12 @@ const buildInitialPrices = (entitlements, courseRuns) => { const hasMastersTrack = (runTypeUuid, runTypeModes) => (!!runTypeUuid && !!runTypeModes[runTypeUuid] && runTypeModes[runTypeUuid].includes(MASTERS_TRACK.key)); +const restrictionTypeOptions = [ + { value: '', label: '--------' }, + { value: 'custom-b2b-enterprise', label: 'Custom Enterprise' }, + { value: 'custom-b2c', label: 'Custom B2C' }, +]; + export { courseRunIsArchived, getDateWithDashes, @@ -401,4 +407,5 @@ export { loadOptions, courseTagObjectsToSelectOptions, getCourseUrlSlugPattern, + restrictionTypeOptions, };