import React, { useEffect, useState } from 'react'; import { connect } from 'react-redux'; import { getConfig, snakeCaseObject } from '@edx/frontend-platform'; import { identifyAuthenticatedUser } from '@edx/frontend-platform/analytics'; import { AxiosJwtAuthService, configure as configureAuth, getAuthenticatedUser, } from '@edx/frontend-platform/auth'; import { useIntl } from '@edx/frontend-platform/i18n'; import { getLoggingService } from '@edx/frontend-platform/logging'; import { Alert, Form, Hyperlink, Spinner, StatefulButton, } from '@openedx/paragon'; import { Error } from '@openedx/paragon/icons'; import PropTypes from 'prop-types'; import { Helmet } from 'react-helmet'; import { useLocation } from 'react-router-dom'; import { saveUserProfile } from './data/actions'; import { welcomePageContextSelector } from './data/selectors'; import messages from './messages'; import ProgressiveProfilingPageModal from './ProgressiveProfilingPageModal'; import BaseContainer from '../base-container'; import { RedirectLogistration } from '../common-components'; import { getThirdPartyAuthContext } from '../common-components/data/actions'; import { COMPLETE_STATE, DEFAULT_REDIRECT_URL, DEFAULT_STATE, FAILURE_STATE, PENDING_STATE, } from '../data/constants'; 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(); const { getFieldDataFromBackend, submitState, showError, welcomePageContext, welcomePageContextApiStatus, } = props; const location = useLocation(); const registrationEmbedded = isHostAvailableInQueryParams(); const queryParams = getAllPossibleQueryParams(); const authenticatedUser = getAuthenticatedUser() || location.state?.authenticatedUser; const functionalCookiesConsent = isOneTrustFunctionalCookieEnabled(); const enablePostRegistrationRecommendations = ( getConfig().ENABLE_POST_REGISTRATION_RECOMMENDATIONS && functionalCookiesConsent ); const [registrationResult, setRegistrationResult] = useState({ redirectUrl: '' }); const [formFieldData, setFormFieldData] = useState({ fields: {}, extendedProfile: [] }); const [values, setValues] = useState({}); const [showModal, setShowModal] = useState(false); const [showRecommendationsPage, setShowRecommendationsPage] = useState(false); useEffect(() => { if (registrationEmbedded) { getFieldDataFromBackend({ is_welcome_page: true, next: queryParams?.next }); } else { configureAuth(AxiosJwtAuthService, { loggingService: getLoggingService(), config: getConfig() }); } }, [registrationEmbedded, getFieldDataFromBackend, queryParams?.next]); useEffect(() => { const registrationResponse = location.state?.registrationResult; if (registrationResponse) { setRegistrationResult(registrationResponse); setFormFieldData({ fields: location.state?.optionalFields.fields, extendedProfile: location.state?.optionalFields.extended_profile, }); } }, [location.state]); useEffect(() => { if (registrationEmbedded && Object.keys(welcomePageContext).includes('fields')) { setFormFieldData({ fields: welcomePageContext.fields, extendedProfile: welcomePageContext.extended_profile, }); const nextUrl = welcomePageContext.nextUrl ? welcomePageContext.nextUrl : getConfig().SEARCH_CATALOG_URL; setRegistrationResult({ redirectUrl: nextUrl }); } }, [registrationEmbedded, welcomePageContext]); useEffect(() => { if (authenticatedUser?.userId) { identifyAuthenticatedUser(authenticatedUser.userId); trackProgressiveProfilingPageViewed(); } }, [authenticatedUser]); useEffect(() => { if (!enablePostRegistrationRecommendations) { trackDisablePostRegistrationRecommendations( { functionalCookiesConsent, page: 'authn_recommendations' }, ); return; } if (registrationResult.redirectUrl && authenticatedUser?.userId) { const redirectQueryParams = getAllPossibleQueryParams(registrationResult.redirectUrl); if (!('enrollment_action' in redirectQueryParams || queryParams?.next)) { setShowRecommendationsPage(true); } } }, [ authenticatedUser, enablePostRegistrationRecommendations, functionalCookiesConsent, registrationResult.redirectUrl, queryParams?.next, ]); if ( !authenticatedUser || !(location.state?.registrationResult || registrationEmbedded) || welcomePageContextApiStatus === FAILURE_STATE || (welcomePageContextApiStatus === COMPLETE_STATE && !Object.keys(welcomePageContext).includes('fields')) ) { const DASHBOARD_URL = getConfig().LMS_BASE_URL.concat(DEFAULT_REDIRECT_URL); global.location.assign(DASHBOARD_URL); return null; } const handleSubmit = (e) => { e.preventDefault(); window.history.replaceState(location.state, null, ''); const payload = { ...values, extendedProfile: [] }; if (Object.keys(formFieldData.extendedProfile).length > 0) { formFieldData.extendedProfile.forEach(fieldName => { if (values[fieldName]) { payload.extendedProfile.push({ fieldName, fieldValue: values[fieldName] }); } delete payload[fieldName]; }); } props.saveUserProfile(authenticatedUser.username, snakeCaseObject(payload)); 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); trackProgressiveProfilingSkipLinkClick({ host: queryParams?.host || '', }); }; const onChangeHandler = (e) => { if (e.target.type === 'checkbox') { setValues({ ...values, [e.target.name]: e.target.checked }); } else { setValues({ ...values, [e.target.name]: e.target.value }); } }; const formFields = Object.keys(formFieldData.fields).map((fieldName) => { const fieldData = formFieldData.fields[fieldName]; return ( ); }); return ( {formatMessage(messages['progressive.profiling.page.title'], { siteName: getConfig().SITE_NAME })} {(props.shouldRedirect && welcomePageContext.nextUrl) && ( )} {props.shouldRedirect && ( )}
{registrationEmbedded && welcomePageContextApiStatus === PENDING_STATE ? ( ) : ( <>

{formatMessage(messages['progressive.profiling.page.heading'])}


{showError ? ( {formatMessage(messages['welcome.page.error.heading'])}

{formatMessage(messages['welcome.page.error.message'])}

) : null}
{formFields} {(getConfig().AUTHN_PROGRESSIVE_PROFILING_SUPPORT_LINK) && ( (trackProgressiveProfilingSupportLinkCLick())} > {formatMessage(messages['optional.fields.information.link'])} )}
e.preventDefault()} /> e.preventDefault()} />
)}
); }; ProgressiveProfiling.propTypes = { authenticatedUser: PropTypes.shape({ username: PropTypes.string, userId: PropTypes.number, fullName: PropTypes.string, }), showError: PropTypes.bool, shouldRedirect: PropTypes.bool, submitState: PropTypes.string, welcomePageContext: PropTypes.shape({ extended_profile: PropTypes.arrayOf(PropTypes.string), fields: PropTypes.shape({}), nextUrl: PropTypes.string, }), welcomePageContextApiStatus: PropTypes.string, // Actions getFieldDataFromBackend: PropTypes.func.isRequired, saveUserProfile: PropTypes.func.isRequired, }; ProgressiveProfiling.defaultProps = { authenticatedUser: {}, shouldRedirect: false, showError: false, submitState: DEFAULT_STATE, welcomePageContext: {}, welcomePageContextApiStatus: PENDING_STATE, }; const mapStateToProps = state => { const welcomePageStore = state.welcomePage; return { shouldRedirect: welcomePageStore.success, showError: welcomePageStore.showError, submitState: welcomePageStore.submitState, welcomePageContext: welcomePageContextSelector(state), welcomePageContextApiStatus: state.commonComponents.thirdPartyAuthApiStatus, }; }; export default connect( mapStateToProps, { saveUserProfile, getFieldDataFromBackend: getThirdPartyAuthContext, }, )(ProgressiveProfiling);