AA-264 mfe courseware reset dates (#165)

- add toast display after successful reset of dates via banner.
This commit is contained in:
Nick
2020-08-06 15:20:53 -04:00
committed by GitHub
parent ad74b2295b
commit 9a315aa29d
10 changed files with 305 additions and 28 deletions

View File

@@ -5,6 +5,7 @@ Object {
"courseHome": Object {
"courseId": null,
"courseStatus": "loading",
"displayResetDatesToast": false,
},
"courseware": Object {
"courseId": null,
@@ -21,6 +22,7 @@ Object {
"courseHome": Object {
"courseId": "course-v1:edX+DemoX+Demo_Course_1",
"courseStatus": "loaded",
"displayResetDatesToast": false,
},
"courseware": Object {
"courseId": null,
@@ -104,6 +106,7 @@ Object {
"courseHome": Object {
"courseId": "course-v1:edX+DemoX+Demo_Course_1",
"courseStatus": "loaded",
"displayResetDatesToast": false,
},
"courseware": Object {
"courseId": null,

View File

@@ -10,6 +10,7 @@ const slice = createSlice({
initialState: {
courseStatus: 'loading',
courseId: null,
displayResetDatesToast: false,
},
reducers: {
fetchTabRequest: (state, { payload }) => {
@@ -24,6 +25,9 @@ const slice = createSlice({
state.courseId = payload.courseId;
state.courseStatus = FAILED;
},
toggleResetDatesToast: (state, { payload }) => {
state.displayResetDatesToast = payload.displayResetDatesToast;
},
},
});
@@ -31,6 +35,7 @@ export const {
fetchTabRequest,
fetchTabSuccess,
fetchTabFailure,
toggleResetDatesToast,
} = slice.actions;
export const {

View File

@@ -17,6 +17,7 @@ import {
fetchTabFailure,
fetchTabRequest,
fetchTabSuccess,
toggleResetDatesToast,
} from './slice';
export function fetchTab(courseId, tab, getTabData) {
@@ -78,6 +79,7 @@ export function resetDeadlines(courseId, getTabData) {
return async (dispatch) => {
postCourseDeadlines(courseId).then(() => {
dispatch(getTabData(courseId));
dispatch(toggleResetDatesToast({ displayResetDatesToast: true }));
});
};
}

View File

@@ -5,6 +5,7 @@ import React, {
useState,
useLayoutEffect,
} from 'react';
import { useDispatch } from 'react-redux';
import PropTypes from 'prop-types';
import { getConfig } from '@edx/frontend-platform';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
@@ -12,6 +13,8 @@ import messages from './messages';
import BookmarkButton from '../bookmark/BookmarkButton';
import { useModel } from '../../../generic/model-store';
import PageLoading from '../../../generic/PageLoading';
import { resetDeadlines } from '../../../course-home/data/thunks';
import { fetchCourse } from '../../data/thunks';
const LockPaywall = React.lazy(() => import('./lock-paywall'));
@@ -64,6 +67,8 @@ function Unit({
contentTypeGatingEnabled,
} = course;
const dispatch = useDispatch();
// Do not remove this hook. See function description.
useLoadBearingHook(id);
@@ -130,6 +135,13 @@ function Unit({
height={iframeHeight}
scrolling="no"
referrerPolicy="origin"
onLoad={() => {
window.onmessage = function (e) {
if (e.data === 'reset_dates') {
dispatch(resetDeadlines(courseId, fetchCourse));
}
};
}}
/>
</div>
</div>

View File

@@ -1,18 +1,27 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { useDispatch, useSelector } from 'react-redux';
import { Header } from '../course-header';
import PageLoading from '../generic/PageLoading';
import messages from './messages';
import LoadedTabPage from './LoadedTabPage';
import LearningToast from '../toast/LearningToast';
import { toggleResetDatesToast } from '../course-home/data/slice';
function TabPage({
intl,
courseStatus,
...passthroughProps
}) {
const courseId = useSelector(state => state.courseHome.courseId || state.courseware.courseId);
const {
displayResetDatesToast,
} = useSelector(state => state.courseHome);
const dispatch = useDispatch();
if (courseStatus === 'loading') {
return (
<>
@@ -26,7 +35,16 @@ function TabPage({
if (courseStatus === 'loaded') {
return (
<LoadedTabPage {...passthroughProps} />
<>
<LearningToast
body={messages.datesResetSuccessBody}
header={messages.datesResetSuccessHeader}
link={`/course/${courseId}/dates`}
onClose={() => dispatch(toggleResetDatesToast({ displayResetDatesToast: false }))}
show={displayResetDatesToast}
/>
<LoadedTabPage {...passthroughProps} />
</>
);
}

View File

@@ -9,6 +9,14 @@ const messages = defineMessages({
id: 'learning.loading',
defaultMessage: 'Loading course page…',
},
datesResetSuccessBody: {
id: 'learning.datesResetSuccessBody',
defaultMessage: 'View all dates',
},
datesResetSuccessHeader: {
id: 'learning.datesResetSuccessHeader',
defaultMessage: 'Your due dates have been successfully shifted to help you stay on track.',
},
});
export default messages;

View File

@@ -0,0 +1,57 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Toast } from '@edx/paragon';
import { Link } from 'react-router-dom';
import './LearningToast.scss';
function LearningToast({
intl,
body,
header,
link,
onClose,
show,
}) {
return (
<Toast
onClose={onClose}
show={show}
delay={3000}
autohide
style={{
boxShadow: 'none',
position: 'fixed',
bottom: '1rem',
left: '1rem',
zIndex: 2,
}}
>
<Toast.Header className="bg-gray-700 border-bottom-0 text-light">
<div className="mr-auto">{intl.formatMessage(header)}</div>
</Toast.Header>
<Toast.Body className="bg-gray-700 text-light">
<Link className="text-light" to={link}>{intl.formatMessage(body)}</Link>
</Toast.Body>
</Toast>
);
}
LearningToast.propTypes = {
body: PropTypes.shape({
possible: PropTypes.number,
earned: PropTypes.number,
graded: PropTypes.bool,
}).isRequired,
header: PropTypes.shape({
id: PropTypes.string,
defaultMessage: PropTypes.string,
}).isRequired,
intl: intlShape.isRequired,
link: PropTypes.string.isRequired,
onClose: PropTypes.func.isRequired,
show: PropTypes.bool.isRequired,
};
export default injectIntl(LearningToast);

View File

@@ -0,0 +1,19 @@
.toast-header {
div {
margin-top: 10px;
}
button {
align-self: flex-start;
color: #FFFFFF;
&:hover {
color: #FFFFFF;
}
}
}
.toast-body {
a {
text-decoration: underline;
}
}