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:
Marcos Rigoli
2025-01-30 16:35:39 -03:00
committed by GitHub
parent c70fb138f0
commit bd9c97c269
3 changed files with 43 additions and 54 deletions

View File

@@ -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",

View File

@@ -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',

View File

@@ -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,
);
};