Skip to content

Commit

Permalink
feat: optimistic query update with the BFF inclusion
Browse files Browse the repository at this point in the history
  • Loading branch information
brobro10000 committed Dec 5, 2024
1 parent 8b8f6ba commit 70179e1
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,6 @@ export const InProgressCourseCard = ({
const { data: enterpriseCustomer } = useEnterpriseCustomer();
const updateCourseEnrollmentStatus = useUpdateCourseEnrollmentStatus({ enterpriseCustomer });
const isExecutiveEducation = EXECUTIVE_EDUCATION_COURSE_MODES.includes(mode);

const coursewareOrUpgradeLink = useLinkToCourse({
linkToCourse,
subsidyForCourse,
Expand Down Expand Up @@ -196,7 +195,6 @@ export const InProgressCourseCard = ({
updateCourseEnrollmentStatus({
courseRunId: response.courseRunId,
newStatus: response.courseRunStatus,
savedForLater: response.savedForLater,
});
navigate('.', {
replace: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ const SavedForLaterCourseCard = (props) => {
const navigate = useNavigate();
const { data: enterpriseCustomer } = useEnterpriseCustomer();
const updateCourseEnrollmentStatus = useUpdateCourseEnrollmentStatus({ enterpriseCustomer });

const [isModalOpen, setIsModalOpen] = useState(false);

const handleMoveToInProgressOnClose = () => {
Expand All @@ -63,7 +62,6 @@ const SavedForLaterCourseCard = (props) => {
updateCourseEnrollmentStatus({
courseRunId: response.courseRunId,
newStatus: response.courseRunStatus,
savedForLater: response.savedForLater,
});
navigate('.', {
replace: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@ import React, { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import { useQueryClient } from '@tanstack/react-query';
import {
AlertModal, Alert, StatefulButton, Button, ActionRow,
ActionRow, Alert, AlertModal, Button, StatefulButton,
} from '@openedx/paragon';
import { logError } from '@edx/frontend-platform/logging';

import { useParams } from 'react-router-dom';
import { ToastsContext } from '../../../../../Toasts';
import { unenrollFromCourse } from './data';
import { queryEnterpriseCourseEnrollments, useEnterpriseCustomer } from '../../../../../app/data';
import {
isBFFEnabledForEnterpriseCustomer,
queryEnterpriseCourseEnrollments,
queryEnterpriseLearnerDashboardBFF,
useEnterpriseCustomer,
} from '../../../../../app/data';

const btnLabels = {
default: 'Unenroll',
Expand All @@ -22,6 +28,7 @@ const UnenrollModal = ({
onSuccess,
}) => {
const { data: enterpriseCustomer } = useEnterpriseCustomer();
const params = useParams();
const { addToast } = useContext(ToastsContext);
const queryClient = useQueryClient();
const [btnState, setBtnState] = useState('default');
Expand All @@ -33,6 +40,33 @@ const UnenrollModal = ({
onClose();
};

const updateQueryForUnenrollment = () => {

Check failure on line 43 in src/components/dashboard/main-content/course-enrollments/course-cards/unenroll/UnenrollModal.jsx

View workflow job for this annotation

GitHub Actions / tests

'updateQueryForUnenrollment' is assigned a value but never used
const enrollmentForCourseFilter = (enrollment) => enrollment.courseRunId !== courseRunId;
const isBFFEnabled = isBFFEnabledForEnterpriseCustomer(enterpriseCustomer.uuid);
// Determine which BFF queries need to be updated after unenrolling.
if (isBFFEnabled) {
const dashboardBFFQueryKey = queryEnterpriseLearnerDashboardBFF(
{ enterpriseSlug: params.enterpriseSlug },
).queryKey;
const bffQueryKeysToUpdate = [dashboardBFFQueryKey];
// Update the enterpriseCourseEnrollments data in the cache for each BFF query.
bffQueryKeysToUpdate.forEach((queryKey) => {
const existingBFFData = queryClient.getQueryData(queryKey);
const updatedBFFData = {
...existingBFFData,
enterpriseCourseEnrollments: existingBFFData.enterpriseCourseEnrollments.filter(enrollmentForCourseFilter),
};
queryClient.setQueryData(queryKey, updatedBFFData);
});
} else {
// Update the legacy queryEnterpriseCourseEnrollments cache as well.
const enterpriseCourseEnrollmentsQueryKey = queryEnterpriseCourseEnrollments(enterpriseCustomer.uuid).queryKey;
const existingCourseEnrollmentsData = queryClient.getQueryData(enterpriseCourseEnrollmentsQueryKey);
const updatedCourseEnrollmentsData = existingCourseEnrollmentsData.filter(enrollmentForCourseFilter);
queryClient.setQueryData(enterpriseCourseEnrollmentsQueryKey, updatedCourseEnrollmentsData);
}
};

const handleUnenrollButtonClick = async () => {
setBtnState('pending');
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { sendEnterpriseTrackEventWithDelay } from '@edx/frontend-enterprise-util
import _camelCase from 'lodash.camelcase';
import _cloneDeep from 'lodash.clonedeep';

import { useLocation } from 'react-router-dom';
import { useLocation, useParams } from 'react-router-dom';
import * as service from './service';
import { COURSE_STATUSES, HAS_USER_DISMISSED_NEW_GROUP_ALERT } from './constants';
import {
Expand All @@ -24,10 +24,12 @@ import {
COUPON_CODE_SUBSIDY_TYPE,
getSubsidyToApplyForCourse,
groupCourseEnrollmentsByStatus,
isBFFEnabledForEnterpriseCustomer,
isEnrollmentUpgradeable,
LEARNER_CREDIT_SUBSIDY_TYPE,
LICENSE_SUBSIDY_TYPE,
queryEnterpriseCourseEnrollments,
queryEnterpriseLearnerDashboardBFF,
queryRedeemablePolicies,
transformCourseEnrollment,
useCanUpgradeWithLearnerCredit,
Expand Down Expand Up @@ -525,29 +527,67 @@ export function useCourseEnrollmentsBySection(courseEnrollmentsByStatus) {
};
}

function handleQueriesForUpdatedCourseEnrollmentStatus({
queryClient,
enterpriseSlug,
enterpriseCustomer,
courseRunId,
updatedEnrollmentParams,
}) {
// Transformation
const { newStatus } = updatedEnrollmentParams;
const transformUpdatedEnrollment = (enrollment) => {
if (enrollment.courseRunId !== courseRunId) {
return enrollment;
}
return {
...enrollment,
courseRunStatus: newStatus,
};
};
const isBFFEnabled = isBFFEnabledForEnterpriseCustomer(enterpriseCustomer.uuid);

if (isBFFEnabled) {
// Determine which BFF queries need to be updated after unenrolling.
const dashboardBFFQueryKey = queryEnterpriseLearnerDashboardBFF(
{ enterpriseSlug },
).queryKey;
const bffQueryKeysToUpdate = [dashboardBFFQueryKey];
// Update the enterpriseCourseEnrollments data in the cache for each BFF query.
bffQueryKeysToUpdate.forEach((queryKey) => {
const existingBFFData = queryClient.getQueryData(queryKey);
const updatedBFFData = {
...existingBFFData,
enterpriseCourseEnrollments: existingBFFData.enterpriseCourseEnrollments.map(transformUpdatedEnrollment),
};
queryClient.setQueryData(queryKey, updatedBFFData);
});
} else {
// Update the legacy queryEnterpriseCourseEnrollments cache as well.
const enterpriseCourseEnrollmentsQueryKey = queryEnterpriseCourseEnrollments(enterpriseCustomer.uuid).queryKey;
const existingCourseEnrollmentsData = queryClient.getQueryData(enterpriseCourseEnrollmentsQueryKey);
const updatedCourseEnrollmentsData = existingCourseEnrollmentsData.map(transformUpdatedEnrollment);
queryClient.setQueryData(enterpriseCourseEnrollmentsQueryKey, updatedCourseEnrollmentsData);
}
}

export const useUpdateCourseEnrollmentStatus = ({ enterpriseCustomer }) => {
const queryClient = useQueryClient();
const params = useParams();
const location = useLocation();

const updateCourseEnrollmentStatus = useCallback(({ courseRunId, newStatus, savedForLater }) => {
const enrollmentsQueryKey = queryEnterpriseCourseEnrollments(enterpriseCustomer.uuid).queryKey;
const existingEnrollments = queryClient.getQueryData(enrollmentsQueryKey);
queryClient.setQueryData(
enrollmentsQueryKey,
existingEnrollments.map((enrollment) => {
if (enrollment.courseRunId === courseRunId) {
return {
...enrollment,
courseRunStatus: newStatus,
savedForLater,
};
}
return enrollment;
}),
);
}, [
enterpriseCustomer.uuid,
queryClient,
]);
const updateCourseEnrollmentStatus = useCallback(({ courseRunId, newStatus }) => {
handleQueriesForUpdatedCourseEnrollmentStatus({
queryClient,
location,
enterpriseSlug: params.enterpriseSlug,
enterpriseCustomer,
courseRunId,
updatedEnrollmentParams: {
newStatus,
},
});
}, [queryClient, location, params.enterpriseSlug, enterpriseCustomer]);

return updateCourseEnrollmentStatus;
};
Expand Down
36 changes: 27 additions & 9 deletions src/components/executive-education-2u/UserEnrollmentForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ import { checkoutExecutiveEducation2U, isDuplicateExternalCourseOrder, toISOStri
import { useStatefulEnroll } from '../stateful-enroll/data';
import { CourseContext } from '../course/CourseContextProvider';
import {
isBFFEnabledForEnterpriseCustomer,
LEARNER_CREDIT_SUBSIDY_TYPE,
queryCanRedeemContextQueryKey,
queryEnterpriseCourseEnrollments,
queryEnterpriseLearnerDashboardBFF,
queryRedeemablePolicies,
useCourseMetadata,
useEnterpriseCourseEnrollments,
Expand All @@ -34,6 +36,7 @@ const UserEnrollmentForm = ({ className }) => {
const navigate = useNavigate();
const config = getConfig();
const queryClient = useQueryClient();
const params = useParams();
const intl = useIntl();
const {
authenticatedUser: { userId, email: userEmail },
Expand All @@ -51,22 +54,37 @@ const UserEnrollmentForm = ({ className }) => {
const [isFormSubmitted, setIsFormSubmitted] = useState(false);
const [enrollButtonState, setEnrollButtonState] = useState('default');

const handleQueryInvalidationForEnrollSuccess = () => {
const isBFFEnabled = isBFFEnabledForEnterpriseCustomer(enterpriseCustomer.uuid);
const canRedeemQueryKey = queryCanRedeemContextQueryKey(enterpriseCustomer.uuid, courseKey).queryKey;
const redeemablePoliciesQueryKey = queryRedeemablePolicies({
enterpriseUuid: enterpriseCustomer.uuid,
lmsUserId: userId,
}).queryKey;
const enterpriseCourseEnrollmentsQueryKey = queryEnterpriseCourseEnrollments(enterpriseCustomer.uuid).queryKey;
const queriesToInvalidate = [canRedeemQueryKey, redeemablePoliciesQueryKey, enterpriseCourseEnrollmentsQueryKey];

if (isBFFEnabled) {
// Determine which BFF queries need to be updated after successfully enrolling.
const dashboardBFFQueryKey = queryEnterpriseLearnerDashboardBFF(
{ enterpriseSlug: params.enterpriseSlug },
).queryKey;
queriesToInvalidate.push(dashboardBFFQueryKey);
}

queriesToInvalidate.forEach((queryKey) => {
queryClient.invalidateQueries({ queryKey });
});
};

const handleFormSubmissionSuccess = async (newTransaction) => {
// If a transaction is passed, it must be in the 'committed' state to proceed
if (!isNil(newTransaction) && newTransaction.state !== 'committed') {
return;
}

const canRedeemQueryKey = queryCanRedeemContextQueryKey(enterpriseCustomer.uuid, courseKey);
await Promise.all([
queryClient.invalidateQueries({ queryKey: canRedeemQueryKey }),
queryClient.invalidateQueries({
queryKey: queryRedeemablePolicies({
enterpriseUuid: enterpriseCustomer.uuid,
lmsUserId: userId,
}),
}),
queryClient.invalidateQueries({ queryKey: queryEnterpriseCourseEnrollments(enterpriseCustomer.uuid) }),
handleQueryInvalidationForEnrollSuccess(),
sendEnterpriseTrackEventWithDelay(
enterpriseCustomer.uuid,
'edx.ui.enterprise.learner_portal.executive_education.checkout_form.submitted',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,11 @@ const useStatefulEnroll = ({
}) => {
const { data: enterpriseCustomer } = useEnterpriseCustomer();
const [transaction, setTransaction] = useState();
const optimizelyHandler = useOptimizelyEnrollmentClickHandler(
contentKey,
// TODO: What is the href/enrollmentURL for a statefulEnroll?
const optimizelyHandler = useOptimizelyEnrollmentClickHandler({
courseRunKey: contentKey,
userEnrollments,
);
});
const searchHandler = useTrackSearchConversionClickHandler({
eventName: EVENT_NAMES.sucessfulEnrollment,
});
Expand Down

0 comments on commit 70179e1

Please sign in to comment.