diff --git a/src/data/constants.js b/src/data/constants.js
index 90fdf75f..5adf4382 100644
--- a/src/data/constants.js
+++ b/src/data/constants.js
@@ -37,3 +37,4 @@ export const VALID_EMAIL_REGEX = '(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+
// things like auto-enrollment upon login and registration.
export const AUTH_PARAMS = ['course_id', 'enrollment_action', 'course_mode', 'email_opt_in', 'purchase_workflow', 'next', 'register_for_free', 'track', 'is_account_recovery', 'variant', 'host', 'cta'];
export const REDIRECT = 'redirect';
+export const APP_NAME = 'authn_mfe';
diff --git a/src/data/segment/utils.js b/src/data/segment/utils.js
new file mode 100644
index 00000000..fa84443d
--- /dev/null
+++ b/src/data/segment/utils.js
@@ -0,0 +1,37 @@
+/* eslint-disable import/prefer-default-export */
+import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
+
+import { APP_NAME } from '../constants';
+
+export const LINK_TIMEOUT = 300;
+
+/**
+ * Creates an event tracker function that sends a tracking event with the given name and options.
+ *
+ * @param {string} name - The name of the event to be tracked.
+ * @param {object} [options={}] - Additional options to be included with the event.
+ * @returns {function} - A function that, when called, sends the tracking event.
+ */
+export const createEventTracker = (name, options = {}) => () => sendTrackEvent(
+ name,
+ { ...options, app_name: APP_NAME },
+);
+
+/**
+ * Creates an event tracker function that sends a tracking event with the given name and options.
+ *
+ * @param {string} name - The name of the event to be tracked.
+ * @param {object} [options={}] - Additional options to be included with the event.
+ * @returns {function} - A function that, when called, sends the tracking event.
+ */
+export const createPageEventTracker = (name, options = null) => () => sendPageEvent(
+ name,
+ options,
+ { app_name: APP_NAME },
+);
+
+export const createLinkTracker = (tracker, href) => (e) => {
+ e.preventDefault();
+ tracker();
+ return setTimeout(() => { window.location.href = href; }, LINK_TIMEOUT);
+};
diff --git a/src/forgot-password/ForgotPasswordPage.jsx b/src/forgot-password/ForgotPasswordPage.jsx
index f4c7b314..fad31300 100644
--- a/src/forgot-password/ForgotPasswordPage.jsx
+++ b/src/forgot-password/ForgotPasswordPage.jsx
@@ -2,7 +2,6 @@ import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { getConfig } from '@edx/frontend-platform';
-import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Form,
@@ -25,6 +24,10 @@ import BaseContainer from '../base-container';
import { FormGroup } from '../common-components';
import { DEFAULT_STATE, LOGIN_PAGE, VALID_EMAIL_REGEX } from '../data/constants';
import { updatePathWithQueryParams, windowScrollTo } from '../data/utils';
+import {
+ trackForgotPasswordPageEvent,
+ trackForgotPasswordPageViewed,
+} from '../tracking/trackers/forgotpassword';
const ForgotPasswordPage = (props) => {
const platformName = getConfig().SITE_NAME;
@@ -41,8 +44,8 @@ const ForgotPasswordPage = (props) => {
const navigate = useNavigate();
useEffect(() => {
- sendPageEvent('login_and_registration', 'forgot-password');
- sendTrackEvent('edx.bi.password_reset_form.viewed', { category: 'user-engagement' });
+ trackForgotPasswordPageEvent();
+ trackForgotPasswordPageViewed();
}, []);
useEffect(() => {
diff --git a/src/login/LoginPage.jsx b/src/login/LoginPage.jsx
index 62815726..0ff25258 100644
--- a/src/login/LoginPage.jsx
+++ b/src/login/LoginPage.jsx
@@ -2,7 +2,6 @@ import React, { useEffect, useMemo, useState } from 'react';
import { connect } from 'react-redux';
import { getConfig } from '@edx/frontend-platform';
-import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
import { injectIntl, useIntl } from '@edx/frontend-platform/i18n';
import {
Form, StatefulButton,
@@ -43,6 +42,9 @@ import {
updatePathWithQueryParams,
} from '../data/utils';
import ResetPasswordSuccess from '../reset-password/ResetPasswordSuccess';
+import {
+ trackForgotPasswordLinkClick, trackLoginPageViewed, trackLoginSuccess,
+} from '../tracking/trackers/login';
const LoginPage = (props) => {
const {
@@ -78,9 +80,15 @@ const LoginPage = (props) => {
const tpaHint = getTpaHint();
useEffect(() => {
- sendPageEvent('login_and_registration', 'login');
+ trackLoginPageViewed();
}, []);
+ useEffect(() => {
+ if (loginResult.success) {
+ trackLoginSuccess();
+ }
+ }, [loginResult]);
+
useEffect(() => {
const payload = { ...queryParams };
if (tpaHint) {
@@ -170,9 +178,6 @@ const LoginPage = (props) => {
const { name } = event.target;
setErrors(prevErrors => ({ ...prevErrors, [name]: '' }));
};
- const trackForgotPasswordLinkClick = () => {
- sendTrackEvent('edx.bi.password-reset_form.toggled', { category: 'user-engagement' });
- };
const { provider, skipHintedLogin } = getTpaProvider(tpaHint, providers, secondaryProviders);
diff --git a/src/login/tests/LoginPage.test.jsx b/src/login/tests/LoginPage.test.jsx
index 9c337bf2..38778b8c 100644
--- a/src/login/tests/LoginPage.test.jsx
+++ b/src/login/tests/LoginPage.test.jsx
@@ -11,7 +11,9 @@ import { act } from 'react-dom/test-utils';
import { MemoryRouter } from 'react-router-dom';
import configureStore from 'redux-mock-store';
-import { COMPLETE_STATE, LOGIN_PAGE, PENDING_STATE } from '../../data/constants';
+import {
+ APP_NAME, COMPLETE_STATE, LOGIN_PAGE, PENDING_STATE,
+} from '../../data/constants';
import { backupLoginFormBegin, dismissPasswordResetBanner, loginRequest } from '../data/actions';
import { INTERNAL_SERVER_ERROR } from '../data/constants';
import LoginPage from '../LoginPage';
@@ -751,7 +753,7 @@ describe('LoginPage', () => {
it('should send page event when login page is rendered', () => {
render(reduxWrapper());
- expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'login');
+ expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'login', { app_name: APP_NAME });
});
it('tests that form is in invalid state when it is submitted', () => {
@@ -784,7 +786,7 @@ describe('LoginPage', () => {
{ selector: '#forgot-password' },
));
- expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.password-reset_form.toggled', { category: 'user-engagement' });
+ expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.password-reset_form.toggled', { category: 'user-engagement', app_name: APP_NAME });
});
it('should backup the login form state when shouldBackupState is true', () => {
diff --git a/src/logistration/Logistration.jsx b/src/logistration/Logistration.jsx
index 9451aa27..1a65b259 100644
--- a/src/logistration/Logistration.jsx
+++ b/src/logistration/Logistration.jsx
@@ -20,7 +20,7 @@ import {
tpaProvidersSelector,
} from '../common-components/data/selectors';
import messages from '../common-components/messages';
-import { LOGIN_PAGE, REGISTER_PAGE } from '../data/constants';
+import { APP_NAME, LOGIN_PAGE, REGISTER_PAGE } from '../data/constants';
import {
getTpaHint, getTpaProvider, updatePathWithQueryParams,
} from '../data/utils';
@@ -56,11 +56,11 @@ const Logistration = (props) => {
}, [navigate, disablePublicAccountCreation]);
const handleInstitutionLogin = (e) => {
- sendTrackEvent('edx.bi.institution_login_form.toggled', { category: 'user-engagement' });
+ sendTrackEvent('edx.bi.institution_login_form.toggled', { category: 'user-engagement', app_name: APP_NAME });
if (typeof e === 'string') {
- sendPageEvent('login_and_registration', e === '/login' ? 'login' : 'register');
+ sendPageEvent('login_and_registration', e === '/login' ? 'login' : 'register', { app_name: APP_NAME });
} else {
- sendPageEvent('login_and_registration', e.target.dataset.eventName);
+ sendPageEvent('login_and_registration', e.target.dataset.eventName, { app_name: APP_NAME });
}
setInstitutionLogin(!institutionLogin);
@@ -70,7 +70,7 @@ const Logistration = (props) => {
if (tabKey === currentTab) {
return;
}
- sendTrackEvent(`edx.bi.${tabKey.replace('/', '')}_form.toggled`, { category: 'user-engagement' });
+ sendTrackEvent(`edx.bi.${tabKey.replace('/', '')}_form.toggled`, { category: 'user-engagement', app_name: APP_NAME });
props.clearThirdPartyAuthContextErrorMessage();
if (tabKey === LOGIN_PAGE) {
props.backupRegistrationForm();
diff --git a/src/logistration/Logistration.test.jsx b/src/logistration/Logistration.test.jsx
index b16ae216..b4b0d1e9 100644
--- a/src/logistration/Logistration.test.jsx
+++ b/src/logistration/Logistration.test.jsx
@@ -11,6 +11,7 @@ import configureStore from 'redux-mock-store';
import Logistration from './Logistration';
import { clearThirdPartyAuthContextErrorMessage } from '../common-components/data/actions';
import {
+ APP_NAME,
COMPLETE_STATE, LOGIN_PAGE, REGISTER_PAGE,
} from '../data/constants';
import { backupLoginForm } from '../login/data/actions';
@@ -229,8 +230,8 @@ describe('Logistration', () => {
render(reduxWrapper());
fireEvent.click(screen.getByText('Institution/campus credentials'));
- expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.institution_login_form.toggled', { category: 'user-engagement' });
- expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'institution_login');
+ expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.institution_login_form.toggled', { category: 'user-engagement', app_name: APP_NAME });
+ expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'institution_login', { app_name: APP_NAME });
mergeConfig({
DISABLE_ENTERPRISE_LOGIN: '',
diff --git a/src/progressive-profiling/ProgressiveProfiling.jsx b/src/progressive-profiling/ProgressiveProfiling.jsx
index d42e3d52..55b96462 100644
--- a/src/progressive-profiling/ProgressiveProfiling.jsx
+++ b/src/progressive-profiling/ProgressiveProfiling.jsx
@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { getConfig, snakeCaseObject } from '@edx/frontend-platform';
-import { identifyAuthenticatedUser, sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
+import { identifyAuthenticatedUser } from '@edx/frontend-platform/analytics';
import {
AxiosJwtAuthService,
configure as configureAuth,
@@ -39,6 +39,13 @@ import {
import isOneTrustFunctionalCookieEnabled from '../data/oneTrust';
import { getAllPossibleQueryParams, isHostAvailableInQueryParams } from '../data/utils';
import { FormFieldRenderer } from '../field-renderer';
+import {
+ trackDisablePostRegistrationRecommendations,
+ trackProgressiveProfilingPageViewed,
+ trackProgressiveProfilingSkipLinkClick,
+ trackProgressiveProfilingSubmitClick,
+ trackProgressiveProfilingSupportLinkCLick,
+} from '../tracking/trackers/progressive-profiling';
const ProgressiveProfiling = (props) => {
const { formatMessage } = useIntl();
@@ -98,14 +105,13 @@ const ProgressiveProfiling = (props) => {
useEffect(() => {
if (authenticatedUser?.userId) {
identifyAuthenticatedUser(authenticatedUser.userId);
- sendPageEvent('login_and_registration', 'welcome');
+ trackProgressiveProfilingPageViewed();
}
}, [authenticatedUser]);
useEffect(() => {
if (!enablePostRegistrationRecommendations) {
- sendTrackEvent(
- 'edx.bi.user.recommendations.not.enabled',
+ trackDisablePostRegistrationRecommendations(
{ functionalCookiesConsent, page: 'authn_recommendations' },
);
return;
@@ -149,29 +155,23 @@ const ProgressiveProfiling = (props) => {
});
}
props.saveUserProfile(authenticatedUser.username, snakeCaseObject(payload));
-
- sendTrackEvent(
- 'edx.bi.welcome.page.submit.clicked',
- {
- isGenderSelected: !!values.gender,
- isYearOfBirthSelected: !!values.year_of_birth,
- isLevelOfEducationSelected: !!values.level_of_education,
- isWorkExperienceSelected: !!values.work_experience,
- host: queryParams?.host || '',
- },
- );
+ const eventProperties = {
+ isGenderSelected: !!values.gender,
+ isYearOfBirthSelected: !!values.year_of_birth,
+ isLevelOfEducationSelected: !!values.level_of_education,
+ isWorkExperienceSelected: !!values.work_experience,
+ host: queryParams?.host || '',
+ };
+ trackProgressiveProfilingSubmitClick(eventProperties);
};
const handleSkip = (e) => {
e.preventDefault();
window.history.replaceState(location.state, null, '');
setShowModal(true);
- sendTrackEvent(
- 'edx.bi.welcome.page.skip.link.clicked',
- {
- host: queryParams?.host || '',
- },
- );
+ trackProgressiveProfilingSkipLinkClick({
+ host: queryParams?.host || '',
+ });
};
const onChangeHandler = (e) => {
@@ -242,7 +242,7 @@ const ProgressiveProfiling = (props) => {
destination={getConfig().AUTHN_PROGRESSIVE_PROFILING_SUPPORT_LINK}
target="_blank"
showLaunchIcon={false}
- onClick={() => (sendTrackEvent('edx.bi.welcome.page.support.link.clicked'))}
+ onClick={() => (trackProgressiveProfilingSupportLinkCLick())}
>
{formatMessage(messages['optional.fields.information.link'])}
diff --git a/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx b/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx
index 9bc4439c..c5786b2e 100644
--- a/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx
+++ b/src/progressive-profiling/tests/ProgressiveProfiling.test.jsx
@@ -12,6 +12,7 @@ import { MemoryRouter, mockNavigate, useLocation } from 'react-router-dom';
import configureStore from 'redux-mock-store';
import {
+ APP_NAME,
AUTHN_PROGRESSIVE_PROFILING,
COMPLETE_STATE, DEFAULT_REDIRECT_URL,
EMBEDDED,
@@ -143,8 +144,9 @@ describe('ProgressiveProfilingTests', () => {
const modalContentContainer = document.getElementsByClassName('.pgn__modal-content-container');
expect(modalContentContainer).toBeTruthy();
+ const payload = { host: '', app_name: APP_NAME };
- expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.skip.link.clicked', { host: '' });
+ expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.skip.link.clicked', payload);
});
// ******** test event functionality ********
@@ -165,7 +167,7 @@ describe('ProgressiveProfilingTests', () => {
const supportLink = screen.getByRole('link', { name: /learn more about how we use this information/i });
fireEvent.click(supportLink);
- expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.support.link.clicked');
+ expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.support.link.clicked', { app_name: APP_NAME });
});
it('should set empty host property value for non-embedded experience', () => {
@@ -175,6 +177,7 @@ describe('ProgressiveProfilingTests', () => {
isLevelOfEducationSelected: false,
isWorkExperienceSelected: false,
host: '',
+ app_name: APP_NAME,
};
delete window.location;
window.location = { href: getConfig().BASE_URL.concat(AUTHN_PROGRESSIVE_PROFILING) };
@@ -316,7 +319,7 @@ describe('ProgressiveProfilingTests', () => {
const skipLinkButton = screen.getByText('Skip for now');
fireEvent.click(skipLinkButton);
- expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.skip.link.clicked', { host });
+ expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.welcome.page.skip.link.clicked', { host, app_name: APP_NAME });
});
it('should show spinner while fetching the optional fields', () => {
@@ -349,6 +352,7 @@ describe('ProgressiveProfilingTests', () => {
isLevelOfEducationSelected: false,
isWorkExperienceSelected: false,
host: 'http://example.com',
+ app_name: APP_NAME,
};
delete window.location;
window.location = {
diff --git a/src/register/RegistrationPage.jsx b/src/register/RegistrationPage.jsx
index 22f12e14..0a0a5b53 100644
--- a/src/register/RegistrationPage.jsx
+++ b/src/register/RegistrationPage.jsx
@@ -4,7 +4,6 @@ import React, {
import { useDispatch, useSelector } from 'react-redux';
import { getConfig } from '@edx/frontend-platform';
-import { sendPageEvent, sendTrackEvent } from '@edx/frontend-platform/analytics';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Form, Spinner, StatefulButton } from '@openedx/paragon';
import classNames from 'classnames';
@@ -44,11 +43,12 @@ import { getThirdPartyAuthContext as getRegistrationDataFromBackend } from '../c
import EnterpriseSSO from '../common-components/EnterpriseSSO';
import ThirdPartyAuth from '../common-components/ThirdPartyAuth';
import {
- COMPLETE_STATE, PENDING_STATE, REGISTER_PAGE,
+ APP_NAME, COMPLETE_STATE, PENDING_STATE, REGISTER_PAGE,
} from '../data/constants';
import {
getAllPossibleQueryParams, getTpaHint, getTpaProvider, isHostAvailableInQueryParams, setCookie,
} from '../data/utils';
+import { trackRegistrationPageViewed, trackRegistrationSuccess } from '../tracking/trackers/register';
/**
* Main Registration Page component
@@ -138,7 +138,7 @@ const RegistrationPage = (props) => {
useEffect(() => {
if (!formStartTime) {
- sendPageEvent('login_and_registration', 'register');
+ trackRegistrationPageViewed();
const payload = { ...queryParams, is_register_page: true };
if (tpaHint) {
payload.tpa_hint = tpaHint;
@@ -183,7 +183,7 @@ const RegistrationPage = (props) => {
useEffect(() => {
if (registrationResult.success) {
// This event is used by GTM
- sendTrackEvent('edx.bi.user.account.registered.client', {});
+ trackRegistrationSuccess();
// This is used by the "User Retention Rate Event" on GTM
setCookie(getConfig().USER_RETENTION_COOKIE_NAME, true);
@@ -222,7 +222,7 @@ const RegistrationPage = (props) => {
const registerUser = () => {
const totalRegistrationTime = (Date.now() - formStartTime) / 1000;
- let payload = { ...formFields };
+ let payload = { ...formFields, app_name: APP_NAME };
if (currentProvider) {
delete payload.password;
diff --git a/src/register/RegistrationPage.test.jsx b/src/register/RegistrationPage.test.jsx
index 9773bf03..5633c753 100644
--- a/src/register/RegistrationPage.test.jsx
+++ b/src/register/RegistrationPage.test.jsx
@@ -22,7 +22,7 @@ import useAutoGeneratedUsernameExperimentVariation
from './data/optimizelyExperiment/useAutoGeneratedUsernameExperimentVariation';
import RegistrationPage from './RegistrationPage';
import {
- AUTHN_PROGRESSIVE_PROFILING, COMPLETE_STATE, PENDING_STATE, REGISTER_PAGE,
+ APP_NAME, AUTHN_PROGRESSIVE_PROFILING, COMPLETE_STATE, PENDING_STATE, REGISTER_PAGE,
} from '../data/constants';
jest.mock('@edx/frontend-platform/analytics', () => ({
@@ -190,6 +190,7 @@ describe('RegistrationPage', () => {
honor_code: true,
totalRegistrationTime: 0,
next: '/course/demo-course-url',
+ app_name: APP_NAME,
};
store.dispatch = jest.fn(store.dispatch);
@@ -212,6 +213,7 @@ describe('RegistrationPage', () => {
honor_code: true,
social_auth_provider: 'Apple',
totalRegistrationTime: 0,
+ app_name: APP_NAME,
};
store = mockStore({
@@ -297,6 +299,7 @@ describe('RegistrationPage', () => {
honor_code: true,
totalRegistrationTime: 0,
marketing_emails_opt_in: true,
+ app_name: APP_NAME,
};
store.dispatch = jest.fn(store.dispatch);
@@ -323,6 +326,7 @@ describe('RegistrationPage', () => {
country: 'Pakistan',
honor_code: true,
totalRegistrationTime: 0,
+ app_name: APP_NAME,
};
store.dispatch = jest.fn(store.dispatch);
@@ -597,7 +601,7 @@ describe('RegistrationPage', () => {
it('should send page event when register page is rendered', () => {
render(routerWrapper(reduxWrapper()));
- expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'register');
+ expect(sendPageEvent).toHaveBeenCalledWith('login_and_registration', 'register', { app_name: APP_NAME });
});
it('should send track event when user has successfully registered', () => {
@@ -615,7 +619,7 @@ describe('RegistrationPage', () => {
delete window.location;
window.location = { href: getConfig().BASE_URL };
render(routerWrapper(reduxWrapper()));
- expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.user.account.registered.client', {});
+ expect(sendTrackEvent).toHaveBeenCalledWith('edx.bi.user.account.registered.client', { app_name: APP_NAME });
});
it('should populate form with pipeline user details', () => {
@@ -888,6 +892,7 @@ describe('RegistrationPage', () => {
country: 'PK',
social_auth_provider: 'Apple',
totalRegistrationTime: 0,
+ app_name: APP_NAME,
}));
});
});
diff --git a/src/register/components/tests/ConfigurableRegistrationForm.test.jsx b/src/register/components/tests/ConfigurableRegistrationForm.test.jsx
index 93978509..670415f7 100644
--- a/src/register/components/tests/ConfigurableRegistrationForm.test.jsx
+++ b/src/register/components/tests/ConfigurableRegistrationForm.test.jsx
@@ -9,6 +9,7 @@ import { fireEvent, render } from '@testing-library/react';
import { BrowserRouter as Router } from 'react-router-dom';
import configureStore from 'redux-mock-store';
+import { APP_NAME } from '../../../data/constants';
import { registerNewUser } from '../../data/actions';
import { FIELDS } from '../../data/constants';
import { NOT_INITIALIZED } from '../../data/optimizelyExperiment/helper';
@@ -265,7 +266,7 @@ describe('ConfigurableRegistrationForm', () => {
fireEvent.click(submitButton);
- expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload, country: 'PK' }));
+ expect(store.dispatch).toHaveBeenCalledWith(registerNewUser({ ...payload, country: 'PK', app_name: APP_NAME }));
});
it('should show error messages for required fields on empty form submission', () => {
diff --git a/src/reset-password/ResetPasswordPage.jsx b/src/reset-password/ResetPasswordPage.jsx
index 0393d279..ceb37e8c 100644
--- a/src/reset-password/ResetPasswordPage.jsx
+++ b/src/reset-password/ResetPasswordPage.jsx
@@ -2,7 +2,6 @@ import React, { useEffect, useState } from 'react';
import { connect } from 'react-redux';
import { getConfig } from '@edx/frontend-platform';
-import { sendPageEvent } from '@edx/frontend-platform/analytics';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Form,
@@ -19,7 +18,7 @@ import { useNavigate, useParams } from 'react-router-dom';
import { resetPassword, validateToken } from './data/actions';
import {
- FORM_SUBMISSION_ERROR, PASSWORD_RESET_ERROR, PASSWORD_VALIDATION_ERROR, TOKEN_STATE,
+ FORM_SUBMISSION_ERROR, PASSWORD_RESET_ERROR, PASSWORD_VALIDATION_ERROR, SUCCESS, TOKEN_STATE,
} from './data/constants';
import { resetPasswordResultSelector } from './data/selectors';
import { validatePassword } from './data/service';
@@ -31,6 +30,7 @@ import {
LETTER_REGEX, LOGIN_PAGE, NUMBER_REGEX, RESET_PAGE,
} from '../data/constants';
import { getAllPossibleQueryParams, updatePathWithQueryParams, windowScrollTo } from '../data/utils';
+import { trackPasswordResetSuccess, trackResetPasswordPageViewed } from '../tracking/trackers/reset-password';
const ResetPasswordPage = (props) => {
const { formatMessage } = useIntl();
@@ -44,8 +44,13 @@ const ResetPasswordPage = (props) => {
const navigate = useNavigate();
useEffect(() => {
- sendPageEvent('login_and_registration', 'reset-password');
- }, []);
+ if (props.status === TOKEN_STATE.VALID) {
+ trackResetPasswordPageViewed();
+ }
+ if (props.status === SUCCESS) {
+ trackPasswordResetSuccess();
+ }
+ }, [props.status]);
useEffect(() => {
if (props.status !== TOKEN_STATE.PENDING && props.status !== PASSWORD_RESET_ERROR) {
@@ -144,7 +149,7 @@ const ResetPasswordPage = (props) => {
}
} else if (props.status === PASSWORD_RESET_ERROR) {
navigate(updatePathWithQueryParams(RESET_PAGE));
- } else if (props.status === 'success') {
+ } else if (props.status === SUCCESS) {
navigate(updatePathWithQueryParams(LOGIN_PAGE));
} else {
return (
diff --git a/src/reset-password/tests/ResetPasswordPage.test.jsx b/src/reset-password/tests/ResetPasswordPage.test.jsx
index 0c9ba1a2..f3619be7 100644
--- a/src/reset-password/tests/ResetPasswordPage.test.jsx
+++ b/src/reset-password/tests/ResetPasswordPage.test.jsx
@@ -21,6 +21,7 @@ const token = '1c-bmjdkc-5e60e084cf8113048ca7';
jest.mock('@edx/frontend-platform/analytics', () => ({
sendPageEvent: jest.fn(),
+ sendTrackEvent: jest.fn(),
}));
jest.mock('@edx/frontend-platform/auth');
diff --git a/src/tracking/trackers/forgotpassword.js b/src/tracking/trackers/forgotpassword.js
new file mode 100644
index 00000000..3918478d
--- /dev/null
+++ b/src/tracking/trackers/forgotpassword.js
@@ -0,0 +1,22 @@
+import { createEventTracker, createPageEventTracker } from '../../data/segment/utils';
+
+export const eventNames = {
+ loginAndRegistration: 'login_and_registration',
+ forgotPasswordPageViewed: 'edx.bi.password_reset_form.viewed',
+};
+
+export const categories = {
+ userEngagement: 'user-engagement',
+};
+
+// Event tracker for forgot password page viewed
+export const trackForgotPasswordPageViewed = () => createEventTracker(
+ eventNames.forgotPasswordPageViewed,
+ {
+ category: categories.userEngagement,
+ },
+)();
+
+export const trackForgotPasswordPageEvent = () => {
+ createPageEventTracker(eventNames.loginAndRegistration, 'forgot-password')();
+};
diff --git a/src/tracking/trackers/login.js b/src/tracking/trackers/login.js
new file mode 100644
index 00000000..3c6a4a2a
--- /dev/null
+++ b/src/tracking/trackers/login.js
@@ -0,0 +1,29 @@
+import { createEventTracker, createPageEventTracker } from '../../data/segment/utils';
+
+export const eventNames = {
+ forgotPasswordLinkClicked: 'edx.bi.password-reset_form.toggled',
+ loginAndRegistration: 'login_and_registration',
+ registerFormToggled: 'edx.bi.register_form.toggled',
+ loginSuccess: 'edx.bi.user.account.authenticated.client',
+};
+
+export const categories = {
+ userEngagement: 'user-engagement',
+};
+
+// Event tracker for Forgot Password link click
+export const trackForgotPasswordLinkClick = () => createEventTracker(
+ eventNames.forgotPasswordLinkClicked,
+ { category: categories.userEngagement },
+)();
+
+// Tracks the login page event.
+export const trackLoginPageViewed = () => {
+ createPageEventTracker(eventNames.loginAndRegistration, 'login')();
+};
+
+// Tracks the login sucess event.
+export const trackLoginSuccess = () => createEventTracker(
+ eventNames.loginSuccess,
+ {},
+)();
diff --git a/src/tracking/trackers/progressive-profiling.js b/src/tracking/trackers/progressive-profiling.js
new file mode 100644
index 00000000..11a6f53e
--- /dev/null
+++ b/src/tracking/trackers/progressive-profiling.js
@@ -0,0 +1,37 @@
+import { createEventTracker, createPageEventTracker } from '../../data/segment/utils';
+
+export const eventNames = {
+ progressiveProfilingSubmitClick: 'edx.bi.welcome.page.submit.clicked',
+ progressiveProfilingSkipLinkClick: 'edx.bi.welcome.page.skip.link.clicked',
+ disablePostRegistrationRecommendations: 'edx.bi.user.recommendations.not.enabled',
+ progressiveProfilingSupportLinkCLick: 'edx.bi.welcome.page.support.link.clicked',
+ loginAndRegistration: 'login_and_registration',
+};
+
+// Event link tracker for Progressive profiling skip button click
+export const trackProgressiveProfilingSkipLinkClick = evenProperties => createEventTracker(
+ eventNames.progressiveProfilingSkipLinkClick, { ...evenProperties },
+)();
+
+// Event tracker for progressive profiling submit button click
+export const trackProgressiveProfilingSubmitClick = (evenProperties) => createEventTracker(
+ eventNames.progressiveProfilingSubmitClick,
+ { ...evenProperties },
+)();
+
+// Event tracker for progressive profiling submit button click
+export const trackDisablePostRegistrationRecommendations = (evenProperties) => createEventTracker(
+ eventNames.disablePostRegistrationRecommendations,
+ { ...evenProperties },
+)();
+
+// Tracks the progressive profiling page event.
+export const trackProgressiveProfilingPageViewed = () => {
+ createPageEventTracker(eventNames.loginAndRegistration, 'welcome')();
+};
+
+// Tracks the progressive profiling spport link click.
+export const trackProgressiveProfilingSupportLinkCLick = () => createEventTracker(
+ eventNames.progressiveProfilingSupportLinkCLick,
+ {},
+)();
diff --git a/src/tracking/trackers/register.js b/src/tracking/trackers/register.js
new file mode 100644
index 00000000..3a860856
--- /dev/null
+++ b/src/tracking/trackers/register.js
@@ -0,0 +1,22 @@
+import { createEventTracker, createPageEventTracker } from '../../data/segment/utils';
+
+export const eventNames = {
+ loginAndRegistration: 'login_and_registration',
+ registrationSuccess: 'edx.bi.user.account.registered.client',
+ loginFormToggled: 'edx.bi.login_form.toggled',
+};
+
+export const categories = {
+ userEngagement: 'user-engagement',
+};
+
+// Event tracker for successful registration
+export const trackRegistrationSuccess = () => createEventTracker(
+ eventNames.registrationSuccess,
+ {},
+)();
+
+// Tracks the progressive profiling page event.
+export const trackRegistrationPageViewed = () => {
+ createPageEventTracker(eventNames.loginAndRegistration, 'register')();
+};
diff --git a/src/tracking/trackers/reset-password.js b/src/tracking/trackers/reset-password.js
new file mode 100644
index 00000000..cb79340f
--- /dev/null
+++ b/src/tracking/trackers/reset-password.js
@@ -0,0 +1,14 @@
+import { createEventTracker, createPageEventTracker } from '../../data/segment/utils';
+
+export const eventNames = {
+ loginAndRegistration: 'login_and_registration',
+ resetPasswordSuccess: 'edx.bi.user.password.reset.success',
+};
+
+export const trackResetPasswordPageViewed = () => {
+ createPageEventTracker(eventNames.loginAndRegistration, 'reset-password')();
+};
+
+export const trackPasswordResetSuccess = () => {
+ createEventTracker(eventNames.resetPasswordSuccess, {})();
+};
diff --git a/src/tracking/trackers/tests/forgot-password.test.jsx b/src/tracking/trackers/tests/forgot-password.test.jsx
new file mode 100644
index 00000000..6a9b87c4
--- /dev/null
+++ b/src/tracking/trackers/tests/forgot-password.test.jsx
@@ -0,0 +1,37 @@
+import { createEventTracker, createPageEventTracker } from '../../../data/segment/utils';
+import {
+ categories,
+ eventNames,
+ trackForgotPasswordPageEvent,
+ trackForgotPasswordPageViewed,
+} from '../forgotpassword';
+
+// Mock createEventTracker function
+jest.mock('../../../data/segment/utils', () => ({
+ createEventTracker: jest.fn().mockImplementation(() => jest.fn()),
+ createPageEventTracker: jest.fn().mockImplementation(() => jest.fn()),
+}));
+
+describe('Tracking Functions', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should fire trackForgotPasswordPageEvent', () => {
+ trackForgotPasswordPageEvent();
+
+ expect(createPageEventTracker).toHaveBeenCalledWith(
+ eventNames.loginAndRegistration,
+ 'forgot-password',
+ );
+ });
+
+ it('should fire forgotPasswordPageViewedEvent', () => {
+ trackForgotPasswordPageViewed();
+
+ expect(createEventTracker).toHaveBeenCalledWith(
+ eventNames.forgotPasswordPageViewed,
+ { category: categories.userEngagement },
+ );
+ });
+});
diff --git a/src/tracking/trackers/tests/login.test.jsx b/src/tracking/trackers/tests/login.test.jsx
new file mode 100644
index 00000000..fac7d082
--- /dev/null
+++ b/src/tracking/trackers/tests/login.test.jsx
@@ -0,0 +1,37 @@
+import { createEventTracker, createPageEventTracker } from '../../../data/segment/utils';
+import {
+ categories,
+ eventNames,
+ trackForgotPasswordLinkClick,
+ trackLoginPageViewed,
+} from '../login';
+
+// Mock createEventTracker function
+jest.mock('../../../data/segment/utils', () => ({
+ createEventTracker: jest.fn().mockImplementation(() => jest.fn()),
+ createPageEventTracker: jest.fn().mockImplementation(() => jest.fn()),
+}));
+
+describe('Tracking Functions', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('trackForgotPasswordLinkClick function', () => {
+ trackForgotPasswordLinkClick();
+
+ expect(createEventTracker).toHaveBeenCalledWith(
+ eventNames.forgotPasswordLinkClicked,
+ { category: categories.userEngagement },
+ );
+ });
+
+ it('trackLoginPageEvent function', () => {
+ trackLoginPageViewed();
+
+ expect(createPageEventTracker).toHaveBeenCalledWith(
+ eventNames.loginAndRegistration,
+ 'login',
+ );
+ });
+});
diff --git a/src/tracking/trackers/tests/progressive-profiling.test.jsx b/src/tracking/trackers/tests/progressive-profiling.test.jsx
new file mode 100644
index 00000000..4cda593d
--- /dev/null
+++ b/src/tracking/trackers/tests/progressive-profiling.test.jsx
@@ -0,0 +1,37 @@
+import { createEventTracker, createPageEventTracker } from '../../../data/segment/utils';
+import {
+ eventNames,
+ trackProgressiveProfilingPageViewed,
+ trackProgressiveProfilingSkipLinkClick,
+} from '../progressive-profiling';
+
+// Mock createEventTracker function
+jest.mock('../../../data/segment/utils', () => ({
+ createEventTracker: jest.fn().mockImplementation(() => jest.fn()),
+ createPageEventTracker: jest.fn().mockImplementation(() => jest.fn()),
+ createLinkTracker: jest.fn().mockImplementation(() => jest.fn()),
+}));
+
+describe('Tracking Functions', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should fire trackProgressiveProfilingSkipLinkClickEvent', () => {
+ trackProgressiveProfilingSkipLinkClick();
+
+ expect(createEventTracker).toHaveBeenCalledWith(
+ eventNames.progressiveProfilingSkipLinkClick,
+ {},
+ );
+ });
+
+ it('should fire trackProgressiveProfilingPageEvent', () => {
+ trackProgressiveProfilingPageViewed();
+
+ expect(createPageEventTracker).toHaveBeenCalledWith(
+ eventNames.loginAndRegistration,
+ 'welcome',
+ );
+ });
+});
diff --git a/src/tracking/trackers/tests/register.test.jsx b/src/tracking/trackers/tests/register.test.jsx
new file mode 100644
index 00000000..d058908f
--- /dev/null
+++ b/src/tracking/trackers/tests/register.test.jsx
@@ -0,0 +1,36 @@
+import { createEventTracker, createPageEventTracker } from '../../../data/segment/utils';
+import {
+ eventNames,
+ trackRegistrationPageViewed,
+ trackRegistrationSuccess,
+} from '../register';
+
+// Mock createEventTracker function
+jest.mock('../../../data/segment/utils', () => ({
+ createEventTracker: jest.fn().mockImplementation(() => jest.fn()),
+ createPageEventTracker: jest.fn().mockImplementation(() => jest.fn()),
+}));
+
+describe('Tracking Functions', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should fire registrationSuccessEvent', () => {
+ trackRegistrationSuccess();
+
+ expect(createEventTracker).toHaveBeenCalledWith(
+ eventNames.registrationSuccess,
+ {},
+ );
+ });
+
+ it('should fire trackRegistrationPageEvent', () => {
+ trackRegistrationPageViewed();
+
+ expect(createPageEventTracker).toHaveBeenCalledWith(
+ eventNames.loginAndRegistration,
+ 'register',
+ );
+ });
+});
diff --git a/src/tracking/trackers/tests/reset-password.test.jsx b/src/tracking/trackers/tests/reset-password.test.jsx
new file mode 100644
index 00000000..15d6ed79
--- /dev/null
+++ b/src/tracking/trackers/tests/reset-password.test.jsx
@@ -0,0 +1,26 @@
+import { createPageEventTracker } from '../../../data/segment/utils';
+import {
+ eventNames,
+ trackResetPasswordPageViewed,
+} from '../reset-password';
+
+// Mock createEventTracker function
+jest.mock('../../../data/segment/utils', () => ({
+ createEventTracker: jest.fn().mockImplementation(() => jest.fn()),
+ createPageEventTracker: jest.fn().mockImplementation(() => jest.fn()),
+}));
+
+describe('Tracking Functions', () => {
+ beforeEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should fire trackResettPasswordPageEvent', () => {
+ trackResetPasswordPageViewed();
+
+ expect(createPageEventTracker).toHaveBeenCalledWith(
+ eventNames.loginAndRegistration,
+ 'reset-password',
+ );
+ });
+});