From cd1d3dd379151b268956926447dbe32e3788aebe Mon Sep 17 00:00:00 2001 From: Michael Terry Date: Thu, 29 Oct 2020 09:55:14 -0400 Subject: [PATCH] AA-402: send segment events for course-exit (#265) Specifically, a "I visited this page" event, with information on which variant was seen. And "I clicked this button" events, for our various calls to action. --- .../course/course-exit/CourseCelebration.jsx | 33 ++++++++++------ .../course/course-exit/CourseNonPassing.jsx | 14 ++++++- .../course/course-exit/UpgradeFootnote.jsx | 7 ++++ src/courseware/course/course-exit/utils.js | 38 ++++++++++++++++++- 4 files changed, 77 insertions(+), 15 deletions(-) diff --git a/src/courseware/course/course-exit/CourseCelebration.jsx b/src/courseware/course/course-exit/CourseCelebration.jsx index fe6c81d5..67933ffa 100644 --- a/src/courseware/course/course-exit/CourseCelebration.jsx +++ b/src/courseware/course/course-exit/CourseCelebration.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { FormattedDate, FormattedMessage, injectIntl, intlShape, @@ -9,7 +9,6 @@ import { useDispatch, useSelector } from 'react-redux'; import { LinkedinIcon } from 'react-share'; import { Alert, Button, Hyperlink } from '@edx/paragon'; import { getConfig } from '@edx/frontend-platform'; -import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import CelebrationMobile from './assets/celebration_456x328.gif'; @@ -22,6 +21,7 @@ import { requestCert } from '../../../course-home/data/thunks'; import DashboardFootnote from './DashboardFootnote'; import UpgradeFootnote from './UpgradeFootnote'; import SocialIcons from '../../social-share/SocialIcons'; +import { logClick, logVisit } from './utils'; const LINKEDIN_BLUE = '#007fb1'; @@ -81,22 +81,16 @@ function CourseCelebration({ intl }) { ); - const logClick = (service) => { - sendTrackEvent('edx.ui.lms.course_celebration.linkedin_add_to_profile.clicked', { - course_id: courseId, - is_staff: administrator, - service, - }); - }; - let buttonLocation; let buttonText; let buttonBackground = 'bg-white'; let buttonVariant = 'outline-primary'; + let buttonEvent = null; let certificateImage = certificate; let footnote; let message; let certHeader; + let visitEvent = 'celebration_generic'; // These cases are taken from the edx-platform `get_cert_data` function found in lms/courseware/views/views.py switch (certStatus) { case 'downloadable': @@ -120,6 +114,8 @@ function CourseCelebration({ intl }) { buttonLocation = downloadUrl; buttonText = intl.formatMessage(messages.downloadButton); } + buttonEvent = 'view_cert'; + visitEvent = 'celebration_with_cert'; footnote = ; break; case 'earned_but_not_available': { @@ -146,17 +142,21 @@ function CourseCelebration({ intl }) {

); + visitEvent = 'celebration_with_unavailable_cert'; footnote = ; break; } case 'requesting': buttonText = intl.formatMessage(messages.requestCertificateButton); + buttonEvent = 'request_cert'; certHeader = intl.formatMessage(messages.certificateHeaderRequestable); message = (

{intl.formatMessage(messages.requestCertificateBodyText)}

); + visitEvent = 'celebration_with_requestable_cert'; footnote = ; break; case 'unverified': buttonText = intl.formatMessage(messages.verifyIdentityButton); + buttonEvent = 'verify_id'; buttonLocation = verifyIdentityUrl; certHeader = intl.formatMessage(messages.certificateHeaderUnverified); // todo: check for idVerificationSupportLink null @@ -170,6 +170,7 @@ function CourseCelebration({ intl }) { />

); + visitEvent = 'celebration_unverified'; footnote = ; break; case 'audit_passing': @@ -200,10 +201,12 @@ function CourseCelebration({ intl }) {

); buttonText = intl.formatMessage(messages.upgradeButton); + buttonEvent = 'upgrade'; buttonLocation = verifiedMode.upgradeUrl; buttonBackground = ''; buttonVariant = 'primary'; certificateImage = certificateLocked; + visitEvent = 'celebration_upgrade'; if (verifiedMode.accessExpirationDate) { footnote = ; } else { @@ -215,6 +218,8 @@ function CourseCelebration({ intl }) { break; } + useEffect(() => logVisit(courseId, administrator, visitEvent), [courseId, administrator, visitEvent]); + return ( <> @@ -262,7 +267,10 @@ function CourseCelebration({ intl }) { @@ -271,7 +279,7 @@ function CourseCelebration({ intl }) { diff --git a/src/courseware/course/course-exit/CourseNonPassing.jsx b/src/courseware/course/course-exit/CourseNonPassing.jsx index d8721d80..bff7f6f7 100644 --- a/src/courseware/course/course-exit/CourseNonPassing.jsx +++ b/src/courseware/course/course-exit/CourseNonPassing.jsx @@ -1,5 +1,6 @@ -import React from 'react'; +import React, { useEffect } from 'react'; +import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Helmet } from 'react-helmet'; import { useSelector } from 'react-redux'; @@ -10,15 +11,19 @@ import { useModel } from '../../../generic/model-store'; import DashboardFootnote from './DashboardFootnote'; import messages from './messages'; +import { logClick, logVisit } from './utils'; function CourseNonPassing({ intl }) { const { courseId } = useSelector(state => state.courseware); const { tabs } = useModel('courses', courseId); + const { administrator } = getAuthenticatedUser(); // Get progress tab link for 'view grades' button const progressTab = tabs.find(tab => tab.slug === 'progress'); const progressLink = progressTab && progressTab.url; + useEffect(() => logVisit(courseId, administrator, 'nonpassing'), [courseId, administrator]); + return ( <> @@ -31,7 +36,12 @@ function CourseNonPassing({ intl }) {
{ intl.formatMessage(messages.endOfCourseDescription) }
{progressLink && ( - )} diff --git a/src/courseware/course/course-exit/UpgradeFootnote.jsx b/src/courseware/course/course-exit/UpgradeFootnote.jsx index 31a96afd..29ff2f06 100644 --- a/src/courseware/course/course-exit/UpgradeFootnote.jsx +++ b/src/courseware/course/course-exit/UpgradeFootnote.jsx @@ -1,6 +1,8 @@ import React from 'react'; +import { useSelector } from 'react-redux'; import PropTypes from 'prop-types'; +import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { FormattedDate, FormattedMessage, injectIntl, intlShape, } from '@edx/frontend-platform/i18n'; @@ -8,14 +10,19 @@ import { Hyperlink } from '@edx/paragon'; import { faCalendarAlt } from '@fortawesome/free-regular-svg-icons'; import Footnote from './Footnote'; +import { logClick } from './utils'; import messages from './messages'; function UpgradeFootnote({ deadline, href, intl }) { + const { courseId } = useSelector(state => state.courseware); + const { administrator } = getAuthenticatedUser(); + const upgradeLink = ( logClick(courseId, administrator, 'upgrade_footnote')} > {intl.formatMessage(messages.upgradeLink)} diff --git a/src/courseware/course/course-exit/utils.js b/src/courseware/course/course-exit/utils.js index df225fb2..ba8f7cb2 100644 --- a/src/courseware/course/course-exit/utils.js +++ b/src/courseware/course/course-exit/utils.js @@ -1,3 +1,5 @@ +import { sendTrackEvent } from '@edx/frontend-platform/analytics'; + import { useModel } from '../../../generic/model-store'; import messages from './messages'; @@ -68,4 +70,38 @@ function getCourseExitText(courseId, intl) { } } -export { COURSE_EXIT_MODES, getCourseExitMode, getCourseExitText }; +// 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 = (courseId, administrator, event) => { + if (!event) { + return; + } + + sendTrackEvent(`edx.ui.lms.course_exit.${event}.clicked`, { + course_id: courseId, + is_staff: administrator, + }); +}; + +// Use like the following to call this only once on initial page load: +// useEffect(() => logVisit(courseId, administrator, variant), [courseId, administrator, variant]); +// For convenience, you can pass a falsy variant and it will be ignored. +const logVisit = (courseId, administrator, variant) => { + if (!variant) { + return; + } + + sendTrackEvent('edx.ui.lms.course_exit.visited', { + course_id: courseId, + is_staff: administrator, + variant, + }); +}; + +export { + COURSE_EXIT_MODES, + getCourseExitMode, + getCourseExitText, + logClick, + logVisit, +};