Skip to content

Commit

Permalink
feat: update course about sidebar to take into account usd fixed price (
Browse files Browse the repository at this point in the history
  • Loading branch information
brobro10000 authored Oct 8, 2024
1 parent beca20e commit bcfbe24
Show file tree
Hide file tree
Showing 15 changed files with 228 additions and 140 deletions.
17 changes: 16 additions & 1 deletion src/components/app/data/hooks/useCourseRedemptionEligibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,21 @@ import { queryCanRedeem } from '../queries';
import useEnterpriseCustomer from './useEnterpriseCustomer';
import useLateEnrollmentBufferDays from './useLateEnrollmentBufferDays';

const getContentListPriceRange = ({ courseRuns }) => {
const flatContentPrice = courseRuns.flatMap(run => run.listPrice?.usd).filter(x => !!x);
// Find the max and min prices
if (!flatContentPrice.length) {
return [];
}
const maxPrice = Math.max(...flatContentPrice);
const minPrice = Math.min(...flatContentPrice);
// Heuristic for displaying the price as a range or a singular price based on runs
if (maxPrice !== minPrice) {
return [minPrice, maxPrice];
}
return [flatContentPrice[0]];
};

export function transformCourseRedemptionEligibility({
courseMetadata,
canRedeemData,
Expand All @@ -17,7 +32,7 @@ export function transformCourseRedemptionEligibility({
const otherSubsidyAccessPolicy = canRedeemData.find(
r => r.redeemableSubsidyAccessPolicy,
)?.redeemableSubsidyAccessPolicy;
const listPrice = redeemabilityForActiveCourseRun?.listPrice?.usd;
const listPrice = getContentListPriceRange({ courseRuns: canRedeemData });
const hasSuccessfulRedemption = courseRunKey
? !!canRedeemData.find(r => r.contentKey === courseRunKey)?.hasSuccessfulRedemption
: canRedeemData.some(r => r.hasSuccessfulRedemption);
Expand Down
35 changes: 17 additions & 18 deletions src/components/course/CourseSidebarPrice.jsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { Skeleton } from '@openedx/paragon';
import classNames from 'classnames';
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';

import { numberWithPrecision } from './data/utils';
import {
getContentPriceDisplay,
useCanUserRequestSubsidyForCourse,
useCoursePrice,
useIsCourseAssigned,
useUserSubsidyApplicableToCourse,
} from './data';
import {
ENTERPRISE_OFFER_SUBSIDY_TYPE,
LEARNER_CREDIT_SUBSIDY_TYPE,
LICENSE_SUBSIDY_TYPE,
ENTERPRISE_OFFER_SUBSIDY_TYPE,
useEnterpriseCustomer,
} from '../app/data';
import { sumOfArray } from '../../utils/common';

const CourseSidebarPrice = () => {
const intl = useIntl();
Expand All @@ -23,12 +23,10 @@ const CourseSidebarPrice = () => {
const { isCourseAssigned } = useIsCourseAssigned();
const canRequestSubsidy = useCanUserRequestSubsidyForCourse();
const { userSubsidyApplicableToCourse } = useUserSubsidyApplicableToCourse();

if (!coursePrice) {
return <Skeleton containerTestId="course-price-skeleton" height={24} />;
}

const originalPriceDisplay = numberWithPrecision(coursePrice.list);
const originalPriceDisplay = getContentPriceDisplay(coursePrice.listRange);
const showOrigPrice = !enterpriseCustomer.hideCourseOriginalPrice;
const crossedOutOriginalPrice = (
<del>
Expand All @@ -38,7 +36,8 @@ const CourseSidebarPrice = () => {
defaultMessage="Priced reduced from:"
description="Message to indicate that the price has been reduced."
/>
</span>${originalPriceDisplay} {currency}
</span>
{originalPriceDisplay} {currency}
</del>
);

Expand All @@ -62,13 +61,13 @@ const CourseSidebarPrice = () => {
);
}

const hasDiscountedPrice = coursePrice.discounted < coursePrice.list;

const hasDiscountedPrice = coursePrice.discountedList
&& sumOfArray(coursePrice.discountedList) < sumOfArray(coursePrice.listRange);
// Case 2: No subsidies found but learner can request a subsidy
if (!hasDiscountedPrice && canRequestSubsidy) {
return (
<span style={{ whiteSpace: 'pre-wrap' }} data-testid="browse-and-request-pricing">
<s>${originalPriceDisplay} {currency}</s><br />
<s>{originalPriceDisplay} {currency}</s><br />
<FormattedMessage
id="enterprise.course.about.course.sidebar.price.free.when.approved"
defaultMessage="Free to me{br}(when approved)"
Expand All @@ -83,7 +82,7 @@ const CourseSidebarPrice = () => {
if (!hasDiscountedPrice) {
return (
<span className="d-block">
${originalPriceDisplay} {currency}
{originalPriceDisplay} {currency}
</span>
);
}
Expand Down Expand Up @@ -113,22 +112,22 @@ const CourseSidebarPrice = () => {
});
}
}
const discountedPriceDisplay = `${numberWithPrecision(coursePrice.discounted)} ${currency}`;

const discountedPriceDisplay = `${getContentPriceDisplay(coursePrice.discountedList)} ${currency}`;
return (
<>
<div className={classNames({ 'mb-2': coursePrice.discounted > 0 || showOrigPrice })}>
{/* discounted > 0 means partial discount */}
<div className={classNames({ 'mb-2': sumOfArray(coursePrice.discountedList) > 0 || showOrigPrice })}>
{/* discountedList > 0 means partial discount */}
{showOrigPrice && <>{crossedOutOriginalPrice}{' '}</>}
{coursePrice.discounted > 0 && (
{sumOfArray(coursePrice.discountedList) > 0 && (
<>
<span className="sr-only">
<FormattedMessage
id="enterprise.course.about.price.discounted"
defaultMessage="Discounted price:"
description="Message to indicate that the price has been discounted."
description="Message to indicate that the price has been discountedList."
/>
</span>${discountedPriceDisplay}
</span>
{discountedPriceDisplay}
</>
)}
</div>
Expand Down
28 changes: 16 additions & 12 deletions src/components/course/data/hooks.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,10 @@ import { canUserRequestSubsidyForCourse, getExternalCourseEnrollmentUrl } from '
import { createExecutiveEducationFailureMessage } from '../../executive-education-2u/ExecutiveEducation2UError';
import { SUBSIDY_TYPE } from '../../../constants';
import {
COUPON_CODE_SUBSIDY_TYPE,
determineAllocatedAssignmentsForCourse,
getSubsidyToApplyForCourse,
LICENSE_SUBSIDY_TYPE,
useBrowseAndRequest,
useBrowseAndRequestConfiguration,
useCatalogsForSubsidyRequests,
Expand All @@ -48,9 +51,6 @@ import {
useEnterpriseOffers,
useRedeemablePolicies,
useSubscriptions,
COUPON_CODE_SUBSIDY_TYPE,
LICENSE_SUBSIDY_TYPE,
determineAllocatedAssignmentsForCourse,
} from '../../app/data';
import { LICENSE_STATUS } from '../../enterprise-user-subsidy/data/constants';
import { CourseContext } from '../CourseContextProvider';
Expand Down Expand Up @@ -173,8 +173,8 @@ export function useCoursePacingType(courseRun) {

/**
* @typedef {Object} CoursePrice
* @property {number} list The list price.
* @property {number} discounted The discounted price.
* @property {number[]} listRange The list price.
* @property {number[]} discountedList The discountedList price.
*/

/**
Expand Down Expand Up @@ -203,30 +203,34 @@ export const useCoursePriceForUserSubsidy = ({
}

const onlyListPrice = {
list: listPrice,
listRange: listPrice,
};

if (userSubsidyApplicableToCourse) {
const { discountType, discountValue } = userSubsidyApplicableToCourse;
let discountedPrice;
let discountedPriceList = [];

if (discountType && discountType.toLowerCase() === SUBSIDY_DISCOUNT_TYPE_MAP.PERCENTAGE.toLowerCase()) {
discountedPrice = listPrice - (listPrice * (discountValue / 100));
discountedPriceList = onlyListPrice.listRange.map(
(individualPrice) => individualPrice - (individualPrice * (discountValue / 100)),
);
}

if (discountType && discountType.toLowerCase() === SUBSIDY_DISCOUNT_TYPE_MAP.ABSOLUTE.toLowerCase()) {
discountedPrice = Math.max(listPrice - discountValue, 0);
discountedPriceList = onlyListPrice.listRange.map(
(individualPrice) => Math.max(individualPrice - discountValue, 0),
);
}

if (isDefinedAndNotNull(discountedPrice)) {
if (isDefinedAndNotNull(discountedPriceList)) {
return {
...onlyListPrice,
discounted: discountedPrice,
discountedList: discountedPriceList,
};
}
return {
...onlyListPrice,
discounted: onlyListPrice.list,
discountedList: onlyListPrice.listRange,
};
}

Expand Down
Loading

0 comments on commit bcfbe24

Please sign in to comment.