fix: Unify Xpert audit trial eligibility between backend and frontend (#1581)
* fix: Refactored Chat to be easier to read * chore: Fixed comment typo
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
"start": "fedx-scripts webpack-dev-server --progress",
|
||||
"dev": "PUBLIC_PATH=/learning/ MFE_CONFIG_API_URL='http://localhost:8000/api/mfe_config/v1' fedx-scripts webpack-dev-server --progress --host apps.local.openedx.io",
|
||||
"test": "fedx-scripts jest --coverage --passWithNoTests",
|
||||
"test:watch": "fedx-scripts jest --watch --passWithNoTests",
|
||||
"types": "tsc --noEmit"
|
||||
},
|
||||
"author": "edX",
|
||||
|
||||
@@ -55,6 +55,13 @@ export const AUDIT_MODES = [
|
||||
'unpaid-bootcamp',
|
||||
] as const satisfies readonly string[];
|
||||
|
||||
// In sync with CourseMode.UPSELL_TO_VERIFIED_MODES
|
||||
// https://github.com/openedx/edx-platform/blob/master/common/djangoapps/course_modes/models.py#L231
|
||||
export const ALLOW_UPSELL_MODES = [
|
||||
'audit',
|
||||
'honor',
|
||||
] as const satisfies readonly string[];
|
||||
|
||||
export const WIDGETS = {
|
||||
DISCUSSIONS: 'DISCUSSIONS',
|
||||
NOTIFICATIONS: 'NOTIFICATIONS',
|
||||
|
||||
@@ -6,7 +6,7 @@ import { Xpert } from '@edx/frontend-lib-learning-assistant';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import { AUDIT_MODES, VERIFIED_MODES } from '@src/constants';
|
||||
import { ALLOW_UPSELL_MODES, VERIFIED_MODES } from '@src/constants';
|
||||
import { useModel } from '../../../generic/model-store';
|
||||
|
||||
const Chat = ({
|
||||
@@ -22,67 +22,48 @@ const Chat = ({
|
||||
} = useSelector(state => state.specialExams);
|
||||
const course = useModel('coursewareMeta', courseId);
|
||||
|
||||
// If is disabled or taking an exam, we don't show the chat.
|
||||
if (!enabled || activeAttempt?.attempt_id || exam?.id) { return null; }
|
||||
|
||||
// If is not staff and doesn't have an enrollment, we don't show the chat.
|
||||
if (!isStaff && !enrollmentMode) { return null; }
|
||||
|
||||
const verifiedMode = VERIFIED_MODES.includes(enrollmentMode); // Enrollment verified
|
||||
const auditMode = (
|
||||
!isStaff
|
||||
&& !verifiedMode
|
||||
&& ALLOW_UPSELL_MODES.includes(enrollmentMode) // Can upgrade course
|
||||
&& getConfig().ENABLE_XPERT_AUDIT
|
||||
);
|
||||
// If user has no access, we don't show the chat.
|
||||
if (!isStaff && !(verifiedMode || auditMode)) { return null; }
|
||||
|
||||
// Date validation
|
||||
const {
|
||||
accessExpiration,
|
||||
start,
|
||||
end,
|
||||
} = course;
|
||||
|
||||
const hasVerifiedEnrollment = (
|
||||
enrollmentMode !== null
|
||||
&& enrollmentMode !== undefined
|
||||
&& VERIFIED_MODES.includes(enrollmentMode)
|
||||
const utcDate = (new Date()).toISOString();
|
||||
const expiration = accessExpiration?.expirationDate || utcDate;
|
||||
const validDate = (
|
||||
(start ? start <= utcDate : true)
|
||||
&& (end ? end >= utcDate : true)
|
||||
&& (auditMode ? expiration >= utcDate : true)
|
||||
);
|
||||
// If date is invalid, we don't show the chat.
|
||||
if (!validDate) { return null; }
|
||||
|
||||
// audit learners should only have access if the ENABLE_XPERT_AUDIT setting is true
|
||||
const hasAuditEnrollmentAndAccess = (
|
||||
enrollmentMode !== null
|
||||
&& enrollmentMode !== undefined
|
||||
&& AUDIT_MODES.includes(enrollmentMode)
|
||||
&& getConfig().ENABLE_XPERT_AUDIT
|
||||
);
|
||||
|
||||
const validDates = () => {
|
||||
const date = new Date();
|
||||
const utcDate = date.toISOString();
|
||||
|
||||
const startDate = start || utcDate;
|
||||
const endDate = end || utcDate;
|
||||
const accessExpirationDate = accessExpiration && accessExpiration.expirationDate
|
||||
? accessExpiration.expirationDate : utcDate;
|
||||
|
||||
return (
|
||||
startDate <= utcDate
|
||||
&& utcDate <= endDate
|
||||
&& (hasAuditEnrollmentAndAccess ? utcDate <= accessExpirationDate : true)
|
||||
);
|
||||
};
|
||||
|
||||
const shouldDisplayChat = (
|
||||
enabled
|
||||
&& (hasVerifiedEnrollment || isStaff || hasAuditEnrollmentAndAccess)
|
||||
&& validDates()
|
||||
// it is necessary to check both whether the user is in an exam, and whether or not they are viewing an exam
|
||||
// this will prevent the learner from interacting with the tool at any point of the exam flow, even at the
|
||||
// entrance interstitial.
|
||||
&& !(activeAttempt?.attempt_id || exam?.id)
|
||||
);
|
||||
|
||||
const isUpgradeEligible = !hasVerifiedEnrollment && !isStaff;
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Use a portal to ensure that component overlay does not compete with learning MFE styles. */}
|
||||
{shouldDisplayChat && (createPortal(
|
||||
<Xpert
|
||||
courseId={courseId}
|
||||
contentToolsEnabled={contentToolsEnabled}
|
||||
unitId={unitId}
|
||||
isUpgradeEligible={isUpgradeEligible}
|
||||
/>,
|
||||
document.body,
|
||||
))}
|
||||
</>
|
||||
// Use a portal to ensure that component overlay does not compete with learning MFE styles.
|
||||
return createPortal(
|
||||
<Xpert
|
||||
courseId={courseId}
|
||||
contentToolsEnabled={contentToolsEnabled}
|
||||
unitId={unitId}
|
||||
isUpgradeEligible={auditMode}
|
||||
/>,
|
||||
document.body,
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user