diff --git a/src/courseware/data/__factories__/courseMetadata.factory.js b/src/courseware/data/__factories__/courseMetadata.factory.js
index a8ca74a1..3f77e82f 100644
--- a/src/courseware/data/__factories__/courseMetadata.factory.js
+++ b/src/courseware/data/__factories__/courseMetadata.factory.js
@@ -63,4 +63,5 @@ Factory.define('courseMetadata')
related_programs: null,
user_needs_integrity_signature: false,
recommendations: null,
+ username: 'testuser',
});
diff --git a/src/courseware/data/api.js b/src/courseware/data/api.js
index 941426ef..e05b07ac 100644
--- a/src/courseware/data/api.js
+++ b/src/courseware/data/api.js
@@ -221,6 +221,7 @@ function normalizeMetadata(metadata) {
relatedPrograms: camelCaseObject(data.related_programs),
userNeedsIntegritySignature: data.user_needs_integrity_signature,
isMasquerading: data.original_user_is_staff && !data.is_staff,
+ username: data.username,
};
}
diff --git a/src/generic/upgrade-button/UpgradeNowButton.jsx b/src/generic/upgrade-button/UpgradeNowButton.jsx
index c262fd4d..127e4370 100644
--- a/src/generic/upgrade-button/UpgradeNowButton.jsx
+++ b/src/generic/upgrade-button/UpgradeNowButton.jsx
@@ -15,7 +15,7 @@ function UpgradeNowButton(props) {
...rest
} = props;
- // Prefer offer's url in case it is ever different (though it is not at time of this writing)
+ // Prefer offer's url in case it is different (might hold a coupon code that the normal does not)
const url = offer ? offer.upgradeUrl : verifiedMode.upgradeUrl;
return (
diff --git a/src/setupTest.js b/src/setupTest.js
index 2fb761b6..2afe10c9 100755
--- a/src/setupTest.js
+++ b/src/setupTest.js
@@ -123,7 +123,7 @@ export function loadUnit(message = messageEvent) {
export function logUnhandledRequests(axiosMock) {
axiosMock.onAny().reply((config) => {
// eslint-disable-next-line no-console
- console.log(config.method, config.url.href);
+ console.log(config.method, config.url);
return [200, {}];
});
}
diff --git a/src/shared/data/__factories__/courseMetadataBase.factory.js b/src/shared/data/__factories__/courseMetadataBase.factory.js
index 9c822cfb..9afe8686 100644
--- a/src/shared/data/__factories__/courseMetadataBase.factory.js
+++ b/src/shared/data/__factories__/courseMetadataBase.factory.js
@@ -12,10 +12,10 @@ export default new Factory()
original_user_is_staff: false,
number: 'DemoX',
org: 'edX',
- verifiedMode: {
- upgradeUrl: 'test',
+ verified_mode: {
+ upgrade_url: 'test',
price: 10,
- currencySymbol: '$',
+ currency_symbol: '$',
},
})
.attr(
diff --git a/src/shared/streak-celebration/StreakCelebrationModal.jsx b/src/shared/streak-celebration/StreakCelebrationModal.jsx
index f8fac945..ec203bc1 100644
--- a/src/shared/streak-celebration/StreakCelebrationModal.jsx
+++ b/src/shared/streak-celebration/StreakCelebrationModal.jsx
@@ -1,11 +1,14 @@
import React, { useEffect, useState } from 'react';
import PropTypes from 'prop-types';
+import { camelCaseObject, getConfig } from '@edx/frontend-platform';
+import { sendTrackEvent } from '@edx/frontend-platform/analytics';
+import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import {
FormattedMessage, injectIntl, intlShape,
} from '@edx/frontend-platform/i18n';
import { Lightbulb, MoneyFilled } from '@edx/paragon/icons';
import {
- Alert, Icon, ModalDialog,
+ Alert, Icon, ModalDialog, Spinner,
} from '@edx/paragon';
import { layoutGenerator } from 'react-break';
import { useDispatch } from 'react-redux';
@@ -41,18 +44,31 @@ function getRandomFactoid(intl, streakLength) {
return factoids[Math.floor(Math.random() * (factoids.length))];
}
+async function calculateVoucherDiscount(voucher, sku, username) {
+ const urlBase = `${getConfig().ECOMMERCE_BASE_URL}/api/v2/baskets/calculate`;
+ const url = `${urlBase}/?code=${voucher}&sku=${sku}&username=${username}`;
+ return getAuthenticatedHttpClient().get(url)
+ .then(res => camelCaseObject(res));
+}
+
function StreakModal({
courseId, metadataModel, streakLengthToCelebrate, intl, isStreakCelebrationOpen,
- closeStreakCelebration, StreakDiscountCouponEnabled, verifiedMode, ...rest
+ closeStreakCelebration, streakDiscountCouponEnabled, verifiedMode, ...rest
}) {
if (!isStreakCelebrationOpen) {
return null;
}
- const { org, celebrations } = useModel(metadataModel, courseId);
+ const { org, celebrations, username } = useModel(metadataModel, courseId);
const factoid = getRandomFactoid(intl, streakLengthToCelebrate);
// eslint-disable-next-line no-unused-vars
const [randomFactoid, setRandomFactoid] = useState(factoid); // Don't change factoid on re-render
+ // Open edX Folks: if you create a voucher with this code, the MFE will notice and show the discount
+ const discountCode = 'ZGY11119949';
+ // Negative means "we don't know yet" vs zero meaning no discount available
+ const [discountPercent, setDiscountPercent] = useState(-1);
+ const queryingDiscount = discountPercent < 0;
+
const layout = layoutGenerator({
mobile: 0,
desktop: 575,
@@ -68,6 +84,37 @@ function StreakModal({
}
}, [isStreakCelebrationOpen, org, courseId]);
+ // Ask ecommerce to calculate discount savings
+ useEffect(() => {
+ if (streakDiscountCouponEnabled && verifiedMode && getConfig().ECOMMERCE_BASE_URL) {
+ calculateVoucherDiscount(discountCode, verifiedMode.sku, username)
+ .then(
+ (result) => {
+ const { totalInclTax, totalInclTaxExclDiscounts } = result.data;
+ if (totalInclTaxExclDiscounts && totalInclTax !== totalInclTaxExclDiscounts) {
+ // Just store the percent (rather than using these values directly), because ecommerce doesn't give us
+ // the currency symbol to use, so we want to use the symbol that LMS gives us. And I don't want to assume
+ // ecommerce's currency is the same as the LMS. So we'll keep using the values in verifiedMode, just
+ // multiplied by the calculated percentage.
+ setDiscountPercent(1 - totalInclTax / totalInclTaxExclDiscounts);
+ sendTrackEvent('edx.bi.course.streak_discount_enabled', {
+ course_id: courseId,
+ sku: verifiedMode.sku,
+ });
+ } else {
+ setDiscountPercent(0);
+ }
+ },
+ () => {
+ // ignore any errors - we just won't show the discount to the user then
+ setDiscountPercent(0);
+ },
+ );
+ } else {
+ setDiscountPercent(0);
+ }
+ }, [streakDiscountCouponEnabled, username, verifiedMode]);
+
function CloseText() {
return (
@@ -82,21 +129,25 @@ function StreakModal({
let offer;
if (verifiedMode) {
- upgradeUrl = `${verifiedMode.upgradeUrl}&code=ZGY11119949`;
+ upgradeUrl = `${verifiedMode.upgradeUrl}`;
mode = {
currencySymbol: verifiedMode.currencySymbol,
price: verifiedMode.price,
upgradeUrl,
};
- offer = {
- discountedPrice: `${verifiedMode.currencySymbol}${(mode.price * 0.85).toFixed(2).toString()}`,
- originalPrice: `${verifiedMode.currencySymbol}${mode.price.toString()}`,
- upgradeUrl: mode.upgradeUrl,
- };
+ if (discountPercent > 0) {
+ const discountMultipler = 1 - discountPercent;
+ offer = {
+ discountedPrice: `${verifiedMode.currencySymbol}${(mode.price * discountMultipler).toFixed(2).toString()}`,
+ originalPrice: `${verifiedMode.currencySymbol}${mode.price.toString()}`,
+ upgradeUrl: `${mode.upgradeUrl}&code=${discountCode}`,
+ };
+ }
}
const title = `${streakLengthToCelebrate} ${intl.formatMessage(messages.streakHeader)}`;
+ const showOffer = offer && streakDiscountCouponEnabled;
return (