diff --git a/src/courseware/course/course-exit/CourseCelebration.jsx b/src/courseware/course/course-exit/CourseCelebration.jsx index cd7735e1..a207273e 100644 --- a/src/courseware/course/course-exit/CourseCelebration.jsx +++ b/src/courseware/course/course-exit/CourseCelebration.jsx @@ -3,6 +3,7 @@ import React from 'react'; import { FormattedDate, FormattedMessage, injectIntl, intlShape, } from '@edx/frontend-platform/i18n'; +import { layoutGenerator } from 'react-break'; import { Helmet } from 'react-helmet'; import { useDispatch } from 'react-redux'; import { useParams } from 'react-router-dom'; @@ -12,13 +13,22 @@ import { faCalendarAlt } from '@fortawesome/free-regular-svg-icons'; import { getConfig } from '@edx/frontend-platform'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; -import celebration from './assets/celebration_456x328.gif'; +import CelebrationMobile from './assets/celebration_456x328.gif'; +import CelebrationDesktop from './assets/celebration_750x540.gif'; import certificate from './assets/certificate.png'; import messages from './messages'; import { useModel } from '../../../generic/model-store'; import { requestCert } from '../../../course-home/data/thunks'; function CourseCelebration({ intl }) { + const layout = layoutGenerator({ + mobile: 0, + tablet: 768, + }); + + const OnMobile = layout.is('mobile'); + const OnAtLeastTablet = layout.isAtLeast('tablet'); + const { courseId } = useParams(); const dispatch = useDispatch(); const { @@ -31,7 +41,7 @@ function CourseCelebration({ intl }) { certStatus, certWebViewUrl, downloadUrl, - } = certificateData || { downloadUrl: 'google.com', certWebViewUrl: 'duckduckgo.com' }; + } = certificateData; const { username } = getAuthenticatedUser(); @@ -43,6 +53,7 @@ function CourseCelebration({ intl }) { {intl.formatMessage(messages.dashboardLink)} ); + // todo: remove this hardcoded link to edX support const idVerificationSupportLink = getConfig().SUPPORT_URL && ( ); @@ -91,17 +101,31 @@ function CourseCelebration({ intl }) { const endDate = ; title = intl.formatMessage(messages.certificateHeaderNotAvailable); message = ( - + <> +
+ +
+
+ +
+
+ +
+ ); break; } @@ -114,12 +138,12 @@ function CourseCelebration({ intl }) { buttonText = intl.formatMessage(messages.verifyIdentityButton); buttonLocation = verifyIdentityUrl; title = intl.formatMessage(messages.certificateHeaderUnverified); + // todo: check for idVerificationSupportLink null message = ( ); @@ -131,43 +155,78 @@ function CourseCelebration({ intl }) { return ( <> - {`Congratulations! | ${getConfig().SITE_NAME}`} + {`${intl.formatMessage(messages.congratulationsHeader)} | ${getConfig().SITE_NAME}`} -
-

{ intl.formatMessage(messages.congratulationsHeader) }

-
{ intl.formatMessage(messages.shareHeader) }
-
-
-
-
{title}
- {message} - {/* The requesting status needs a different button because it does a POST instead of a GET */} - {certStatus === 'requesting' ? ( -
- -
- ) : ( -
- + )} +
+ {certStatus !== 'unverified' && ( +
+ {`${intl.formatMessage(messages.certificateImage)}`}
)}
- {certStatus !== 'unverified' && ( -
- )} -
-
- - +
+

+   + +

+
diff --git a/src/courseware/course/course-exit/CourseExit.jsx b/src/courseware/course/course-exit/CourseExit.jsx index a1e283cd..aa789376 100644 --- a/src/courseware/course/course-exit/CourseExit.jsx +++ b/src/courseware/course/course-exit/CourseExit.jsx @@ -1,8 +1,12 @@ import React from 'react'; +import { getConfig } from '@edx/frontend-platform'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; +import { Button } from '@edx/paragon'; import { Redirect, useParams } from 'react-router-dom'; import CourseCelebration from './CourseCelebration'; +import messages from './messages'; import { useModel } from '../../../generic/model-store'; // These are taken from the edx-platform `get_cert_data` function found in lms/courseware/views/views.py @@ -13,7 +17,7 @@ const CELEBRATION_STATUSES = [ 'unverified', ]; -export default function CourseExit() { +function CourseExit({ intl }) { const { courseId } = useParams(); const { courseExitPageIsActive, @@ -31,8 +35,26 @@ export default function CourseExit() { } = certificateData; if (CELEBRATION_STATUSES.indexOf(certStatus) !== -1) { - return (); + return ( + <> +
+ +
+ + + ); } // Just to be safe return (); } + +CourseExit.propTypes = { + intl: intlShape.isRequired, +}; + +export default injectIntl(CourseExit); diff --git a/src/courseware/course/course-exit/assets/celebration_750x540.gif b/src/courseware/course/course-exit/assets/celebration_750x540.gif new file mode 100644 index 00000000..a6a4da05 Binary files /dev/null and b/src/courseware/course/course-exit/assets/celebration_750x540.gif differ diff --git a/src/courseware/course/course-exit/messages.js b/src/courseware/course/course-exit/messages.js index 938aca03..63417595 100644 --- a/src/courseware/course/course-exit/messages.js +++ b/src/courseware/course/course-exit/messages.js @@ -9,7 +9,7 @@ const messages = defineMessages({ certificateHeaderNotAvailable: { id: 'courseCelebration.certificateHeader.notAvailable', defaultMessage: 'Your certificate will be available soon!', - description: 'Text displayed when course certificate is not yet available to view', + description: 'Text displayed when course certificate is not yet available to be viewed', }, certificateHeaderUnverified: { id: 'courseCelebration.certificateHeader.unverified', @@ -21,55 +21,67 @@ const messages = defineMessages({ defaultMessage: 'Congratulations, you qualified for a certificate!', description: 'Text displayed when a user has completed the course and can request a certificate', }, + certificateImage: { + id: 'courseCelebration.certificateImage', + defaultMessage: 'Sample certificate', + description: 'Alt text used to describe an image of a certificate', + }, congratulationsHeader: { id: 'courseCelebration.congratulationsHeader', defaultMessage: 'Congratulations!', - description: 'Congratulations header on course completion page', + }, + congratulationsImage: { + id: 'courseCelebration.congratulationsImage', + defaultMessage: 'Four people raising their hands in celebration', + description: 'Alt text used to describe celebratory image', }, dashboardLink: { id: 'courseExit.dashboardLink', defaultMessage: 'Dashboard', - description: "Link to learner's dashboard", + description: "Link to user's dashboard", }, downloadButton: { id: 'courseCelebration.downloadButton', defaultMessage: 'Download my certificate', - description: 'Text for download button in certificate banner', + description: 'Button to download the course certificate', }, idVerificationSupportLink: { id: 'courseExit.idVerificationSupportLink', defaultMessage: 'Learn more about ID verification', - description: 'Link to help article about ID verification', + description: 'Link to an article about identity verification', }, profileLink: { id: 'courseExit.profileLink', defaultMessage: 'Profile', - description: 'Link to profile', + description: "Link to user's profile", }, requestCertificateBodyText: { id: 'courseCelebration.requestCertificateBodyText', defaultMessage: 'In order to access your certificate, request it below.', - description: 'Body text for certificate banner', }, requestCertificateButton: { id: 'courseCelebration.requestCertificateButton', defaultMessage: 'Request certificate', - description: 'Text for request certificate button in certificate banner', + description: 'Button to request the course certificate', }, shareHeader: { id: 'courseCelebration.shareHeader', defaultMessage: 'You have completed your course. Share your success on social media or email.', - description: 'Social media/email share header', }, verifyIdentityButton: { id: 'courseCelebration.verifyIdentityButton', - defaultMessage: 'Verify now', - description: 'Text for verify identity button in certificate banner', + defaultMessage: 'Verify ID now', + description: 'Button to verify the identify of the user', }, viewCertificateButton: { id: 'courseCelebration.viewCertificateButton', - defaultMessage: 'View now', - description: 'Text for view certificate button in certificate banner', + defaultMessage: 'View my certificate', + description: 'Button to view the course certificate', + }, + viewCoursesButton: { + id: 'courseExit.viewCoursesButton', + defaultMessage: 'View my courses', + description: 'Button to redirect user to their course dashboard', }, }); diff --git a/src/courseware/course/sequence/sequence-navigation/SequenceNavigation.jsx b/src/courseware/course/sequence/sequence-navigation/SequenceNavigation.jsx index e87b0612..9076fc2f 100644 --- a/src/courseware/course/sequence/sequence-navigation/SequenceNavigation.jsx +++ b/src/courseware/course/sequence/sequence-navigation/SequenceNavigation.jsx @@ -4,7 +4,7 @@ import { Button } from '@edx/paragon'; import classNames from 'classnames'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faChevronLeft, faChevronRight } from '@fortawesome/free-solid-svg-icons'; -import { FormattedMessage } from '@edx/frontend-platform/i18n'; +import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { useSelector } from 'react-redux'; import UnitButton from './UnitButton'; @@ -13,7 +13,10 @@ import { useSequenceNavigationMetadata } from './hooks'; import { useModel } from '../../../../generic/model-store'; import { LOADED } from '../../../data/slice'; -export default function SequenceNavigation({ +import messages from './messages'; + +function SequenceNavigation({ + intl, unitId, sequenceId, className, @@ -64,21 +67,9 @@ export default function SequenceNavigation({ // AA-198: The userHasPassingGrade condition can be removed once we have a view for learners with failing grades const disabled = isLastUnit && (!courseExitPageIsActive || !userHasPassingGrade); - let buttonText = ( - - ); + let buttonText = (intl.formatMessage(messages.nextButton)); if (isLastUnit && courseExitPageIsActive && userHasPassingGrade) { - buttonText = ( - - ); + buttonText = (intl.formatMessage(messages.completeCourseButton)); } // AA-198: Uncomment once there is a view for learners with failing grades // else if (isLastUnit && courseExitPageIsActive && !userHasPassingGrade) { @@ -107,11 +98,7 @@ export default function SequenceNavigation({