feat: card banners and test data wireframing

This commit is contained in:
Ben Warzeski
2022-06-02 14:14:03 -04:00
parent 21c2fd3b0b
commit c70c3830d6
21 changed files with 539 additions and 206 deletions

85
package-lock.json generated
View File

@@ -13,7 +13,7 @@
"@edx/frontend-component-footer": "10.1.6",
"@edx/frontend-component-header": "^2.4.6",
"@edx/frontend-platform": "^1.15.6",
"@edx/paragon": "16.14.4",
"@edx/paragon": "19.25.0",
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-brands-svg-icons": "^5.15.4",
"@fortawesome/free-solid-svg-icons": "^5.15.4",
@@ -3522,36 +3522,34 @@
}
},
"node_modules/@edx/paragon": {
"version": "16.14.4",
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-16.14.4.tgz",
"integrity": "sha512-vDB8ur2zBvJjHnT0HGmtzRzmeyu9UNjv5fYbACwDnW9iheCnLX4CxP4hPxdM1L0I/FpuoOa3EeKealJzwUhJqQ==",
"version": "19.25.0",
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-19.25.0.tgz",
"integrity": "sha512-l6V7KNqTGqUyiAJbDRCtPzWQ0Ec7QAhHUPM/FMFvGQbVm1KWqcA2x0f2KcdtzDc0mqXGFhcuL4oxfZOS6NSuHg==",
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.30",
"@fortawesome/free-solid-svg-icons": "^5.14.0",
"@fortawesome/react-fontawesome": "^0.1.11",
"@popperjs/core": "^2.6.0",
"airbnb-prop-types": "^2.12.0",
"bootstrap": "4.6.0",
"classnames": "^2.2.6",
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/react-fontawesome": "^0.1.18",
"@popperjs/core": "^2.11.4",
"airbnb-prop-types": "^2.16.0",
"bootstrap": "^4.6.1",
"classnames": "^2.3.1",
"email-prop-type": "^3.0.0",
"font-awesome": "^4.7.0",
"lodash.uniqby": "^4.7.0",
"mailto-link": "^1.0.0",
"prop-types": "^15.7.2",
"react-bootstrap": "^1.3.0",
"react-focus-on": "^3.5.0",
"react-popper": "^2.2.4",
"prop-types": "^15.8.1",
"react-bootstrap": "^1.6.4",
"react-focus-on": "^3.5.4",
"react-popper": "^2.2.5",
"react-proptype-conditional-require": "^1.0.4",
"react-responsive": "^8.2.0",
"react-table": "^7.6.1",
"react-transition-group": "^4.0.0",
"react-table": "^7.7.0",
"react-transition-group": "^4.4.2",
"tabbable": "^4.0.0",
"uncontrollable": "7.2.1"
"uncontrollable": "^7.2.1"
},
"peerDependencies": {
"prop-types": "^15.7.2",
"react": "^16.8.6",
"react-dom": "^16.8.6"
"react": "^16.8.6 || ^17.0.0",
"react-dom": "^16.8.6 || ^17.0.0"
}
},
"node_modules/@edx/paragon/node_modules/prop-types": {
@@ -9099,9 +9097,9 @@
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"node_modules/bootstrap": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz",
"integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==",
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz",
"integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/bootstrap"
@@ -36248,31 +36246,30 @@
}
},
"@edx/paragon": {
"version": "16.14.4",
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-16.14.4.tgz",
"integrity": "sha512-vDB8ur2zBvJjHnT0HGmtzRzmeyu9UNjv5fYbACwDnW9iheCnLX4CxP4hPxdM1L0I/FpuoOa3EeKealJzwUhJqQ==",
"version": "19.25.0",
"resolved": "https://registry.npmjs.org/@edx/paragon/-/paragon-19.25.0.tgz",
"integrity": "sha512-l6V7KNqTGqUyiAJbDRCtPzWQ0Ec7QAhHUPM/FMFvGQbVm1KWqcA2x0f2KcdtzDc0mqXGFhcuL4oxfZOS6NSuHg==",
"requires": {
"@fortawesome/fontawesome-svg-core": "^1.2.30",
"@fortawesome/free-solid-svg-icons": "^5.14.0",
"@fortawesome/react-fontawesome": "^0.1.11",
"@popperjs/core": "^2.6.0",
"airbnb-prop-types": "^2.12.0",
"bootstrap": "4.6.0",
"classnames": "^2.2.6",
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/react-fontawesome": "^0.1.18",
"@popperjs/core": "^2.11.4",
"airbnb-prop-types": "^2.16.0",
"bootstrap": "^4.6.1",
"classnames": "^2.3.1",
"email-prop-type": "^3.0.0",
"font-awesome": "^4.7.0",
"lodash.uniqby": "^4.7.0",
"mailto-link": "^1.0.0",
"prop-types": "^15.7.2",
"react-bootstrap": "^1.3.0",
"react-focus-on": "^3.5.0",
"react-popper": "^2.2.4",
"prop-types": "^15.8.1",
"react-bootstrap": "^1.6.4",
"react-focus-on": "^3.5.4",
"react-popper": "^2.2.5",
"react-proptype-conditional-require": "^1.0.4",
"react-responsive": "^8.2.0",
"react-table": "^7.6.1",
"react-transition-group": "^4.0.0",
"react-table": "^7.7.0",
"react-transition-group": "^4.4.2",
"tabbable": "^4.0.0",
"uncontrollable": "7.2.1"
"uncontrollable": "^7.2.1"
},
"dependencies": {
"prop-types": {
@@ -40641,9 +40638,9 @@
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
},
"bootstrap": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.0.tgz",
"integrity": "sha512-Io55IuQY3kydzHtbGvQya3H+KorS/M9rSNyfCGCg9WZ4pyT/lCxIlpJgG1GXW/PswzC84Tr2fBYi+7+jFVQQBw==",
"version": "4.6.1",
"resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-4.6.1.tgz",
"integrity": "sha512-0dj+VgI9Ecom+rvvpNZ4MUZJz8dcX7WCX+eTID9+/8HgOkv3dsRzi8BGeZJCQU6flWQVYxwTQnEZFrmJSEO7og==",
"requires": {}
},
"bottleneck": {

View File

@@ -30,7 +30,7 @@
"@edx/frontend-component-footer": "10.1.6",
"@edx/frontend-component-header": "^2.4.6",
"@edx/frontend-platform": "^1.15.6",
"@edx/paragon": "16.14.4",
"@edx/paragon": "19.25.0",
"@fortawesome/fontawesome-svg-core": "^1.2.36",
"@fortawesome/free-brands-svg-icons": "^5.15.4",
"@fortawesome/free-solid-svg-icons": "^5.15.4",

22
src/components/Banner.jsx Normal file
View File

@@ -0,0 +1,22 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Alert } from '@edx/paragon';
import { Info } from '@edx/paragon/icons';
export const Banner = ({ children, variant, icon }) => (
<Alert variant={variant} className="mb-0" icon={icon}>
{children}
</Alert>
);
Banner.defaultProps = {
icon: Info,
variant: 'info',
};
Banner.propTypes = {
variant: PropTypes.string,
icon: PropTypes.string,
children: PropTypes.node.isRequired,
};
export default Banner;

View File

@@ -1,17 +0,0 @@
import React from 'react';
import {
// Button,
// PageBanner,
Alert,
} from '@edx/paragon';
import {
// Program
} from '@edx/paragon/icons';
export const CourseCardFooter = () => (
<Alert variant="success">
footer
</Alert>
);
export default CourseCardFooter;

View File

@@ -0,0 +1,43 @@
import React from 'react';
import { Button } from '@edx/paragon';
import { Locked } from '@edx/paragon/icons';
import shapes from 'data/services/lms/shapes';
export const CourseCardActions = ({ cardData: { enrollment, courseRun } }) => {
let primary;
let secondary = null;
if (!enrollment.isVerified) {
secondary = (
<Button
iconBefore={Locked}
variant="outline-primary"
disabled={!enrollment.canUpgrade}
>
Upgrade
</Button>
);
}
if (courseRun.isPending) {
primary = (
<Button>
Begin Course
</Button>
);
} else if (!courseRun.isEnded) {
if (enrollment.isAudit && enrollment.isAuditAccessExpired) {
primary = (<Button disabled>Resume</Button>);
} else {
primary = (<Button>Resume</Button>);
}
} else {
primary = (<Button>View Course</Button>);
}
return (<>{secondary}{primary}</>);
};
CourseCardActions.propTypes = {
cardData: shapes.courseRunCardData.isRequired,
};
export default CourseCardActions;

View File

@@ -0,0 +1,91 @@
/* eslint-disable max-len */
import React from 'react';
import { Hyperlink } from '@edx/paragon';
import { CheckCircle } from '@edx/paragon/icons';
import shapes from 'data/services/lms/shapes';
import Banner from 'components/Banner';
const restrictedMessage = 'Your Certificate of Achievement is being held pending confirmation that the issuance of your Certificate is in compliance with strict U.S. embargoes on Iran, Cuba, Syria, and Sudan. If you think our system has mistakenly identified you as being connected with one of those countries, please let us know by contacting ';
export const CertificateBanner = ({ cardData }) => {
const {
certificates,
courseRun,
enrollment,
grades,
} = cardData;
if (certificates.isRestricted) {
if (enrollment.isAudit) {
return (
<Banner variant="danger">
{restrictedMessage}<Hyperlink>info@example.com</Hyperlink>
</Banner>
);
}
return (
<Banner variant="danger">
{restrictedMessage}<Hyperlink>info@example.com</Hyperlink>
If you would like a refund on your Certificate of Achievement, please contact our billing address <Hyperlink>billing@example.com</Hyperlink>
</Banner>
);
}
if (!grades.isPassing) {
if (enrollment.isAudit) {
return (
<Banner>
Grade required to pass the course: {courseRun.minPassingGrade}%
</Banner>
);
}
if (courseRun.isFinished) {
return (
<Banner variant="warning">
You are not eligible for a certificate.
{' '}
<Hyperlink>View grades.</Hyperlink>
</Banner>
);
}
return (
<Banner variant="warning">
Grade required for a certificate: {courseRun.minPassingGrade}%
</Banner>
);
}
if (certificates.isDownloadable) {
if (certificates.downloadUrls.preview) {
return (
<Banner variant="success" icon={CheckCircle}>
Congratulations. Your certificate is ready.
{' '}
<Hyperlink>View Certificate.</Hyperlink>
</Banner>
);
}
return (
<Banner variant="success" icon={CheckCircle}>
Congratulations. Your certificate is ready.
{' '}
<Hyperlink>Download Certificate.</Hyperlink>
</Banner>
);
}
if (certificates.isEarned && !certificates.isAvailable) {
return (
<Banner>
Your grade and certificate will be ready after {certificates.availableDate}.
</Banner>
);
}
return null;
};
CertificateBanner.propTypes = {
cardData: shapes.courseRunCardData.isRequired,
};
export default CertificateBanner;

View File

@@ -0,0 +1,49 @@
/* eslint-disable max-len */
import React from 'react';
import { Hyperlink } from '@edx/paragon';
import shapes from 'data/services/lms/shapes';
import Banner from 'components/Banner';
export const CourseBanner = ({ cardData }) => {
const {
// course,
enrollment,
courseRun,
} = cardData;
if (enrollment.isVerified) {
return null;
}
const isActive = courseRun.isStarted && !courseRun.isFinished;
const { canUpgrade, isAuditAccessExpired } = enrollment;
if (isAuditAccessExpired) {
if (canUpgrade) {
return (
<Banner>
Your audit access to this course has expired. Upgrade now to access your course again.
</Banner>
);
}
return (
<Banner>
Your audit access to this course has expired. <Hyperlink>Find another course</Hyperlink>
</Banner>
);
}
if (isActive && !canUpgrade) {
return (
<Banner>
Your upgrade deadline for this course has passed. To upgrade, enroll in a session that is farther in the future.
{' '}
<Hyperlink href={cardData.course.website}>Explore course details.</Hyperlink>
</Banner>
);
}
return null;
};
CourseBanner.propTypes = {
cardData: shapes.courseRunCardData.isRequired,
};
export default CourseBanner;

View File

@@ -0,0 +1,42 @@
import React from 'react';
import { Hyperlink } from '@edx/paragon';
import shapes from 'data/services/lms/shapes';
import Banner from 'components/Banner';
export const EntitlementBanner = ({ cardData }) => {
const { entitlements } = cardData;
if (!entitlements.isEntitlement) {
return null;
}
if (entitlements.isExpired) {
return null;
}
if (!entitlements.isFulfilled) {
if (entitlements.canChange) {
return (
<Banner>You must select a session to access the course.</Banner>
);
}
return (
<Banner>The deadline to select a session has passed</Banner>
);
}
if (entitlements.canChange) {
return (
<Banner>
You can change sessions until {entitlements.changeDeadline}.
{' '}
<Hyperlink>Change or leave session</Hyperlink>
</Banner>
);
}
return null;
};
EntitlementBanner.propTypes = {
cardData: shapes.courseRunCardData.isRequired,
};
export default EntitlementBanner;

View File

@@ -0,0 +1,20 @@
import React from 'react';
import shapes from 'data/services/lms/shapes';
import CourseBanner from './components/CourseBanner';
import CertificateBanner from './components/CertificateBanner';
import EntitlementBanner from './components/EntitlementBanner';
export const CourseCardBanners = ({ cardData }) => (
<>
<CourseBanner cardData={cardData} />
<CertificateBanner cardData={cardData} />
<EntitlementBanner cardData={cardData} />
</>
);
CourseCardBanners.propTypes = {
cardData: shapes.courseRunCardData.isRequired,
};
export default CourseCardBanners;

View File

@@ -1,24 +1,28 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Locked } from '@edx/paragon/icons';
import { Button, Card } from '@edx/paragon';
// import PropTypes from 'prop-types';
import { Card } from '@edx/paragon';
import { courseData } from 'data/services/lms/fakeData/courses';
import shapes from 'data/services/lms/shapes';
import RelatedProgram from './RelatedProgram';
import CourseCardMenu from './CourseCardMenu';
import CourseCardFooter from './CourseCardFooter';
import RelatedProgram from './components/RelatedProgram';
import CourseCardMenu from './components/CourseCardMenu';
import CourseCardBanners from './components/CourseCardBanners';
import CourseCardActions from './components/CourseCardActions';
export const CourseCard = ({ courseID }) => {
export const CourseCard = ({ cardData }) => {
const {
title,
imageUrl,
displayNumber,
displayOrg,
accessExpiryDate,
} = courseData[courseID] || {};
course: {
title,
bannerUrl: imageUrl,
},
courseRun: {
courseNumber,
accessExpirationDate,
},
} = cardData;
const providerName = cardData.provider?.name;
return (
<div>
<div className="mb-3">
<Card orientation="horizontal">
<Card.ImageCap
src={imageUrl}
@@ -32,23 +36,20 @@ export const CourseCard = ({ courseID }) => {
actions={<CourseCardMenu />}
/>
<Card.Section>
{displayOrg} {displayNumber} Access expires {accessExpiryDate}
{providerName || 'Unkown'} {courseNumber} Access expires {accessExpirationDate}
</Card.Section>
<Card.Footer orientation="vertical" textElement={<RelatedProgram />}>
<Button iconBefore={Locked} variant="outline-primary">
Upgrade
</Button>
<Button>Resume</Button>
<CourseCardActions cardData={cardData} />
</Card.Footer>
</Card.Body>
</Card>
<CourseCardFooter />
<CourseCardBanners cardData={cardData} />
</div>
);
};
CourseCard.propTypes = {
courseID: PropTypes.string.isRequired,
cardData: shapes.courseRunCardData.isRequired,
};
CourseCard.defaultProps = {};

View File

@@ -1,17 +1,19 @@
import React from 'react';
import PropTypes from 'prop-types';
import shapes from 'data/services/lms/shapes';
import CourseCard from 'containers/CourseCard';
export const CourseList = ({ courseIDs }) => (
export const CourseList = ({ courseListData }) => (
<div className="d-flex flex-column flex-grow-1">
{courseIDs.map((id) => (
<CourseCard courseID={id} />
{courseListData.map((cardData) => (
<CourseCard cardData={cardData} />
))}
</div>
);
CourseList.propTypes = {
courseIDs: PropTypes.arrayOf(PropTypes.string).isRequired,
courseListData: PropTypes.arrayOf(shapes.courseRunCardData).isRequired,
};
export default CourseList;

View File

@@ -1,29 +1,51 @@
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import { selectors, thunkActions } from 'data/redux';
import CourseList from 'containers/CourseList';
import WidgetSidebar from 'containers/WidgetSidebar';
import { courseIDs } from 'data/services/lms/fakeData/courses';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import EmptyCourse from '../EmptyCourse';
import EmptyCourse from 'containers/EmptyCourse';
import messages from './messages';
import * as module from '.';
export const Dashboard = () => (
<div className="d-flex flex-column p-2">
{courseIDs.length ? (
<>
<h2 className="py-2">
<FormattedMessage {...messages.myCourse} />
</h2>
<div className="d-flex">
<CourseList courseIDs={courseIDs} />
<WidgetSidebar />
</div>
</>
) : (
<EmptyCourse />
)}
</div>
);
export const hooks = ({ dispatch }) => ({
initialize: () => React.useEffect(
() => { dispatch(thunkActions.app.initialize()); },
[],
),
enrollments: useSelector(selectors.app.enrollments),
entitlements: useSelector(selectors.app.entitlements),
});
export const Dashboard = () => {
const dispatch = useDispatch();
const {
initialize,
enrollments,
// entitlements,
} = module.hooks({ dispatch });
initialize();
return (
<div className="d-flex flex-column p-2">
{enrollments.length ? (
<>
<h2 className="py-2">
<FormattedMessage {...messages.myCourse} />
</h2>
<div className="d-flex">
<CourseList courseListData={enrollments} />
<WidgetSidebar />
</div>
</>
) : (
<EmptyCourse />
)}
</div>
);
};
export default Dashboard;

View File

@@ -8,6 +8,7 @@ import './index.scss';
export const WidgetSidebar = () => (
<div className="widget-sidebar">
<div className="d-flex">
{/* <img src='more-courses-sidewidget.svg' />
<div>
<h3>
@@ -17,6 +18,7 @@ export const WidgetSidebar = () => (
<FormattedMessage {...messages.findCoursesButton} />
</Hyperlink>
</div> */}
<Card orientation="horizontal" className="mb-4">
<Card.ImageCap
src="more-courses-sidewidget.svg"

View File

@@ -2,12 +2,8 @@ import { StrictDict } from 'utils';
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
courseMetadata: {
name: '',
number: '',
org: '',
courseId: '',
},
enrollments: [],
entitlements: [],
};
// eslint-disable-next-line no-unused-vars
@@ -15,7 +11,8 @@ const app = createSlice({
name: 'app',
initialState,
reducers: {
loadCourseMetadata: (state, { payload }) => ({ ...state, courseMetadata: payload }),
loadEnrollments: (state, { payload }) => ({ ...state, enrollments: payload }),
loadEntitlements: (state, { payload }) => ({ ...state, entitlements: payload }),
},
});

View File

@@ -10,7 +10,8 @@ const mkSimpleSelector = (cb) => createSelector([module.appSelector], cb);
// top-level app data selectors
export const simpleSelectors = {
courseMetadata: mkSimpleSelector(app => app.courseMetadata),
enrollments: mkSimpleSelector(app => app.enrollments),
entitlements: mkSimpleSelector(app => app.entitlements),
};
export default StrictDict({

View File

@@ -1,6 +1,8 @@
import { StrictDict } from 'utils';
import { actions } from 'data/redux';
import requests from './requests';
// import { selectors, actions } from 'data/redux';
// import { locationId } from 'data/constants/app';
// import { } from './requests';
@@ -10,8 +12,14 @@ import { StrictDict } from 'utils';
* initialize the app, loading ora and course metadata from the api, and loading the initial
* submission list data.
*/
export const initialize = () => () => {
};
export const initialize = () => (dispatch) => (
requests.initialize().then(
({ enrollments, entitlements }) => {
dispatch(actions.app.loadEnrollments(enrollments));
dispatch(actions.app.loadEntitlements(entitlements));
},
)
);
export default StrictDict({
initialize,

View File

@@ -2,6 +2,7 @@ import { StrictDict } from 'utils';
// import { RequestKeys } from 'data/constants/requests';
import { actions } from 'data/redux';
import fakeData from 'data/services/lms/fakeData/courses';
// import api from 'data/services/lms/api';
// import * as module from './requests';
@@ -33,5 +34,13 @@ export const networkRequest = ({
});
};
export const initialize = () => (
Promise.resolve({
enrollments: fakeData.courseRunData,
entitlements: fakeData.entitlementCourses,
})
);
export default StrictDict({
initialize,
});

View File

@@ -6,7 +6,7 @@ export const providers = StrictDict({
website: 'www.edx.com',
email: 'support@edx.com',
},
MIT: {
mit: {
name: 'MIT',
website: 'www.mit.edu',
email: 'support@mit.edu',
@@ -38,7 +38,7 @@ export const genCourseRunData = (data = {}) => ({
export const genEnrollmentData = (data = {}) => ({
isAudit: true,
isVerified: false,
canUpgrade: data.verified ? null : false,
canUpgrade: data.verified ? null : true,
isAuditAccessExpired: data.verified ? null : false,
...data,
});
@@ -48,10 +48,8 @@ export const genCertificateData = (data = {}) => ({
isRestricted: false,
isAvailable: false,
isEarned: false,
isRequesting: false,
isGenerating: false,
isDownloadable: false,
downloadUrls: null, // { preview, download, honorCertDownload }
downloadUrls: null, // { preview, download }
...data,
});
@@ -68,15 +66,15 @@ export const courseRuns = [
grades: { isPassing: true },
courseRun: { isPending: true },
certificates: genCertificateData(),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// audit, started, cannot upgrade, restricted
{
enrollment: genEnrollmentData({ isAudit: true }),
enrollment: genEnrollmentData({ isAudit: true, canUpgrade: false }),
grades: { isPassing: true },
courseRun: { isStarted: true },
certificates: genCertificateData({ isRestricted: true }),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// audit, started, can upgrade
{
@@ -84,23 +82,35 @@ export const courseRuns = [
grades: { isPassing: true },
courseRun: { isStarted: true },
certificates: genCertificateData(),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// audit, started, not passing
{
enrollment: genEnrollmentData({ isAudit: true, canUpgrade: true }),
grades: { isPassing: false },
courseRun: { isStarted: true, accessExpirationDate: pastDate },
courseRun: { isStarted: true },
certificates: genCertificateData(),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// audit, started, audit access expired
// audit, started, audit access expired, can upgrade
{
enrollment: genEnrollmentData({ isAudit: true, isAuditAccessExpired: true }),
grades: { isPassing: true },
courseRun: { isStarted: true, accessExpirationDate: pastDate },
certificates: genCertificateData(),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// audit, started, audit access expired, cannot upgrade
{
enrollment: genEnrollmentData({
isAudit: true,
isAuditAccessExpired: true,
canUpgrade: false,
}),
grades: { isPassing: true },
courseRun: { isStarted: true, accessExpirationDate: pastDate },
certificates: genCertificateData(),
entitlements: { isEntitlement: false },
},
// verified, pending, restricted
{
@@ -108,7 +118,7 @@ export const courseRuns = [
grades: { isPassing: true },
courseRun: { isPending: true },
certificates: genCertificateData({ isRestricted: true }),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// verified, started
{
@@ -116,7 +126,7 @@ export const courseRuns = [
grades: { isPassing: true },
courseRun: { isStarted: true },
certificates: genCertificateData(),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// verified, not passing
{
@@ -124,7 +134,7 @@ export const courseRuns = [
grades: { isPassing: false },
courseRun: { isStarted: true },
certificates: genCertificateData(),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// verified, finished, not passing
{
@@ -132,7 +142,7 @@ export const courseRuns = [
grades: { isPassing: false },
courseRun: { isFinished: true },
certificates: genCertificateData(),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// verified, restricted
{
@@ -140,7 +150,7 @@ export const courseRuns = [
grades: { isPassing: true },
courseRun: { isStarted: true },
certificates: genCertificateData({ isRestricted: true }),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// verified, earned but not available
{
@@ -151,33 +161,7 @@ export const courseRuns = [
isEarned: true,
availableDate: futureDate,
}),
entitlement: { isEntitlement: false },
},
// verified, earned, requesting
{
enrollment: genEnrollmentData({ isVerified: true }),
grades: { isPassing: true },
courseRun: { isStarted: true },
certificates: genCertificateData({
isEarned: true,
isAvailable: true,
isRequesting: true,
availableDate: pastDate,
}),
entitlement: { isEntitlement: false },
},
// verified, earned, generating
{
enrollment: genEnrollmentData({ isVerified: true }),
grades: { isPassing: true },
courseRun: { isStarted: true },
certificates: genCertificateData({
isEarned: true,
isAvailable: true,
isGenerating: true,
availableDate: pastDate,
}),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// verified, earned, downloadable (web + link)
{
@@ -194,7 +178,7 @@ export const courseRuns = [
download: logos.social,
},
}),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// verified, earned, downloadable (link)
{
@@ -210,23 +194,7 @@ export const courseRuns = [
download: logos.social,
},
}),
entitlement: { isEntitlement: false },
},
// verified, earned, downloadable (honor cert)
{
enrollment: genEnrollmentData({ isVerified: true }),
grades: { isPassing: true },
courseRun: { isStarted: true },
certificates: genCertificateData({
isEarned: true,
isAvailable: true,
isDownloadable: true,
availableDate: pastDate,
downloadUrls: {
honorCertDownload: logos.bio,
},
}),
entitlement: { isEntitlement: false },
entitlements: { isEntitlement: false },
},
// Entitlement Course Run - Cannot view yet
{
@@ -234,11 +202,12 @@ export const courseRuns = [
grades: { isPassing: true },
courseRun: { isPending: true },
certificates: genCertificateData(),
entitlement: {
entitlements: {
isEntitlement: true,
isFulfilled: true,
isRefundable: true,
canViewCourse: false,
canChange: true,
changeDeadline: futureDate,
isExpired: false,
},
@@ -249,11 +218,12 @@ export const courseRuns = [
grades: { isPassing: true },
courseRun: { isStarted: true },
certificates: genCertificateData(),
entitlement: {
entitlements: {
isEntitlement: true,
isFulfilled: true,
isRefundable: true,
canViewCourse: true,
canChange: true,
changeDeadline: futureDate,
isExpired: false,
},
@@ -264,11 +234,12 @@ export const courseRuns = [
grades: { isPassing: true },
courseRun: { isStarted: true },
certificates: genCertificateData(),
entitlement: {
entitlements: {
isEntitlement: true,
isFulfilled: true,
isRefundable: true,
canViewCourse: true,
canChange: false,
changeDeadline: pastDate,
isExpired: false,
},
@@ -277,13 +248,14 @@ export const courseRuns = [
{
enrollment: genEnrollmentData({ isVerified: true }),
grades: { isPassing: true },
courseRun: { isStarted: true },
courseRun: { isStarted: true, isFinished: true, isArchived: true },
certificates: genCertificateData(),
entitlement: {
entitlements: {
isEntitlement: true,
isFulfilled: true,
isRefundable: true,
isRefundable: false,
canViewCourse: true,
canChange: false,
changeDeadline: pastDate,
isExpired: true,
},
@@ -294,31 +266,37 @@ export const entitlementCourses = [
{
course: { title: genCourseTitle(100) },
entitlements: {
isEntitlement: true,
availableSessions,
isRefundable: true,
isFulfilled: false,
canViewCourse: false,
changeDeadline: futureDate,
canChange: true,
isExpired: false,
},
}, {
course: { title: genCourseTitle(101) },
entitlements: {
isEntitlement: true,
availableSessions,
isRefundable: true,
isFulfilled: false,
canViewCourse: false,
changeDeadline: pastDate,
canChange: false,
isExpired: false,
},
}, {
course: { title: genCourseTitle(102) },
entitlements: {
isEntitlement: true,
availableSessions,
isRefundable: true,
isFulfilled: false,
canViewCourse: false,
changeDeadline: pastDate,
canChange: false,
isExpired: true,
},
},
@@ -328,30 +306,26 @@ export const entitlementCourses = [
// Entitlement Course - cannot view yet
// Entitlement Course - can view and change
// Entitlement Course - expired
export const courseRunData = courseRuns.reduce(
(obj, curr, index) => {
const out = { ...curr };
export const courseRunData = courseRuns.map(
(data, index) => {
const title = genCourseTitle(index);
const courseNumber = genCourseID(index);
const providerIndex = index % 3;
const iteratedData = [
{
provider: providers.edx,
course: { title: genCourseTitle(index), bannerUrl: logos.edx },
},
{
provider: providers.mit,
course: { title: genCourseTitle(index), bannerUrl: logos.bio },
},
{
provider: null,
course: { title: genCourseTitle(index), bannerUrl: logos.social },
},
{ provider: providers.edx, course: { title, bannerUrl: logos.edx } },
{ provider: providers.mit, course: { title, bannerUrl: logos.science } },
{ provider: null, course: { title, bannerUrl: logos.social } },
];
out.courseRun.courseNumber = genCourseID(index);
return {
...out,
...data,
courseRun: genCourseRunData({ ...data.courseRun, courseNumber }),
...iteratedData[providerIndex],
credit: { isPurchased: false, requestStatus: null },
};
},
{},
);
export default {
courseRunData,
entitlementCourses,
};

View File

@@ -0,0 +1,70 @@
import PropTypes from 'prop-types';
import { StrictDict } from 'utils';
export const shapes = StrictDict({
course: PropTypes.shape({
bannerUrl: PropTypes.string,
title: PropTypes.string,
website: PropTypes.string,
}),
provider: PropTypes.shape({
email: PropTypes.string,
name: PropTypes.string,
website: PropTypes.string,
}),
courseRun: PropTypes.shape({
accessExpirationDate: PropTypes.string,
courseNumber: PropTypes.string,
isArchived: PropTypes.bool,
isFinished: PropTypes.bool,
isPending: PropTypes.bool,
isStarted: PropTypes.bool,
minPassingGrade: PropTypes.number,
}),
credit: PropTypes.shape({
isPurchased: PropTypes.bool,
requestStatus: PropTypes.string,
}),
certificates: PropTypes.shape({
availableDate: PropTypes.string,
downloadUrls: PropTypes.shape({
preview: PropTypes.string,
download: PropTypes.string,
}),
isAvailable: PropTypes.bool,
isDownloadable: PropTypes.bool,
isEarned: PropTypes.bool,
isRestricted: PropTypes.bool,
}),
enrollment: PropTypes.shape({
canUpgrade: PropTypes.bool,
isAudit: PropTypes.bool,
isAuditAccessExpired: PropTypes.bol,
isVerified: PropTypes.bool,
}),
entitlement: PropTypes.shape({
isEntitlement: PropTypes.bool,
canChange: PropTypes.bool,
isFulfilled: PropTypes.bool,
isRefundable: PropTypes.bool,
changeDeadline: PropTypes.string,
isExpired: PropTypes.bool,
canViewCourse: PropTypes.bool,
}),
grades: PropTypes.shape({
isPassing: PropTypes.bool,
}),
});
shapes.courseRunCardData = PropTypes.shape({
course: shapes.course,
provider: shapes.provider,
courseRun: shapes.courseRun,
credit: shapes.credit,
certificates: shapes.certificates,
enrollment: shapes.enrollment,
entitlement: shapes.entitlement,
grades: shapes.grades,
});
export default shapes;