diff --git a/.env b/.env index a8774b81..83803d07 100644 --- a/.env +++ b/.env @@ -17,6 +17,7 @@ FAVICON_URL=null MARKETING_SITE_BASE_URL=null ORDER_HISTORY_URL=null REFRESH_ACCESS_TOKEN_ENDPOINT=null +SEARCH_CATALOG_URL=null SEGMENT_KEY=null SITE_NAME=null SOCIAL_UTM_MILESTONE_CAMPAIGN=null diff --git a/.env.development b/.env.development index 3475c068..bbfa8af4 100644 --- a/.env.development +++ b/.env.development @@ -17,6 +17,7 @@ MARKETING_SITE_BASE_URL='http://localhost:18000' ORDER_HISTORY_URL='http://localhost:1996/orders' PORT=2000 REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh' +SEARCH_CATALOG_URL='http://localhost:18000/courses' SEGMENT_KEY=null SITE_NAME='edX' SOCIAL_UTM_MILESTONE_CAMPAIGN='edxmilestone' diff --git a/.env.test b/.env.test index 09d05b40..09318fae 100644 --- a/.env.test +++ b/.env.test @@ -17,6 +17,7 @@ MARKETING_SITE_BASE_URL='http://localhost:18000' ORDER_HISTORY_URL='http://localhost:1996/orders' PORT=2000 REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh' +SEARCH_CATALOG_URL='http://localhost:18000/courses' SEGMENT_KEY=null SITE_NAME='edX' SOCIAL_UTM_MILESTONE_CAMPAIGN='edxmilestone' diff --git a/src/courseware/course/course-exit/CatalogSuggestion.jsx b/src/courseware/course/course-exit/CatalogSuggestion.jsx new file mode 100644 index 00000000..1ee21d89 --- /dev/null +++ b/src/courseware/course/course-exit/CatalogSuggestion.jsx @@ -0,0 +1,54 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { useSelector } from 'react-redux'; + +import { getConfig } from '@edx/frontend-platform'; +import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; +import { + FormattedMessage, injectIntl, intlShape, +} from '@edx/frontend-platform/i18n'; +import { Hyperlink } from '@edx/paragon'; +import { faSearch } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +import { useModel } from '../../../generic/model-store'; + +import messages from './messages'; +import { logClick } from './utils'; + +function CatalogSuggestion({ intl, variant }) { + const { courseId } = useSelector(state => state.courseware); + const { org } = useModel('courses', courseId); + const { administrator } = getAuthenticatedUser(); + + const searchOurCatalogLink = ( + logClick(org, courseId, administrator, 'catalog_search', { variant })} + > + {intl.formatMessage(messages.searchOurCatalogLink)} + + ); + + return ( +
+
+   + +
+
+ ); +} + +CatalogSuggestion.propTypes = { + intl: intlShape.isRequired, + variant: PropTypes.string.isRequired, +}; + +export default injectIntl(CatalogSuggestion); diff --git a/src/courseware/course/course-exit/CourseCelebration.jsx b/src/courseware/course/course-exit/CourseCelebration.jsx index db2f2ce5..f846fb00 100644 --- a/src/courseware/course/course-exit/CourseCelebration.jsx +++ b/src/courseware/course/course-exit/CourseCelebration.jsx @@ -12,6 +12,7 @@ import { Alert, Button, Hyperlink } from '@edx/paragon'; import { getConfig } from '@edx/frontend-platform'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; +import CatalogSuggestion from './CatalogSuggestion'; import CelebrationMobile from './assets/celebration_456x328.gif'; import CelebrationDesktop from './assets/celebration_750x540.gif'; import certificate from '../../../generic/assets/edX_verified_certificate.png'; @@ -119,7 +120,7 @@ function CourseCelebration({ intl }) { } buttonEvent = 'view_cert'; visitEvent = 'celebration_with_cert'; - footnote = ; + footnote = ; break; case 'earned_but_not_available': { const endDate = ; @@ -146,7 +147,7 @@ function CourseCelebration({ intl }) { ); visitEvent = 'celebration_with_unavailable_cert'; - footnote = ; + footnote = ; break; } case 'requesting': @@ -155,12 +156,12 @@ function CourseCelebration({ intl }) { certHeader = intl.formatMessage(messages.certificateHeaderRequestable); message = (

{intl.formatMessage(messages.requestCertificateBodyText)}

); visitEvent = 'celebration_with_requestable_cert'; - footnote = ; + footnote = ; break; case 'unverified': certHeader = intl.formatMessage(messages.certificateHeaderUnverified); visitEvent = 'celebration_unverified'; - footnote = ; + footnote = ; if (verificationStatus === 'pending') { message = (

{intl.formatMessage(messages.verificationPending)}

); } else { @@ -215,7 +216,7 @@ function CourseCelebration({ intl }) { if (verifiedMode.accessExpirationDate) { footnote = ; } else { - footnote = ; + footnote = ; } } break; @@ -323,6 +324,7 @@ function CourseCelebration({ intl }) { /> ))} {footnote} + diff --git a/src/courseware/course/course-exit/CourseNonPassing.jsx b/src/courseware/course/course-exit/CourseNonPassing.jsx index bb090611..bb15a4b8 100644 --- a/src/courseware/course/course-exit/CourseNonPassing.jsx +++ b/src/courseware/course/course-exit/CourseNonPassing.jsx @@ -9,6 +9,7 @@ import { getConfig } from '@edx/frontend-platform'; import { useModel } from '../../../generic/model-store'; +import CatalogSuggestion from './CatalogSuggestion'; import DashboardFootnote from './DashboardFootnote'; import messages from './messages'; import { logClick, logVisit } from './utils'; @@ -48,7 +49,8 @@ function CourseNonPassing({ intl }) { )} - + + ); diff --git a/src/courseware/course/course-exit/DashboardFootnote.jsx b/src/courseware/course/course-exit/DashboardFootnote.jsx index ab10c22c..512a816b 100644 --- a/src/courseware/course/course-exit/DashboardFootnote.jsx +++ b/src/courseware/course/course-exit/DashboardFootnote.jsx @@ -1,5 +1,8 @@ import React from 'react'; +import PropTypes from 'prop-types'; +import { useSelector } from 'react-redux'; +import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { FormattedMessage, injectIntl, intlShape, } from '@edx/frontend-platform/i18n'; @@ -7,15 +10,23 @@ import { Hyperlink } from '@edx/paragon'; import { faCalendarAlt } from '@fortawesome/free-regular-svg-icons'; import { getConfig } from '@edx/frontend-platform'; +import { useModel } from '../../../generic/model-store'; + import Footnote from './Footnote'; import messages from './messages'; +import { logClick } from './utils'; + +function DashboardFootnote({ intl, variant }) { + const { courseId } = useSelector(state => state.courseware); + const { org } = useModel('courses', courseId); + const { administrator } = getAuthenticatedUser(); -function DashboardFootnote({ intl }) { const dashboardLink = ( logClick(org, courseId, administrator, 'dashboard_footnote', { variant })} > {intl.formatMessage(messages.dashboardLink)} @@ -37,6 +48,7 @@ function DashboardFootnote({ intl }) { DashboardFootnote.propTypes = { intl: intlShape.isRequired, + variant: PropTypes.string.isRequired, }; export default injectIntl(DashboardFootnote); diff --git a/src/courseware/course/course-exit/messages.js b/src/courseware/course/course-exit/messages.js index 9d449d0f..01dcbc85 100644 --- a/src/courseware/course/course-exit/messages.js +++ b/src/courseware/course/course-exit/messages.js @@ -114,6 +114,11 @@ const messages = defineMessages({ defaultMessage: 'Request certificate', description: 'Button to request the course certificate', }, + searchOurCatalogLink: { + id: 'courseExit.searchOurCatalogLink', + defaultMessage: 'Search our catalog', + description: 'First part of a sentence that continues afterward', + }, shareHeader: { id: 'courseCelebration.shareHeader', defaultMessage: 'You have completed your course. Share your success on social media or email.', diff --git a/src/courseware/course/course-exit/utils.js b/src/courseware/course/course-exit/utils.js index d76b05ba..a89e2dcf 100644 --- a/src/courseware/course/course-exit/utils.js +++ b/src/courseware/course/course-exit/utils.js @@ -72,7 +72,7 @@ function getCourseExitText(courseId, intl) { // Meant to be used as part of a button's onClick handler. // For convenience, you can pass a falsy event and it will be ignored. -const logClick = (org, courseId, administrator, event) => { +const logClick = (org, courseId, administrator, event, extraProperties) => { if (!event) { return; } @@ -81,6 +81,7 @@ const logClick = (org, courseId, administrator, event) => { org_key: org, courserun_key: courseId, is_staff: administrator, + ...extraProperties, }); }; diff --git a/src/index.jsx b/src/index.jsx index 99100bf6..b9cd193d 100755 --- a/src/index.jsx +++ b/src/index.jsx @@ -79,7 +79,9 @@ initialize({ config: () => { mergeConfig({ CREDENTIALS_BASE_URL: process.env.CREDENTIALS_BASE_URL || null, + ENTERPRISE_LEARNER_PORTAL_HOSTNAME: process.env.ENTERPRISE_LEARNER_PORTAL_HOSTNAME || null, INSIGHTS_BASE_URL: process.env.INSIGHTS_BASE_URL || null, + SEARCH_CATALOG_URL: process.env.SEARCH_CATALOG_URL || null, SOCIAL_UTM_MILESTONE_CAMPAIGN: process.env.SOCIAL_UTM_MILESTONE_CAMPAIGN || null, STUDIO_BASE_URL: process.env.STUDIO_BASE_URL || null, SUPPORT_URL: process.env.SUPPORT_URL || null, @@ -88,7 +90,6 @@ initialize({ SUPPORT_URL_VERIFIED_CERTIFICATE: process.env.SUPPORT_URL_VERIFIED_CERTIFICATE || null, TWITTER_HASHTAG: process.env.TWITTER_HASHTAG || null, TWITTER_URL: process.env.TWITTER_URL || null, - ENTERPRISE_LEARNER_PORTAL_HOSTNAME: process.env.ENTERPRISE_LEARNER_PORTAL_HOSTNAME || null, }, 'LearnerAppConfig'); }, },