diff --git a/src/course-home/outline-tab/OutlineTab.test.jsx b/src/course-home/outline-tab/OutlineTab.test.jsx index 460cfe4f..28f7f452 100644 --- a/src/course-home/outline-tab/OutlineTab.test.jsx +++ b/src/course-home/outline-tab/OutlineTab.test.jsx @@ -659,6 +659,40 @@ describe('Outline Tab', () => { await fetchAndRender(); expect(screen.queryByText('Your grade and certificate will be ready soon!')).toBeInTheDocument(); }); + it('renders verification alert', async () => { + const now = new Date(); + const yesterday = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); + const tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1); + setMetadata({ is_enrolled: true }); + setTabData({ + cert_data: { + cert_status: CERT_STATUS_TYPE.UNVERIFIED, + cert_web_view_url: null, + download_url: null, + }, + }, { + date_blocks: [ + { + date_type: 'course-end-date', + date: yesterday.toISOString(), + title: 'End', + }, + { + date_type: 'certificate-available-date', + date: tomorrow.toISOString(), + title: 'Cert Available', + }, + { + date_type: 'verification-deadline-date', + date: tomorrow.toISOString(), + link_text: 'Verify', + title: 'Verification Upgrade Deadline', + }, + ], + }); + await fetchAndRender(); + expect(screen.queryByText('Verify your identity to earn a certificate!')).toBeInTheDocument(); + }); }); }); diff --git a/src/course-home/outline-tab/alerts/certificate-status-alert/CertificateStatusAlert.jsx b/src/course-home/outline-tab/alerts/certificate-status-alert/CertificateStatusAlert.jsx index ea993e18..47a1be18 100644 --- a/src/course-home/outline-tab/alerts/certificate-status-alert/CertificateStatusAlert.jsx +++ b/src/course-home/outline-tab/alerts/certificate-status-alert/CertificateStatusAlert.jsx @@ -8,13 +8,15 @@ import { } from '@edx/frontend-platform/i18n'; import { Alert, Button } from '@edx/paragon'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faCheckCircle } from '@fortawesome/free-solid-svg-icons'; +import { faCheckCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; +import { getConfig } from '@edx/frontend-platform'; import certMessages from './messages'; import certStatusMessages from '../../../progress-tab/certificate-status/messages'; export const CERT_STATUS_TYPE = { EARNED_NOT_AVAILABLE: 'earned_but_not_available', DOWNLOADABLE: 'downloadable', + UNVERIFIED: 'unverified', }; function CertificateStatusAlert({ intl, payload }) { @@ -27,65 +29,109 @@ function CertificateStatusAlert({ intl, payload }) { userTimezone, } = payload; - let variant = ''; - if (certStatusType === CERT_STATUS_TYPE.EARNED_NOT_AVAILABLE || certStatusType === CERT_STATUS_TYPE.DOWNLOADABLE) { - variant = 'success'; - } + // eslint-disable-next-line react/prop-types + const AlertWrapper = (props) => props.children(props); - let header = ''; - let body = ''; - let buttonVisible = false; - let buttonMessage = ''; - - if (certStatusType === CERT_STATUS_TYPE.EARNED_NOT_AVAILABLE) { - const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {}; - const certificateAvailableDateFormatted = ; - const courseEndDateFormatted = ; - - header = intl.formatMessage(certMessages.certStatusEarnedNotAvailableHeader); - body = ( -

- -

- ); - } else if (certStatusType === CERT_STATUS_TYPE.DOWNLOADABLE) { - header = intl.formatMessage(certMessages.certStatusDownloadableHeader); - if (isWebCert) { - buttonMessage = intl.formatMessage(certStatusMessages.viewableButton); - } else { - buttonMessage = intl.formatMessage(certStatusMessages.downloadableButton); + const renderCertAwardedStatus = () => { + const alertProps = { + variant: 'success', + icon: faCheckCircle, + iconClassName: 'alert-icon text-success-500', + }; + if (certStatusType === CERT_STATUS_TYPE.EARNED_NOT_AVAILABLE) { + const timezoneFormatArgs = userTimezone ? { timeZone: userTimezone } : {}; + const certificateAvailableDateFormatted = ; + const courseEndDateFormatted = ; + alertProps.header = intl.formatMessage(certMessages.certStatusEarnedNotAvailableHeader); + alertProps.body = ( +

+ +

+ ); + } else if (certStatusType === CERT_STATUS_TYPE.DOWNLOADABLE) { + alertProps.header = intl.formatMessage(certMessages.certStatusDownloadableHeader); + if (isWebCert) { + alertProps.buttonMessage = intl.formatMessage(certStatusMessages.viewableButton); + } else { + alertProps.buttonMessage = intl.formatMessage(certStatusMessages.downloadableButton); + } + alertProps.buttonVisible = true; + alertProps.buttonLink = certURL; } - buttonVisible = true; + return alertProps; + }; + + const renderNotIDVerifiedStatus = () => { + const alertProps = { + variant: 'warning', + icon: faExclamationTriangle, + iconClassName: 'alert-icon text-warning-500', + header: intl.formatMessage(certStatusMessages.unverifiedHomeHeader), + buttonMessage: intl.formatMessage(certStatusMessages.unverifiedHomeButton), + body: intl.formatMessage(certStatusMessages.unverifiedHomeBody), + buttonVisible: true, + buttonLink: getConfig().SUPPORT_URL_ID_VERIFICATION, + }; + + return alertProps; + }; + + let alertProps = {}; + switch (certStatusType) { + case CERT_STATUS_TYPE.EARNED_NOT_AVAILABLE: + case CERT_STATUS_TYPE.DOWNLOADABLE: + alertProps = renderCertAwardedStatus(); + break; + case CERT_STATUS_TYPE.UNVERIFIED: + alertProps = renderNotIDVerifiedStatus(); + break; + default: + break; } + return ( - -
-
- - {header} - {body} -
- {buttonVisible && ( -
- + + {({ + variant, + buttonVisible, + iconClassName, + icon, + header, + buttonLink, + body, + buttonMessage, + }) => ( + +
+
+ + {header} + {body} +
+ {buttonVisible && ( +
+ +
+ )}
- )} -
- + + + )} + ); } diff --git a/src/course-home/outline-tab/alerts/certificate-status-alert/hooks.js b/src/course-home/outline-tab/alerts/certificate-status-alert/hooks.js index 99c6b241..2f2f130f 100644 --- a/src/course-home/outline-tab/alerts/certificate-status-alert/hooks.js +++ b/src/course-home/outline-tab/alerts/certificate-status-alert/hooks.js @@ -16,6 +16,9 @@ function verifyCertStatusType(status) { if (status === CERT_STATUS_TYPE.EARNED_NOT_AVAILABLE) { return CERT_STATUS_TYPE.EARNED_NOT_AVAILABLE; } + if (status === CERT_STATUS_TYPE.UNVERIFIED) { + return CERT_STATUS_TYPE.UNVERIFIED; + } return ''; } @@ -37,9 +40,7 @@ function useCertificateStatusAlert(courseId) { certificateAvailableDate, downloadUrl, } = certData || {}; - const endBlock = courseDateBlocks.find(b => b.dateType === 'course-end-date'); - const certStatusType = verifyCertStatusType(certStatus); const isWebCert = downloadUrl === null; diff --git a/src/course-home/progress-tab/certificate-status/messages.js b/src/course-home/progress-tab/certificate-status/messages.js index f8253eba..13dde252 100644 --- a/src/course-home/progress-tab/certificate-status/messages.js +++ b/src/course-home/progress-tab/certificate-status/messages.js @@ -77,6 +77,18 @@ const messages = defineMessages({ id: 'progress.certificateStatus.upgradeButton', defaultMessage: 'Upgrade now', }, + unverifiedHomeHeader: { + id: 'progress.certificateStatus.unverifiedHomeHeader', + defaultMessage: 'Verify your identity to earn a certificate!', + }, + unverifiedHomeButton: { + id: 'progress.certificateStatus.unverifiedHomeButton', + defaultMessage: 'Verify my ID', + }, + unverifiedHomeBody: { + id: 'progress.certificateStatus.unverifiedHomeBody', + defaultMessage: 'In order to generate a certificate for this course, you must complete the ID verification process.', + }, }); export default messages; diff --git a/src/setupTest.js b/src/setupTest.js index fdfcf91e..4fb05c6e 100755 --- a/src/setupTest.js +++ b/src/setupTest.js @@ -82,7 +82,7 @@ export function initializeMockApp() { roles: [], administrator: false, }, - SUPPORT_URL_ID_VERIFICATION: true, + SUPPORT_URL_ID_VERIFICATION: 'http://example.com', }); const loggingService = configureLogging(MockLoggingService, {