import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; import messages from './messages'; import { useModel } from '../../../generic/model-store'; const COURSE_EXIT_MODES = { disabled: 0, celebration: 1, nonPassing: 2, inProgress: 3, entranceExamFail: 4, }; // These are taken from the edx-platform `get_cert_data` function found in lms/courseware/views/views.py const CELEBRATION_STATUSES = [ 'audit_passing', 'downloadable', 'earned_but_not_available', 'not_earned_but_available_date', 'honor_passing', 'requesting', 'unverified', ]; const NON_CERTIFICATE_STATUSES = [ // no certificate will be given, though a valid certificateData block is provided 'audit_passing', 'honor_passing', // provided when honor is configured to not give a certificate ]; function getCourseExitMode( certificateData, hasScheduledContent, isEnrolled, userHasPassingGrade, courseExitPageIsActive = null, canImmediatelyViewCertificate = false, entranceExamPassed = null, ) { const authenticatedUser = getAuthenticatedUser(); if (entranceExamPassed === false) { return COURSE_EXIT_MODES.entranceExamFail; } if (courseExitPageIsActive === false || !authenticatedUser || !isEnrolled) { return COURSE_EXIT_MODES.disabled; } // Set defaults for our status-calculated variables, used when no certificateData is provided. // This happens when `get_cert_data` in edx-platform returns None, which it does if we are // in a certificate-earning mode, but the certificate is not available (maybe they didn't pass // or course is not set up for certificates or something). Audit users will always have a // certificateData sent over. let isCelebratoryStatus = true; let isEligibleForCertificate = true; if (certificateData) { const { certStatus } = certificateData; isCelebratoryStatus = CELEBRATION_STATUSES.indexOf(certStatus) !== -1; isEligibleForCertificate = NON_CERTIFICATE_STATUSES.indexOf(certStatus) === -1; } if (hasScheduledContent && !userHasPassingGrade) { return COURSE_EXIT_MODES.inProgress; } if (isEligibleForCertificate && !userHasPassingGrade && canImmediatelyViewCertificate) { return COURSE_EXIT_MODES.nonPassing; } if (isCelebratoryStatus) { return COURSE_EXIT_MODES.celebration; } return COURSE_EXIT_MODES.disabled; } // Returns null in order to render the default navigation text function GetCourseExitNavigation(courseId, intl) { const { certificateData, hasScheduledContent, isEnrolled, userHasPassingGrade, courseExitPageIsActive, entranceExamData: { entranceExamPassed }, } = useModel('coursewareMeta', courseId); const { canViewCertificate } = useModel('courseHomeMeta', courseId); const exitMode = getCourseExitMode( certificateData, hasScheduledContent, isEnrolled, userHasPassingGrade, courseExitPageIsActive, canViewCertificate, entranceExamPassed, ); /** exitActive is used to enable/disable the exit i.e. next buttons. COURSE_EXIT_MODES denote the current status of the course. Available COURSE_EXIT_MODES: disabled, celebration, nonPassing, inProgress, entranceExamFail If the user fails the entrance exam, access to further course sections should not be allowed i.e. disable the next buttons. */ const exitActive = ((exitMode !== COURSE_EXIT_MODES.disabled) && (exitMode !== COURSE_EXIT_MODES.entranceExamFail)); let exitText; switch (exitMode) { case COURSE_EXIT_MODES.celebration: exitText = intl.formatMessage(messages.nextButtonComplete); break; case COURSE_EXIT_MODES.nonPassing: exitText = intl.formatMessage(messages.nextButtonEnd); break; default: exitText = null; } return { exitActive, exitText }; } // 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, extraProperties) => { if (!event) { return; } sendTrackEvent(`edx.ui.lms.course_exit.${event}.clicked`, { org_key: org, courserun_key: courseId, is_staff: administrator, ...extraProperties, }); }; // Use like the following to call this only once on initial page load: // useEffect(() => logVisit(org, courseId, administrator, variant), [org, courseId, administrator, variant]); // For convenience, you can pass a falsy variant and it will be ignored. const logVisit = (org, courseId, administrator, variant) => { if (!variant) { return; } sendTrackEvent('edx.ui.lms.course_exit.visited', { org_key: org, courserun_key: courseId, is_staff: administrator, variant, }); }; export { COURSE_EXIT_MODES, getCourseExitMode, GetCourseExitNavigation, logClick, logVisit, };