AA-133 mfe dates banner fix (#85)

- mfe dates banner along with fixing previously reverted
  PR by adding back in original coursemetadata call.

This reverts commit 8df4654cf1.
This commit is contained in:
Nick
2020-06-16 13:11:50 -04:00
committed by GitHub
parent 8df4654cf1
commit bf0d3b1565
6 changed files with 236 additions and 3 deletions

View File

@@ -40,12 +40,30 @@ function normalizeMetadata(metadata) {
};
}
function normalizeCourseHomeCourseMetadata(metadata) {
const data = camelCaseObject(metadata);
return {
...data,
tabs: data.tabs.map(tab => ({
slug: tab.tabId,
title: tab.title,
url: tab.url,
})),
};
}
export async function getCourseMetadata(courseId) {
const url = `${getConfig().LMS_BASE_URL}/api/courseware/course/${courseId}`;
const { data } = await getAuthenticatedHttpClient().get(url);
return normalizeMetadata(data);
}
export async function getCourseHomeCourseMetadata(courseId) {
const url = `${getConfig().LMS_BASE_URL}/api/course_home/v1/course_metadata/${courseId}`;
const { data } = await getAuthenticatedHttpClient().get(url);
return normalizeCourseHomeCourseMetadata(data);
}
export async function getDatesTabData(courseId, version) {
const url = `${getConfig().LMS_BASE_URL}/api/course_home/${version}/dates/${courseId}`;
try {
@@ -208,3 +226,8 @@ export async function getResumeBlock(courseId) {
const { data } = await getAuthenticatedHttpClient().get(url.href, {});
return camelCaseObject(data);
}
export async function updateCourseDeadlines(courseId) {
const url = new URL(`${getConfig().LMS_BASE_URL}/api/course_experience/v1/reset_course_deadlines`);
await getAuthenticatedHttpClient().post(url.href, { course_key: courseId });
}

View File

@@ -5,6 +5,8 @@ import {
getSequenceMetadata,
getDatesTabData,
getOutlineTabData,
getCourseHomeCourseMetadata,
updateCourseDeadlines,
} from './api';
import {
addModelsMap, updateModel, updateModels, updateModelsMap, addModel,
@@ -96,9 +98,11 @@ export function fetchTab(courseId, tab, version, getTabData) {
dispatch(fetchTabRequest({ courseId }));
Promise.allSettled([
getCourseMetadata(courseId),
getCourseHomeCourseMetadata(courseId),
getTabData(courseId, version),
]).then(([courseMetadataResult, tabDataResult]) => {
]).then(([courseMetadataResult, courseHomeCourseMetadataResult, tabDataResult]) => {
const fetchedMetadata = courseMetadataResult.status === 'fulfilled';
const fetchedCourseHomeCourseMetadata = courseHomeCourseMetadataResult.status === 'fulfilled';
const fetchedTabData = tabDataResult.status === 'fulfilled';
if (fetchedMetadata) {
@@ -111,8 +115,18 @@ export function fetchTab(courseId, tab, version, getTabData) {
modelType: 'courses',
model: courseMetadataResult.value,
}));
}
if (fetchedCourseHomeCourseMetadata) {
dispatch(addModel({
modelType: 'courseHomeMetadata',
model: {
id: courseId,
...courseHomeCourseMetadataResult.value,
},
}));
} else {
logError(courseMetadataResult.reason);
logError(courseHomeCourseMetadataResult.reason);
}
if (fetchedTabData) {
@@ -127,7 +141,7 @@ export function fetchTab(courseId, tab, version, getTabData) {
logError(tabDataResult.reason);
}
if (fetchedMetadata && fetchedTabData) {
if (fetchedMetadata && fetchedCourseHomeCourseMetadata && fetchedTabData) {
dispatch(fetchTabSuccess({ courseId }));
} else {
dispatch(fetchTabFailure({ courseId }));
@@ -164,3 +178,11 @@ export function fetchSequence(sequenceId) {
}
};
}
export function resetDeadlines(courseId, getTabData) {
return async (dispatch) => {
updateCourseDeadlines(courseId).then(() => {
dispatch(getTabData(courseId));
});
};
}

View File

@@ -0,0 +1,43 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import messages from './messages';
function DatesBanner(props) {
const {
intl,
name,
bannerClickHandler,
} = props;
return (
<div className="banner rounded my-2 p-3 container-fluid border border-primary-200 bg-info-100">
<div className="row justify-content-between">
<div className={name === 'datesTabInfoBanner' ? 'col-12' : 'col-9'}>
<strong>
{intl.formatMessage(messages[`datesBanner.${name}.header`])}
</strong>
{intl.formatMessage(messages[`datesBanner.${name}.body`])}
</div>
{bannerClickHandler && (
<button type="button" className="btn rounded align-self-center border border-primary bg-white mr-3" onClick={bannerClickHandler}>
{intl.formatMessage(messages[`datesBanner.${name}.button`])}
</button>
)}
</div>
</div>
);
}
DatesBanner.propTypes = {
intl: intlShape.isRequired,
name: PropTypes.string.isRequired,
bannerClickHandler: PropTypes.func,
};
DatesBanner.defaultProps = {
bannerClickHandler: null,
};
export default injectIntl(DatesBanner);

View File

@@ -0,0 +1,77 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useDispatch, useSelector } from 'react-redux';
import { useModel } from '../model-store';
import DatesBanner from './DatesBanner';
import { fetchDatesTab, resetDeadlines } from '../data/thunks';
function DatesBannerContainer(props) {
const {
model,
} = props;
const {
courseId,
} = useSelector(state => state.courseware);
const {
datesBannerInfo,
} = useModel(model, courseId);
const {
contentTypeGatingEnabled,
missedDeadlines,
missedGatedContent,
verifiedUpgradeLink,
} = datesBannerInfo;
const {
isSelfPaced,
} = useModel('courseHomeMetadata', courseId);
const dispatch = useDispatch();
const upgradeToCompleteGraded = model === 'dates' && contentTypeGatingEnabled && !missedDeadlines;
const upgradeToReset = !upgradeToCompleteGraded && missedDeadlines && missedGatedContent;
const resetDates = !upgradeToCompleteGraded && missedDeadlines && !missedGatedContent;
const datesBanners = [
{
name: 'datesTabInfoBanner',
shouldDisplay: model === 'dates' && !missedDeadlines && isSelfPaced,
},
{
name: 'upgradeToCompleteGradedBanner',
shouldDisplay: upgradeToCompleteGraded,
clickHandler: () => window.location.replace(verifiedUpgradeLink),
},
{
name: 'upgradeToResetBanner',
shouldDisplay: upgradeToReset,
clickHandler: () => window.location.replace(verifiedUpgradeLink),
},
{
name: 'resetDatesBanner',
shouldDisplay: resetDates,
clickHandler: () => dispatch(resetDeadlines(courseId, fetchDatesTab)),
},
];
return (
<>
{datesBanners.map((banner) => banner.shouldDisplay && (
<DatesBanner
name={banner.name}
bannerClickHandler={banner.clickHandler}
key={banner.name}
/>
))}
</>
);
}
DatesBannerContainer.propTypes = {
model: PropTypes.string.isRequired,
};
export default DatesBannerContainer;

View File

@@ -0,0 +1,66 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
'datesBanner.datesTabInfoBanner.header': {
id: 'datesBanner.datesTabInfoBanner.header',
defaultMessage: "We've built a suggested schedule to help you stay on track. ",
description: 'Strong text in Dates Tab Info Banner',
},
'datesBanner.datesTabInfoBanner.body': {
id: 'datesBanner.datesTabInfoBanner.body',
defaultMessage: `But don't worry—it's flexible so you can learn at your own pace. If you happen to fall behind on
our suggested dates, you'll be able to adjust them to keep yourself on track.`,
description: 'Body in Dates Tab Info Banner',
},
'datesBanner.upgradeToCompleteGradedBanner.header': {
id: 'datesBanner.upgradeToCompleteGradedBanner.header',
defaultMessage: 'You are auditing this course, ',
description: 'Strong text in Upgrade To Complete Graded Banner',
},
'datesBanner.upgradeToCompleteGradedBanner.body': {
id: 'datesBanner.upgradeToCompleteGradedBanner.body',
defaultMessage: `which means that you are unable to participate in graded assignments. To complete graded
assignments as part of this course, you can upgrade today.`,
description: 'Body in Upgrade To Complete Graded Banner',
},
'datesBanner.upgradeToCompleteGradedBanner.button': {
id: 'datesBanner.upgradeToCompleteGradedBanner.button',
defaultMessage: 'Upgrade now',
description: 'Button in Upgrade To Complete Graded Banner',
},
'datesBanner.upgradeToResetBanner.header': {
id: 'datesBanner.upgradeToResetBanner.header',
defaultMessage: 'You are auditing this course, ',
description: 'Strong text in Upgrade To Reset Banner',
},
'datesBanner.upgradeToResetBanner.body': {
id: 'datesBanner.upgradeToResetBanner.body',
defaultMessage: `which means that you are unable to participate in graded assignments. It looks like you missed
some important deadlines based on our suggested schedule. To complete graded assignments as part of this course
and shift the past due assignments into the future, you can upgrade today.`,
description: 'Body in Upgrade To Reset Banner',
},
'datesBanner.upgradeToResetBanner.button': {
id: 'datesBanner.upgradeToResetBanner.button',
defaultMessage: 'Upgrade to shift due dates',
description: 'Button in Upgrade To Reset Banner',
},
'datesBanner.resetDatesBanner.header': {
id: 'datesBanner.resetDatesBanner.header',
defaultMessage: 'It looks like you missed some important deadlines based on our suggested schedule. ',
description: 'Strong text in Reset Dates Banner',
},
'datesBanner.resetDatesBanner.body': {
id: 'datesBanner.resetDatesBanner.body',
defaultMessage: `To keep yourself on track, you can update this schedule and shift the past due assignments into
the future. Dont worry—you wont lose any of the progress youve made when you shift your due dates.`,
description: 'Body in Reset Dates Banner',
},
'datesBanner.resetDatesBanner.button': {
id: 'datesBanner.resetDatesBanner.button',
defaultMessage: 'Reset my deadlines',
description: 'Button in Reset Dates Banner',
},
});
export default messages;

View File

@@ -3,10 +3,12 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import messages from './messages';
import Timeline from './Timeline';
import DatesBannerContainer from '../dates-banner/DatesBannerContainer';
function DatesTab({ intl }) {
return (
<>
<DatesBannerContainer model="dates" />
<h2 className="mb-4">
{intl.formatMessage(messages.title)}
</h2>