@edx/frontend-app-alacarte-checkout 1.18.0 → 1.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/checkoutPage/CheckoutPage.js +0 -12
- package/dist/checkoutPage/CheckoutPage.js.map +1 -1
- package/dist/components/CartSummary/CartSummary.js +9 -3
- package/dist/components/CartSummary/CartSummary.js.map +1 -1
- package/dist/components/CartSummary/Messages.js +2 -2
- package/dist/components/CartSummary/Messages.js.map +1 -1
- package/dist/components/Checkout/Checkout.js +1 -4
- package/dist/components/Checkout/Checkout.js.map +1 -1
- package/dist/components/Checkout/Messages.js +2 -2
- package/dist/components/Checkout/Messages.js.map +1 -1
- package/dist/components/ExpressCheckout/ExpressCheckout.js +15 -3
- package/dist/components/ExpressCheckout/ExpressCheckout.js.map +1 -1
- package/dist/components/PromoCode/Messages.js +3 -3
- package/dist/components/PromoCode/Messages.js.map +1 -1
- package/dist/components/PromoCode/PromoCode.js +1 -1
- package/dist/components/PromoCode/PromoCode.js.map +1 -1
- package/dist/data/hooks/keys.d.ts +1 -1
- package/dist/data/hooks/keys.js +1 -1
- package/dist/data/hooks/keys.js.map +1 -1
- package/dist/data/hooks/useAlacarteCheckout.js +21 -15
- package/dist/data/hooks/useAlacarteCheckout.js.map +1 -1
- package/dist/data/hooks/useAlacarteQueries.d.ts +5 -3
- package/dist/data/hooks/useAlacarteQueries.js +8 -6
- package/dist/data/hooks/useAlacarteQueries.js.map +1 -1
- package/dist/data/hooks/useCheckoutQueries.d.ts +6 -0
- package/dist/data/hooks/useCheckoutQueries.js +15 -1
- package/dist/data/hooks/useCheckoutQueries.js.map +1 -1
- package/dist/style.scss +0 -1
- package/dist/utils/alacarteApi.d.ts +2 -2
- package/dist/utils/alacarteApi.js +5 -5
- package/dist/utils/alacarteApi.js.map +1 -1
- package/dist/utils/alacarteQueries.d.ts +1 -1
- package/dist/utils/alacarteQueries.js +1 -1
- package/dist/utils/alacarteQueries.js.map +1 -1
- package/dist/utils/provision.d.ts +12 -0
- package/dist/utils/provision.js +66 -0
- package/dist/utils/provision.js.map +1 -1
- package/package.json +1 -1
- package/dist/components/DemoPdpBanner/DemoPdpBanner.d.ts +0 -8
- package/dist/components/DemoPdpBanner/DemoPdpBanner.js +0 -101
- package/dist/components/DemoPdpBanner/DemoPdpBanner.js.map +0 -1
- package/dist/data/hooks/useBannerData.d.ts +0 -8
- package/dist/data/hooks/useBannerData.js +0 -61
- package/dist/data/hooks/useBannerData.js.map +0 -1
- package/dist/styles/components/demoPdpBanner.scss +0 -269
|
@@ -1,15 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useState } from 'react';
|
|
3
2
|
import { Alert, Container } from '@openedx/paragon';
|
|
4
3
|
import { getAppConfig, getSiteConfig } from '@openedx/frontend-base';
|
|
5
4
|
import { appId } from '../constants';
|
|
6
5
|
import CheckoutGate from '../components/CheckoutGate/CheckoutGate';
|
|
7
|
-
import DemoPdpBanner from '../components/DemoPdpBanner/DemoPdpBanner';
|
|
8
6
|
const PageWrapper = ({ children }) => (_jsx(Container, { children: children }));
|
|
9
|
-
// TEMPORARY — Set to true to show the demo PDP banner before checkout.
|
|
10
|
-
// When true, the banner is displayed first; clicking "Earn a certificate"
|
|
11
|
-
// transitions to the real checkout flow. Set to false to skip the banner.
|
|
12
|
-
const SHOW_DEMO_PDP = true;
|
|
13
7
|
const CheckoutPage = () => {
|
|
14
8
|
const config = getAppConfig(appId);
|
|
15
9
|
const siteConfig = getSiteConfig();
|
|
@@ -20,15 +14,9 @@ const CheckoutPage = () => {
|
|
|
20
14
|
const edxSessionUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/edx-session`;
|
|
21
15
|
const geolocationUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/geolocation/lookup`;
|
|
22
16
|
const complianceCheckUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/compliance-check`;
|
|
23
|
-
// When the demo banner is enabled, start on the "PDP" view; clicking the
|
|
24
|
-
// CTA flips showCheckout to true and the real checkout renders.
|
|
25
|
-
const [showCheckout, setShowCheckout] = useState(!SHOW_DEMO_PDP);
|
|
26
17
|
if (!stripePublishableKey || !apiUrl || !edxSessionUrl) {
|
|
27
18
|
return (_jsx(PageWrapper, { children: _jsxs(Alert, { variant: "danger", children: ["We're having trouble loading the checkout. Please refresh the page or try again in a few moments. If the problem persists,", ' ', _jsx("a", { href: "https://courses.edx.org/support/contact_us", children: "contact support" }), "."] }) }));
|
|
28
19
|
}
|
|
29
|
-
if (!showCheckout) {
|
|
30
|
-
return (_jsx(DemoPdpBanner, { apiUrl: apiUrl, edxSessionUrl: edxSessionUrl, onEnroll: () => setShowCheckout(true) }));
|
|
31
|
-
}
|
|
32
20
|
return (_jsx(PageWrapper, { children: _jsx(CheckoutGate, { stripePublishableKey: stripePublishableKey, apiUrl: apiUrl, edxSessionUrl: edxSessionUrl, geolocationUrl: geolocationUrl, complianceCheckUrl: complianceCheckUrl, learnerDashboardUrl: `${LMS_BASE_URL}/dashboard`, homeUrl: LMS_BASE_URL || '/', onComplete: (_subscriptionBiId) => {
|
|
33
21
|
console.log(_subscriptionBiId);
|
|
34
22
|
// window.location.href = `/checkout/confirmation?sub=${_subscriptionBiId ?? ''}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CheckoutPage.js","sourceRoot":"","sources":["../../src/checkoutPage/CheckoutPage.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"CheckoutPage.js","sourceRoot":"","sources":["../../src/checkoutPage/CheckoutPage.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACrE,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,YAAY,MAAM,yCAAyC,CAAC;AAEnE,MAAM,WAAW,GAA4C,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAC7E,KAAC,SAAS,cAAE,QAAQ,GAAa,CAClC,CAAC;AAEF,MAAM,YAAY,GAAa,GAAG,EAAE;IAClC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IACnC,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC9D,MAAM,oBAAoB,GAAG,MAAM,CAAC,iBAA2B,CAAC;IAChE,MAAM,MAAM,GAAG,MAAM,CAAC,YAAsB,CAAC;IAC7C,MAAM,aAAa,GAAG,UAAU,CAAC,WAAW,KAAK,aAAa,CAAC;IAC/D,MAAM,aAAa,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,YAAY,kBAAkB,CAAC;IAC1G,MAAM,cAAc,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,YAAY,yBAAyB,CAAC;IAClH,MAAM,kBAAkB,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,YAAY,uBAAuB,CAAC;IAEpH,IAAI,CAAC,oBAAoB,IAAI,CAAC,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QACvD,OAAO,CACL,KAAC,WAAW,cACV,MAAC,KAAK,IAAC,OAAO,EAAC,QAAQ,2IAEwC,GAAG,EAChE,YAAG,IAAI,EAAC,4CAA4C,gCAEhD,SAEE,GACI,CACf,CAAC;IACJ,CAAC;IAED,OAAO,CACL,KAAC,WAAW,cACV,KAAC,YAAY,IACX,oBAAoB,EAAE,oBAAoB,EAC1C,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,EAC5B,cAAc,EAAE,cAAc,EAC9B,kBAAkB,EAAE,kBAAkB,EACtC,mBAAmB,EAAE,GAAG,YAAY,YAAY,EAChD,OAAO,EAAE,YAAY,IAAI,GAAG,EAC5B,UAAU,EAAE,CAAC,iBAAiB,EAAE,EAAE;gBAChC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC;gBAC/B,kFAAkF;gBAClF,kBAAkB;YACpB,CAAC,GACD,GACU,CACf,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,YAAY,CAAC","sourcesContent":["import React from 'react';\nimport { Alert, Container } from '@openedx/paragon';\nimport { getAppConfig, getSiteConfig } from '@openedx/frontend-base';\nimport { appId } from '@src/constants';\nimport CheckoutGate from '../components/CheckoutGate/CheckoutGate';\n\nconst PageWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (\n <Container>{children}</Container>\n);\n\nconst CheckoutPage: React.FC = () => {\n const config = getAppConfig(appId);\n const siteConfig = getSiteConfig();\n const LMS_BASE_URL = siteConfig.lmsBaseUrl.replace(/\\/$/, '');\n const stripePublishableKey = config.STRIPE_PUBLIC_KEY as string;\n const apiUrl = config.VIAX_API_URL as string;\n const isDevelopment = siteConfig.environment === 'development';\n const edxSessionUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/edx-session`;\n const geolocationUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/geolocation/lookup`;\n const complianceCheckUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/compliance-check`;\n\n if (!stripePublishableKey || !apiUrl || !edxSessionUrl) {\n return (\n <PageWrapper>\n <Alert variant=\"danger\">\n We're having trouble loading the checkout. Please refresh the\n page or try again in a few moments. If the problem persists,{' '}\n <a href=\"https://courses.edx.org/support/contact_us\">\n contact support\n </a>\n .\n </Alert>\n </PageWrapper>\n );\n }\n\n return (\n <PageWrapper>\n <CheckoutGate\n stripePublishableKey={stripePublishableKey}\n apiUrl={apiUrl}\n edxSessionUrl={edxSessionUrl}\n geolocationUrl={geolocationUrl}\n complianceCheckUrl={complianceCheckUrl}\n learnerDashboardUrl={`${LMS_BASE_URL}/dashboard`}\n homeUrl={LMS_BASE_URL || '/'}\n onComplete={(_subscriptionBiId) => {\n console.log(_subscriptionBiId);\n // window.location.href = `/checkout/confirmation?sub=${_subscriptionBiId ?? ''}`;\n // Success screen.\n }}\n />\n </PageWrapper>\n );\n};\n\nexport default CheckoutPage;\n"]}
|
|
@@ -9,8 +9,15 @@ import PromoCode from '../PromoCode/PromoCode';
|
|
|
9
9
|
import messages from './Messages';
|
|
10
10
|
import learnersIcon from './world-icon.svg';
|
|
11
11
|
import institutionsIcon from './student-icon.svg';
|
|
12
|
+
/** The coupon discount comes back named "COUPON PERCENT". Show the learner label instead. */
|
|
13
|
+
function discountRowLabel(discountName, fallback) {
|
|
14
|
+
const name = discountName === null || discountName === void 0 ? void 0 : discountName.trim();
|
|
15
|
+
if (!name || name.toUpperCase() === 'COUPON PERCENT')
|
|
16
|
+
return fallback;
|
|
17
|
+
return name;
|
|
18
|
+
}
|
|
12
19
|
const CartSummary = ({ productTitle, productBlurb, learnerCount: learnerCountProp, course = null, alacarteLine = null, programDetail = null, programName: programNameProp = null, promoCode = null, }) => {
|
|
13
|
-
var _a, _b, _c, _d, _e, _f, _g
|
|
20
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
14
21
|
const { formatMessage } = useIntl();
|
|
15
22
|
const title = productTitle !== null && productTitle !== void 0 ? productTitle : formatMessage(messages.productTitle);
|
|
16
23
|
const blurb = productBlurb !== null && productBlurb !== void 0 ? productBlurb : formatMessage(messages.productBlurb);
|
|
@@ -50,8 +57,7 @@ const CartSummary = ({ productTitle, productBlurb, learnerCount: learnerCountPro
|
|
|
50
57
|
const discount = (_g = alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.discount) !== null && _g !== void 0 ? _g : null;
|
|
51
58
|
return (_jsxs("div", { className: "cart-summary ", children: [_jsxs(Card, { className: "cart-summary__card p-3", children: [_jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center p-0 pb-3", children: [_jsx("span", { className: "font-weight-bold text-gray-900", children: formatMessage(messages.summaryHeading) }), _jsxs(Badge, { variant: "light", className: "d-flex align-items-center px-2 py-1", children: [_jsx(Icon, { src: Group, className: "mr-1", style: { width: '1rem', height: '1rem' } }), subscriberCount == null ? (_jsx("span", { className: "cart-summary__count-skeleton", "aria-busy": "true", "aria-label": formatMessage(messages.loadingSubscribers), "data-testid": "subscriber-count-skeleton", children: _jsx(Skeleton, { width: 96, height: 12 }) })) : (formatMessage(messages.learnersSubscribed, {
|
|
52
59
|
count: subscriberCount.toLocaleString(),
|
|
53
|
-
}))] })] }), _jsx(Card.Divider, {}), _jsxs(Card.Section, { className: "p-0 py-3", children: [course ? (_jsx(CourseEnrollmentCard, { course: course, variant: "summary" })) : programDetail ? (_jsx(ProgramEnrollmentCard, { program: programDetail, programName: lineName !== null && lineName !== void 0 ? lineName : '', ownedCourses: ownedCourses })) : lineName ? (_jsx("p", { className: "font-weight-bold mb-0 text-primary-500", children: lineName })) : (_jsxs("div", { className: "d-flex align-items-start", children: [_jsx(EdxUnlimitedLogo, { ariaLabel: "edX Plus" }), _jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-0 text-primary-500", children: title }), _jsx("p", { className: "small mb-0 text-gray-900", children: blurb })] })] })), !programDetail && ownedCourseNames && (_jsx("p", { className: "cart-summary__already-yours small text-gray-700 mb-0 mt-2", "data-testid": "already-yours", children: formatMessage(messages.alreadyYours, { names: ownedCourseNames }) }))] }), _jsx(Card.Divider, {}), _jsxs(Card.Section, { className: "cart-summary__subtotal-row d-flex justify-content-between align-items-center p-0 pt-3", children: [_jsx("span", { className: "cart-summary__subtotal text-gray-900", children: formatMessage(messages.subtotal) }), _jsx("span", { className: "cart-summary__subtotal text-gray-900", children: subtotalText })] }), discount && (_jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center p-0", "data-testid": "discount-row", children: [_jsx("span", { className: "text-success font-weight-bold", children: (
|
|
54
|
-
|| formatMessage(messages.discountApplied) }), _jsx("span", { className: "text-success font-weight-bold", children: formatMessage(messages.discountAmount, {
|
|
60
|
+
}))] })] }), _jsx(Card.Divider, {}), _jsxs(Card.Section, { className: "p-0 py-3", children: [course ? (_jsx(CourseEnrollmentCard, { course: course, variant: "summary" })) : programDetail ? (_jsx(ProgramEnrollmentCard, { program: programDetail, programName: lineName !== null && lineName !== void 0 ? lineName : '', ownedCourses: ownedCourses })) : lineName ? (_jsx("p", { className: "font-weight-bold mb-0 text-primary-500", children: lineName })) : (_jsxs("div", { className: "d-flex align-items-start", children: [_jsx(EdxUnlimitedLogo, { ariaLabel: "edX Plus" }), _jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-0 text-primary-500", children: title }), _jsx("p", { className: "small mb-0 text-gray-900", children: blurb })] })] })), !programDetail && ownedCourseNames && (_jsx("p", { className: "cart-summary__already-yours small text-gray-700 mb-0 mt-2", "data-testid": "already-yours", children: formatMessage(messages.alreadyYours, { names: ownedCourseNames }) }))] }), _jsx(Card.Divider, {}), _jsxs(Card.Section, { className: "cart-summary__subtotal-row d-flex justify-content-between align-items-center p-0 pt-3", children: [_jsx("span", { className: "cart-summary__subtotal text-gray-900", children: formatMessage(messages.subtotal) }), _jsx("span", { className: "cart-summary__subtotal text-gray-900", children: subtotalText })] }), discount && (_jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center p-0", "data-testid": "discount-row", children: [_jsx("span", { className: "text-success font-weight-bold", children: discountRowLabel(alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.discountName, formatMessage(messages.discountApplied)) }), _jsx("span", { className: "text-success font-weight-bold", children: formatMessage(messages.discountAmount, {
|
|
55
61
|
amount: discount.amount.toFixed(2),
|
|
56
62
|
currency: discount.currency,
|
|
57
63
|
}) })] })), _jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center p-0", children: [_jsx("span", { className: "cart-summary__total-label text-gray-900", children: formatMessage(messages.todaysTotal) }), _jsx("span", { className: "cart-summary__total-value mb-0 text-gray-900", children: totalText })] })] }), promoCode ? _jsx(PromoCode, Object.assign({}, promoCode)) : null, _jsxs("div", { className: "cart-summary__stats d-none d-lg-flex", children: [_jsxs("div", { className: "cart-summary__stat d-flex align-items-center mr-4", children: [_jsx("img", { src: learnersIcon, alt: "", width: 50, height: 50, className: "mr-2" }), _jsxs("div", { children: [_jsx("p", { className: "cart-summary__stat-count font-weight-bold text-gray-500 mb-0", children: formatMessage(messages.learnersCount) }), _jsx("p", { className: "cart-summary__stat-label text-gray-500 mb-0", children: formatMessage(messages.learnersLabel) })] })] }), _jsxs("div", { className: "cart-summary__stat d-flex align-items-center", children: [_jsx("img", { src: institutionsIcon, alt: "", width: 50, height: 50, className: "mr-2" }), _jsxs("div", { children: [_jsx("p", { className: "cart-summary__stat-count font-weight-bold text-gray-500 mb-0", children: formatMessage(messages.institutionsCount) }), _jsx("p", { className: "cart-summary__stat-label text-gray-500 mb-0", children: formatMessage(messages.institutionsLabel) })] })] })] })] }));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CartSummary.js","sourceRoot":"","sources":["../../../src/components/CartSummary/CartSummary.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport { Badge, Card, Icon, Skeleton } from '@openedx/paragon';\nimport { Group } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport { EdxUnlimitedLogo } from '../BrandMarks/BrandMarks';\nimport CourseEnrollmentCard from '../CourseEnrollmentCard/CourseEnrollmentCard';\nimport ProgramEnrollmentCard from '../ProgramEnrollmentCard/ProgramEnrollmentCard';\nimport PromoCode from '../PromoCode/PromoCode';\nimport type { PromoCodeProps } from '../PromoCode/PromoCode';\nimport type { CourseDetail } from '../../types';\nimport type { AlacarteLine, ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\nimport learnersIcon from './world-icon.svg';\nimport institutionsIcon from './student-icon.svg';\n\nexport type BillingCycle = 'monthly' | 'annual';\n\nexport interface CartSummaryProps {\n productTitle?: string,\n productBlurb?: string,\n /** Promotional monthly price amount e.g. \"39\" */\n monthlyAmount?: string,\n /** Regular monthly price after the promo period e.g. \"59\" */\n monthlyRegularAmount?: string,\n /** Length of the promotional period in months */\n promoMonths?: number,\n /** Annual price amount e.g. \"299\" */\n annualAmount?: string,\n currency?: string,\n defaultCycle?: BillingCycle,\n monthlyRenewalLabel?: string,\n annualRenewalLabel?: string,\n /** Called when the learner switches monthly ↔ annual. */\n onCycleChange?: (cycle: BillingCycle) => void,\n learnerCount?: number,\n /** When set, shows the course enrollment preview card below the summary card. */\n course?: CourseDetail | null,\n /** À la carte checkout line — when present, drives the subtotal/total and\n * the product name shown, in place of the generic edX Plus placeholder. */\n alacarteLine?: AlacarteLine | null,\n /** Program catalog detail (filterTwouProgram) — when present, drives the\n * \"Courses included in this program\" list. */\n programDetail?: ProgramDetail | null,\n /** Fallback program name — used when there is no alacarteLine to provide it\n * (e.g. already-owned overlay). */\n programName?: string | null,\n /** Coupon input — when set, wires PromoCode to tCheckoutUpdate. */\n promoCode?: PromoCodeProps | null,\n}\n\nconst CartSummary = ({\n productTitle,\n productBlurb,\n learnerCount: learnerCountProp,\n course = null,\n alacarteLine = null,\n programDetail = null,\n programName: programNameProp = null,\n promoCode = null,\n}: CartSummaryProps) => {\n const { formatMessage } = useIntl();\n const title = productTitle ?? formatMessage(messages.productTitle);\n const blurb = productBlurb ?? formatMessage(messages.productBlurb);\n\n // Prefer the product's own enrollment count (course or program catalog field).\n const isProgramLine = alacarteLine?.kind === 'program';\n const subscriberCount = course?.tEnrollmentCount\n ?? programDetail?.tEnrollmentCount\n ?? learnerCountProp\n ?? null;\n\n // No fallback amount — an unresolved à la carte line means we don't know\n // the price yet, so the row is left blank rather than showing a made-up\n // number that could be mistaken for the real one.\n // Program subtotal is price less owned-course deduction (before coupon/discount).\n const deductionAmount = isProgramLine && alacarteLine.kind === 'program'\n ? (alacarteLine.deduction?.amount ?? 0)\n : 0;\n const subtotalAmount = alacarteLine?.price\n ? Math.max(0, alacarteLine.price.amount - deductionAmount)\n : null;\n const subtotalText = subtotalAmount != null && alacarteLine?.price\n ? formatMessage(messages.subtotalAmount, {\n amount: subtotalAmount.toFixed(2), currency: alacarteLine.price.currency,\n })\n : '';\n const totalText = alacarteLine?.amountDue\n ? formatMessage(messages.todaysTotalValue, {\n amount: alacarteLine.amountDue.amount.toFixed(2), currency: alacarteLine.amountDue.currency,\n })\n : '';\n\n const lineName = alacarteLine\n ? (alacarteLine.kind === 'course' ? alacarteLine.courseName : alacarteLine.programName)\n : programNameProp;\n\n const ownedCourses = isProgramLine && alacarteLine.kind === 'program'\n ? (alacarteLine.ownedCourses ?? [])\n : [];\n const ownedCourseNames = ownedCourses\n .map((c) => c.name)\n .filter(Boolean)\n .join(', ');\n const discount = alacarteLine?.discount ?? null;\n\n return (\n <div className=\"cart-summary \">\n <Card className=\"cart-summary__card p-3\">\n <Card.Section className=\"d-flex justify-content-between align-items-center p-0 pb-3\">\n <span className=\"font-weight-bold text-gray-900\">\n {formatMessage(messages.summaryHeading)}\n </span>\n <Badge\n variant=\"light\"\n className=\"d-flex align-items-center px-2 py-1\"\n >\n <Icon\n src={Group}\n className=\"mr-1\"\n style={{ width: '1rem', height: '1rem' }}\n />\n {subscriberCount == null ? (\n <span\n className=\"cart-summary__count-skeleton\"\n aria-busy=\"true\"\n aria-label={formatMessage(messages.loadingSubscribers)}\n data-testid=\"subscriber-count-skeleton\"\n >\n <Skeleton width={96} height={12} />\n </span>\n ) : (\n formatMessage(messages.learnersSubscribed, {\n count: subscriberCount.toLocaleString(),\n })\n )}\n </Badge>\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"p-0 py-3\">\n {course ? (\n <CourseEnrollmentCard course={course} variant=\"summary\" />\n ) : programDetail ? (\n <ProgramEnrollmentCard\n program={programDetail}\n programName={lineName ?? ''}\n ownedCourses={ownedCourses}\n />\n ) : lineName ? (\n <p className=\"font-weight-bold mb-0 text-primary-500\">{lineName}</p>\n ) : (\n <div className=\"d-flex align-items-start\">\n <EdxUnlimitedLogo ariaLabel=\"edX Plus\" />\n <div>\n <p className=\"font-weight-bold mb-0 text-primary-500\">{title}</p>\n <p className=\"small mb-0 text-gray-900\">{blurb}</p>\n </div>\n </div>\n )}\n {/* Fallback when catalog detail hasn't loaded yet — once it has,\n owned courses are listed under \"Previously purchased\" in the card. */}\n {!programDetail && ownedCourseNames && (\n <p\n className=\"cart-summary__already-yours small text-gray-700 mb-0 mt-2\"\n data-testid=\"already-yours\"\n >\n {formatMessage(messages.alreadyYours, { names: ownedCourseNames })}\n </p>\n )}\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"cart-summary__subtotal-row d-flex justify-content-between align-items-center p-0 pt-3\">\n <span className=\"cart-summary__subtotal text-gray-900\">\n {formatMessage(messages.subtotal)}\n </span>\n <span className=\"cart-summary__subtotal text-gray-900\">\n {subtotalText}\n </span>\n </Card.Section>\n\n {discount && (\n <Card.Section\n className=\"d-flex justify-content-between align-items-center p-0\"\n data-testid=\"discount-row\"\n >\n <span className=\"text-success font-weight-bold\">\n {alacarteLine?.discountName?.trim()\n || formatMessage(messages.discountApplied)}\n </span>\n <span className=\"text-success font-weight-bold\">\n {formatMessage(messages.discountAmount, {\n amount: discount.amount.toFixed(2),\n currency: discount.currency,\n })}\n </span>\n </Card.Section>\n )}\n\n <Card.Section className=\"d-flex justify-content-between align-items-center p-0\">\n <span className=\"cart-summary__total-label text-gray-900\">\n {formatMessage(messages.todaysTotal)}\n </span>\n <span className=\"cart-summary__total-value mb-0 text-gray-900\">\n {totalText}\n </span>\n </Card.Section>\n </Card>\n\n {promoCode ? <PromoCode {...promoCode} /> : null}\n\n {/* Stats row */}\n <div className=\"cart-summary__stats d-none d-lg-flex\">\n <div className=\"cart-summary__stat d-flex align-items-center mr-4\">\n <img src={learnersIcon} alt=\"\" width={50} height={50} className=\"mr-2\" />\n <div>\n <p className=\"cart-summary__stat-count font-weight-bold text-gray-500 mb-0\">\n {formatMessage(messages.learnersCount)}\n </p>\n <p className=\"cart-summary__stat-label text-gray-500 mb-0\">\n {formatMessage(messages.learnersLabel)}\n </p>\n </div>\n </div>\n <div className=\"cart-summary__stat d-flex align-items-center\">\n <img src={institutionsIcon} alt=\"\" width={50} height={50} className=\"mr-2\" />\n <div>\n <p className=\"cart-summary__stat-count font-weight-bold text-gray-500 mb-0\">\n {formatMessage(messages.institutionsCount)}\n </p>\n <p className=\"cart-summary__stat-label text-gray-500 mb-0\">\n {formatMessage(messages.institutionsLabel)}\n </p>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default CartSummary;\n"]}
|
|
1
|
+
{"version":3,"file":"CartSummary.js","sourceRoot":"","sources":["../../../src/components/CartSummary/CartSummary.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC/D,OAAO,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,oBAAoB,MAAM,8CAA8C,CAAC;AAChF,OAAO,qBAAqB,MAAM,gDAAgD,CAAC;AACnF,OAAO,SAAS,MAAM,wBAAwB,CAAC;AAI/C,OAAO,QAAQ,MAAM,YAAY,CAAC;AAClC,OAAO,YAAY,MAAM,kBAAkB,CAAC;AAC5C,OAAO,gBAAgB,MAAM,oBAAoB,CAAC;AAqClD,6FAA6F;AAC7F,SAAS,gBAAgB,CAAC,YAAuC,EAAE,QAAgB;IACjF,MAAM,IAAI,GAAG,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,IAAI,EAAE,CAAC;IAClC,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,KAAK,gBAAgB;QAAE,OAAO,QAAQ,CAAC;IACtE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,WAAW,GAAG,CAAC,EACnB,YAAY,EACZ,YAAY,EACZ,YAAY,EAAE,gBAAgB,EAC9B,MAAM,GAAG,IAAI,EACb,YAAY,GAAG,IAAI,EACnB,aAAa,GAAG,IAAI,EACpB,WAAW,EAAE,eAAe,GAAG,IAAI,EACnC,SAAS,GAAG,IAAI,GACC,EAAE,EAAE;;IACrB,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,YAAY,aAAZ,YAAY,cAAZ,YAAY,GAAI,aAAa,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,YAAY,aAAZ,YAAY,cAAZ,YAAY,GAAI,aAAa,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;IAEnE,+EAA+E;IAC/E,MAAM,aAAa,GAAG,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,IAAI,MAAK,SAAS,CAAC;IACvD,MAAM,eAAe,GAAG,MAAA,MAAA,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,gBAAgB,mCAC3C,aAAa,aAAb,aAAa,uBAAb,aAAa,CAAE,gBAAgB,mCAC/B,gBAAgB,mCAChB,IAAI,CAAC;IAEV,yEAAyE;IACzE,wEAAwE;IACxE,kDAAkD;IAClD,kFAAkF;IAClF,MAAM,eAAe,GAAG,aAAa,IAAI,YAAY,CAAC,IAAI,KAAK,SAAS;QACtE,CAAC,CAAC,CAAC,MAAA,MAAA,YAAY,CAAC,SAAS,0CAAE,MAAM,mCAAI,CAAC,CAAC;QACvC,CAAC,CAAC,CAAC,CAAC;IACN,MAAM,cAAc,GAAG,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,KAAK;QACxC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,YAAY,CAAC,KAAK,CAAC,MAAM,GAAG,eAAe,CAAC;QAC1D,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,YAAY,GAAG,cAAc,IAAI,IAAI,KAAI,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,KAAK,CAAA;QAChE,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,cAAc,EAAE;YACrC,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,QAAQ;SACzE,CAAC;QACJ,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,SAAS,GAAG,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,SAAS;QACvC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,gBAAgB,EAAE;YACvC,MAAM,EAAE,YAAY,CAAC,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,SAAS,CAAC,QAAQ;SAC5F,CAAC;QACJ,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,QAAQ,GAAG,YAAY;QAC3B,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC,WAAW,CAAC;QACvF,CAAC,CAAC,eAAe,CAAC;IAEpB,MAAM,YAAY,GAAG,aAAa,IAAI,YAAY,CAAC,IAAI,KAAK,SAAS;QACnE,CAAC,CAAC,CAAC,MAAA,YAAY,CAAC,YAAY,mCAAI,EAAE,CAAC;QACnC,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,gBAAgB,GAAG,YAAY;SAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;SAClB,MAAM,CAAC,OAAO,CAAC;SACf,IAAI,CAAC,IAAI,CAAC,CAAC;IACd,MAAM,QAAQ,GAAG,MAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,QAAQ,mCAAI,IAAI,CAAC;IAEhD,OAAO,CACL,eAAK,SAAS,EAAC,eAAe,aAC5B,MAAC,IAAI,IAAC,SAAS,EAAC,wBAAwB,aACtC,MAAC,IAAI,CAAC,OAAO,IAAC,SAAS,EAAC,4DAA4D,aAClF,eAAM,SAAS,EAAC,gCAAgC,YAC7C,aAAa,CAAC,QAAQ,CAAC,cAAc,CAAC,GAClC,EACP,MAAC,KAAK,IACJ,OAAO,EAAC,OAAO,EACf,SAAS,EAAC,qCAAqC,aAE/C,KAAC,IAAI,IACH,GAAG,EAAE,KAAK,EACV,SAAS,EAAC,MAAM,EAChB,KAAK,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,GACxC,EACD,eAAe,IAAI,IAAI,CAAC,CAAC,CAAC,CACzB,eACE,SAAS,EAAC,8BAA8B,eAC9B,MAAM,gBACJ,aAAa,CAAC,QAAQ,CAAC,kBAAkB,CAAC,iBAC1C,2BAA2B,YAEvC,KAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,GAAI,GAC9B,CACR,CAAC,CAAC,CAAC,CACF,aAAa,CAAC,QAAQ,CAAC,kBAAkB,EAAE;wCACzC,KAAK,EAAE,eAAe,CAAC,cAAc,EAAE;qCACxC,CAAC,CACH,IACK,IACK,EAEf,KAAC,IAAI,CAAC,OAAO,KAAG,EAEhB,MAAC,IAAI,CAAC,OAAO,IAAC,SAAS,EAAC,UAAU,aAC/B,MAAM,CAAC,CAAC,CAAC,CACR,KAAC,oBAAoB,IAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAC,SAAS,GAAG,CAC3D,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAClB,KAAC,qBAAqB,IACpB,OAAO,EAAE,aAAa,EACtB,WAAW,EAAE,QAAQ,aAAR,QAAQ,cAAR,QAAQ,GAAI,EAAE,EAC3B,YAAY,EAAE,YAAY,GAC1B,CACH,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CACb,YAAG,SAAS,EAAC,wCAAwC,YAAE,QAAQ,GAAK,CACrE,CAAC,CAAC,CAAC,CACF,eAAK,SAAS,EAAC,0BAA0B,aACvC,KAAC,gBAAgB,IAAC,SAAS,EAAC,UAAU,GAAG,EACzC,0BACE,YAAG,SAAS,EAAC,wCAAwC,YAAE,KAAK,GAAK,EACjE,YAAG,SAAS,EAAC,0BAA0B,YAAE,KAAK,GAAK,IAC/C,IACF,CACP,EAGA,CAAC,aAAa,IAAI,gBAAgB,IAAI,CACrC,YACE,SAAS,EAAC,2DAA2D,iBACzD,eAAe,YAE1B,aAAa,CAAC,QAAQ,CAAC,YAAY,EAAE,EAAE,KAAK,EAAE,gBAAgB,EAAE,CAAC,GAChE,CACL,IACY,EAEf,KAAC,IAAI,CAAC,OAAO,KAAG,EAEhB,MAAC,IAAI,CAAC,OAAO,IAAC,SAAS,EAAC,uFAAuF,aAC7G,eAAM,SAAS,EAAC,sCAAsC,YACnD,aAAa,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAC5B,EACP,eAAM,SAAS,EAAC,sCAAsC,YACnD,YAAY,GACR,IACM,EAEd,QAAQ,IAAI,CACX,MAAC,IAAI,CAAC,OAAO,IACX,SAAS,EAAC,uDAAuD,iBACrD,cAAc,aAE1B,eAAM,SAAS,EAAC,+BAA+B,YAC5C,gBAAgB,CAAC,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,YAAY,EAAE,aAAa,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC,GACjF,EACP,eAAM,SAAS,EAAC,+BAA+B,YAC5C,aAAa,CAAC,QAAQ,CAAC,cAAc,EAAE;oCACtC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;oCAClC,QAAQ,EAAE,QAAQ,CAAC,QAAQ;iCAC5B,CAAC,GACG,IACM,CAChB,EAED,MAAC,IAAI,CAAC,OAAO,IAAC,SAAS,EAAC,uDAAuD,aAC7E,eAAM,SAAS,EAAC,yCAAyC,YACtD,aAAa,CAAC,QAAQ,CAAC,WAAW,CAAC,GAC/B,EACP,eAAM,SAAS,EAAC,8CAA8C,YAC3D,SAAS,GACL,IACM,IACV,EAEN,SAAS,CAAC,CAAC,CAAC,KAAC,SAAS,oBAAK,SAAS,EAAI,CAAC,CAAC,CAAC,IAAI,EAGhD,eAAK,SAAS,EAAC,sCAAsC,aACnD,eAAK,SAAS,EAAC,mDAAmD,aAChE,cAAK,GAAG,EAAE,YAAY,EAAE,GAAG,EAAC,EAAE,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAC,MAAM,GAAG,EACzE,0BACE,YAAG,SAAS,EAAC,8DAA8D,YACxE,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,GACpC,EACJ,YAAG,SAAS,EAAC,6CAA6C,YACvD,aAAa,CAAC,QAAQ,CAAC,aAAa,CAAC,GACpC,IACA,IACF,EACN,eAAK,SAAS,EAAC,8CAA8C,aAC3D,cAAK,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAC,EAAE,EAAC,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAC,MAAM,GAAG,EAC7E,0BACE,YAAG,SAAS,EAAC,8DAA8D,YACxE,aAAa,CAAC,QAAQ,CAAC,iBAAiB,CAAC,GACxC,EACJ,YAAG,SAAS,EAAC,6CAA6C,YACvD,aAAa,CAAC,QAAQ,CAAC,iBAAiB,CAAC,GACxC,IACA,IACF,IACF,IACF,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,WAAW,CAAC","sourcesContent":["import React from 'react';\nimport { Badge, Card, Icon, Skeleton } from '@openedx/paragon';\nimport { Group } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport { EdxUnlimitedLogo } from '../BrandMarks/BrandMarks';\nimport CourseEnrollmentCard from '../CourseEnrollmentCard/CourseEnrollmentCard';\nimport ProgramEnrollmentCard from '../ProgramEnrollmentCard/ProgramEnrollmentCard';\nimport PromoCode from '../PromoCode/PromoCode';\nimport type { PromoCodeProps } from '../PromoCode/PromoCode';\nimport type { CourseDetail } from '../../types';\nimport type { AlacarteLine, ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\nimport learnersIcon from './world-icon.svg';\nimport institutionsIcon from './student-icon.svg';\n\nexport type BillingCycle = 'monthly' | 'annual';\n\nexport interface CartSummaryProps {\n productTitle?: string,\n productBlurb?: string,\n /** Promotional monthly price amount e.g. \"39\" */\n monthlyAmount?: string,\n /** Regular monthly price after the promo period e.g. \"59\" */\n monthlyRegularAmount?: string,\n /** Length of the promotional period in months */\n promoMonths?: number,\n /** Annual price amount e.g. \"299\" */\n annualAmount?: string,\n currency?: string,\n defaultCycle?: BillingCycle,\n monthlyRenewalLabel?: string,\n annualRenewalLabel?: string,\n /** Called when the learner switches monthly ↔ annual. */\n onCycleChange?: (cycle: BillingCycle) => void,\n learnerCount?: number,\n /** When set, shows the course enrollment preview card below the summary card. */\n course?: CourseDetail | null,\n /** À la carte checkout line — when present, drives the subtotal/total and\n * the product name shown, in place of the generic edX Plus placeholder. */\n alacarteLine?: AlacarteLine | null,\n /** Program catalog detail (filterTwouProgram) — when present, drives the\n * \"Courses included in this program\" list. */\n programDetail?: ProgramDetail | null,\n /** Fallback program name — used when there is no alacarteLine to provide it\n * (e.g. already-owned overlay). */\n programName?: string | null,\n /** Coupon input — when set, wires PromoCode to tCheckoutUpdate. */\n promoCode?: PromoCodeProps | null,\n}\n\n/** The coupon discount comes back named \"COUPON PERCENT\". Show the learner label instead. */\nfunction discountRowLabel(discountName: string | null | undefined, fallback: string): string {\n const name = discountName?.trim();\n if (!name || name.toUpperCase() === 'COUPON PERCENT') return fallback;\n return name;\n}\n\nconst CartSummary = ({\n productTitle,\n productBlurb,\n learnerCount: learnerCountProp,\n course = null,\n alacarteLine = null,\n programDetail = null,\n programName: programNameProp = null,\n promoCode = null,\n}: CartSummaryProps) => {\n const { formatMessage } = useIntl();\n const title = productTitle ?? formatMessage(messages.productTitle);\n const blurb = productBlurb ?? formatMessage(messages.productBlurb);\n\n // Prefer the product's own enrollment count (course or program catalog field).\n const isProgramLine = alacarteLine?.kind === 'program';\n const subscriberCount = course?.tEnrollmentCount\n ?? programDetail?.tEnrollmentCount\n ?? learnerCountProp\n ?? null;\n\n // No fallback amount — an unresolved à la carte line means we don't know\n // the price yet, so the row is left blank rather than showing a made-up\n // number that could be mistaken for the real one.\n // Program subtotal is price less owned-course deduction (before coupon/discount).\n const deductionAmount = isProgramLine && alacarteLine.kind === 'program'\n ? (alacarteLine.deduction?.amount ?? 0)\n : 0;\n const subtotalAmount = alacarteLine?.price\n ? Math.max(0, alacarteLine.price.amount - deductionAmount)\n : null;\n const subtotalText = subtotalAmount != null && alacarteLine?.price\n ? formatMessage(messages.subtotalAmount, {\n amount: subtotalAmount.toFixed(2), currency: alacarteLine.price.currency,\n })\n : '';\n const totalText = alacarteLine?.amountDue\n ? formatMessage(messages.todaysTotalValue, {\n amount: alacarteLine.amountDue.amount.toFixed(2), currency: alacarteLine.amountDue.currency,\n })\n : '';\n\n const lineName = alacarteLine\n ? (alacarteLine.kind === 'course' ? alacarteLine.courseName : alacarteLine.programName)\n : programNameProp;\n\n const ownedCourses = isProgramLine && alacarteLine.kind === 'program'\n ? (alacarteLine.ownedCourses ?? [])\n : [];\n const ownedCourseNames = ownedCourses\n .map((c) => c.name)\n .filter(Boolean)\n .join(', ');\n const discount = alacarteLine?.discount ?? null;\n\n return (\n <div className=\"cart-summary \">\n <Card className=\"cart-summary__card p-3\">\n <Card.Section className=\"d-flex justify-content-between align-items-center p-0 pb-3\">\n <span className=\"font-weight-bold text-gray-900\">\n {formatMessage(messages.summaryHeading)}\n </span>\n <Badge\n variant=\"light\"\n className=\"d-flex align-items-center px-2 py-1\"\n >\n <Icon\n src={Group}\n className=\"mr-1\"\n style={{ width: '1rem', height: '1rem' }}\n />\n {subscriberCount == null ? (\n <span\n className=\"cart-summary__count-skeleton\"\n aria-busy=\"true\"\n aria-label={formatMessage(messages.loadingSubscribers)}\n data-testid=\"subscriber-count-skeleton\"\n >\n <Skeleton width={96} height={12} />\n </span>\n ) : (\n formatMessage(messages.learnersSubscribed, {\n count: subscriberCount.toLocaleString(),\n })\n )}\n </Badge>\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"p-0 py-3\">\n {course ? (\n <CourseEnrollmentCard course={course} variant=\"summary\" />\n ) : programDetail ? (\n <ProgramEnrollmentCard\n program={programDetail}\n programName={lineName ?? ''}\n ownedCourses={ownedCourses}\n />\n ) : lineName ? (\n <p className=\"font-weight-bold mb-0 text-primary-500\">{lineName}</p>\n ) : (\n <div className=\"d-flex align-items-start\">\n <EdxUnlimitedLogo ariaLabel=\"edX Plus\" />\n <div>\n <p className=\"font-weight-bold mb-0 text-primary-500\">{title}</p>\n <p className=\"small mb-0 text-gray-900\">{blurb}</p>\n </div>\n </div>\n )}\n {/* Fallback when catalog detail hasn't loaded yet — once it has,\n owned courses are listed under \"Previously purchased\" in the card. */}\n {!programDetail && ownedCourseNames && (\n <p\n className=\"cart-summary__already-yours small text-gray-700 mb-0 mt-2\"\n data-testid=\"already-yours\"\n >\n {formatMessage(messages.alreadyYours, { names: ownedCourseNames })}\n </p>\n )}\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"cart-summary__subtotal-row d-flex justify-content-between align-items-center p-0 pt-3\">\n <span className=\"cart-summary__subtotal text-gray-900\">\n {formatMessage(messages.subtotal)}\n </span>\n <span className=\"cart-summary__subtotal text-gray-900\">\n {subtotalText}\n </span>\n </Card.Section>\n\n {discount && (\n <Card.Section\n className=\"d-flex justify-content-between align-items-center p-0\"\n data-testid=\"discount-row\"\n >\n <span className=\"text-success font-weight-bold\">\n {discountRowLabel(alacarteLine?.discountName, formatMessage(messages.discountApplied))}\n </span>\n <span className=\"text-success font-weight-bold\">\n {formatMessage(messages.discountAmount, {\n amount: discount.amount.toFixed(2),\n currency: discount.currency,\n })}\n </span>\n </Card.Section>\n )}\n\n <Card.Section className=\"d-flex justify-content-between align-items-center p-0\">\n <span className=\"cart-summary__total-label text-gray-900\">\n {formatMessage(messages.todaysTotal)}\n </span>\n <span className=\"cart-summary__total-value mb-0 text-gray-900\">\n {totalText}\n </span>\n </Card.Section>\n </Card>\n\n {promoCode ? <PromoCode {...promoCode} /> : null}\n\n {/* Stats row */}\n <div className=\"cart-summary__stats d-none d-lg-flex\">\n <div className=\"cart-summary__stat d-flex align-items-center mr-4\">\n <img src={learnersIcon} alt=\"\" width={50} height={50} className=\"mr-2\" />\n <div>\n <p className=\"cart-summary__stat-count font-weight-bold text-gray-500 mb-0\">\n {formatMessage(messages.learnersCount)}\n </p>\n <p className=\"cart-summary__stat-label text-gray-500 mb-0\">\n {formatMessage(messages.learnersLabel)}\n </p>\n </div>\n </div>\n <div className=\"cart-summary__stat d-flex align-items-center\">\n <img src={institutionsIcon} alt=\"\" width={50} height={50} className=\"mr-2\" />\n <div>\n <p className=\"cart-summary__stat-count font-weight-bold text-gray-500 mb-0\">\n {formatMessage(messages.institutionsCount)}\n </p>\n <p className=\"cart-summary__stat-label text-gray-500 mb-0\">\n {formatMessage(messages.institutionsLabel)}\n </p>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default CartSummary;\n"]}
|
|
@@ -53,8 +53,8 @@ const messages = defineMessages({
|
|
|
53
53
|
},
|
|
54
54
|
discountApplied: {
|
|
55
55
|
id: 'checkout.cartSummary.discountApplied',
|
|
56
|
-
defaultMessage: '
|
|
57
|
-
description: '
|
|
56
|
+
defaultMessage: 'Discount applied',
|
|
57
|
+
description: 'Label for a coupon discount, and the fallback when discountName is absent',
|
|
58
58
|
},
|
|
59
59
|
discountAmount: {
|
|
60
60
|
id: 'checkout.cartSummary.discountAmount',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/CartSummary/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,cAAc;IACd,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,oCAAoC;KAClD;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,8CAA8C;KAC5D;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,+DAA+D;QAC/E,WAAW,EAAE,oEAAoE;KAClF;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,0BAA0B;QAC1C,WAAW,EAAE,yCAAyC;KACvD;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,0BAA0B;QAC1C,WAAW,EAAE,oDAAoD;KAClE;IACD,QAAQ,EAAE;QACR,EAAE,EAAE,+BAA+B;QACnC,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,4BAA4B;KAC1C;IACD,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,sBAAsB;QACtC,WAAW,EAAE,wDAAwD;KACtE;IACD,WAAW,EAAE;QACX,EAAE,EAAE,kCAAkC;QACtC,cAAc,EAAE,eAAe;QAC/B,WAAW,EAAE,mCAAmC;KACjD;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,uCAAuC;QAC3C,cAAc,EAAE,sBAAsB;QACtC,WAAW,EAAE,6DAA6D;KAC3E;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,wBAAwB;QACxC,WAAW,EAAE,gFAAgF;KAC9F;IACD,eAAe,EAAE;QACf,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/CartSummary/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,cAAc;IACd,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,oCAAoC;KAClD;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,8CAA8C;KAC5D;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,+DAA+D;QAC/E,WAAW,EAAE,oEAAoE;KAClF;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,0BAA0B;QAC1C,WAAW,EAAE,yCAAyC;KACvD;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,0BAA0B;QAC1C,WAAW,EAAE,oDAAoD;KAClE;IACD,QAAQ,EAAE;QACR,EAAE,EAAE,+BAA+B;QACnC,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,4BAA4B;KAC1C;IACD,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,sBAAsB;QACtC,WAAW,EAAE,wDAAwD;KACtE;IACD,WAAW,EAAE;QACX,EAAE,EAAE,kCAAkC;QACtC,cAAc,EAAE,eAAe;QAC/B,WAAW,EAAE,mCAAmC;KACjD;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,uCAAuC;QAC3C,cAAc,EAAE,sBAAsB;QACtC,WAAW,EAAE,6DAA6D;KAC3E;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,wBAAwB;QACxC,WAAW,EAAE,gFAAgF;KAC9F;IACD,eAAe,EAAE;QACf,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,kBAAkB;QAClC,WAAW,EAAE,2EAA2E;KACzF;IACD,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,uBAAuB;QACvC,WAAW,EAAE,6CAA6C;KAC3D;IACD,SAAS,EAAE;QACT,EAAE,EAAE,gCAAgC;QACpC,cAAc,EAAE,uBAAuB;QACvC,WAAW,EAAE,gEAAgE;KAC9E;IACD,eAAe,EAAE;QACf,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,uBAAuB;QACvC,WAAW,EAAE,8CAA8C;KAC5D;IACD,aAAa,EAAE;QACb,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,MAAM;QACtB,WAAW,EAAE,qBAAqB;KACnC;IACD,aAAa,EAAE;QACb,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,qCAAqC;KACnD;IACD,iBAAiB,EAAE;QACjB,EAAE,EAAE,wCAAwC;QAC5C,cAAc,EAAE,MAAM;QACtB,WAAW,EAAE,gCAAgC;KAC9C;IACD,iBAAiB,EAAE;QACjB,EAAE,EAAE,wCAAwC;QAC5C,cAAc,EAAE,qBAAqB;QACrC,WAAW,EAAE,yCAAyC;KACvD;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n // CartSummary\n summaryHeading: {\n id: 'checkout.cartSummary.summaryHeading',\n defaultMessage: 'Summary',\n description: 'Heading for the order summary card',\n },\n productTitle: {\n id: 'checkout.cartSummary.productTitle',\n defaultMessage: 'edX Plus',\n description: 'Product name shown in the order summary card',\n },\n productBlurb: {\n id: 'checkout.cartSummary.productBlurb',\n defaultMessage: 'Subscribe and get access to hundreds of courses and programs.',\n description: 'Short product description under the name in the order summary card',\n },\n learnersSubscribed: {\n id: 'checkout.cartSummary.learnersSubscribed',\n defaultMessage: '{count} already enrolled',\n description: 'Badge showing number of active learners',\n },\n loadingSubscribers: {\n id: 'checkout.cartSummary.loadingSubscribers',\n defaultMessage: 'Loading subscriber count',\n description: 'Accessible label for the subscriber count skeleton',\n },\n subtotal: {\n id: 'checkout.cartSummary.subtotal',\n defaultMessage: 'Subtotal',\n description: 'Label for the subtotal row',\n },\n subtotalAmount: {\n id: 'checkout.cartSummary.subtotalAmount',\n defaultMessage: '${amount} {currency}',\n description: 'Formatted subtotal with currency code e.g. $219.00 USD',\n },\n todaysTotal: {\n id: 'checkout.cartSummary.todaysTotal',\n defaultMessage: \"Today's total\",\n description: \"Label for today's total price row\",\n },\n todaysTotalValue: {\n id: 'checkout.cartSummary.todaysTotalValue',\n defaultMessage: '${amount} {currency}',\n description: \"Formatted today's total with currency code e.g. $219.00 USD\",\n },\n alreadyYours: {\n id: 'checkout.cartSummary.alreadyYours',\n defaultMessage: 'Already yours: {names}',\n description: 'Lists program member courses the learner already owns (explains the deduction)',\n },\n discountApplied: {\n id: 'checkout.cartSummary.discountApplied',\n defaultMessage: 'Discount applied',\n description: 'Label for a coupon discount, and the fallback when discountName is absent',\n },\n discountAmount: {\n id: 'checkout.cartSummary.discountAmount',\n defaultMessage: '-${amount} {currency}',\n description: 'Formatted discount amount e.g. -$122.50 USD',\n },\n deduction: {\n id: 'checkout.cartSummary.deduction',\n defaultMessage: 'Already owned courses',\n description: 'Label for the program price deduction for owned member courses',\n },\n deductionAmount: {\n id: 'checkout.cartSummary.deductionAmount',\n defaultMessage: '-${amount} {currency}',\n description: 'Formatted deduction amount e.g. -$100.00 USD',\n },\n learnersCount: {\n id: 'checkout.cartSummary.learnersCount',\n defaultMessage: '100M',\n description: 'Total learners stat',\n },\n learnersLabel: {\n id: 'checkout.cartSummary.learnersLabel',\n defaultMessage: 'Learners',\n description: 'Label under the learners count stat',\n },\n institutionsCount: {\n id: 'checkout.cartSummary.institutionsCount',\n defaultMessage: '250+',\n description: 'Expert institutions count stat',\n },\n institutionsLabel: {\n id: 'checkout.cartSummary.institutionsLabel',\n defaultMessage: 'Expert institutions',\n description: 'Label under the institutions count stat',\n },\n});\n\nexport default messages;\n"]}
|
|
@@ -208,10 +208,7 @@ const Checkout = (_a) => {
|
|
|
208
208
|
amount: alacarte.line.amountDue.amount.toFixed(2),
|
|
209
209
|
currency: alacarte.line.amountDue.currency,
|
|
210
210
|
}) })), showForm && (_jsxs(_Fragment, { children: [showValidationBanner && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, className: "mb-3", children: formatMessage(messages.validationBanner) })), c.phase === 'failed' && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, className: "mb-3", children: failedMessage
|
|
211
|
-
|| formatMessage(messages.paymentProcessingDescription) })), appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (_jsxs(Alert, { variant: "success", icon: CheckCircle, dismissible: true, closeLabel: formatMessage(messages.dismiss), onClose: () => setDismissedCouponAlert(appliedCouponCode), className: "coupon-applied-alert", "data-testid": "coupon-applied-alert", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83C\uDF89 " }), formatMessage(messages.couponAppliedAlert, {
|
|
212
|
-
code: appliedCouponCode,
|
|
213
|
-
percent: appliedDiscount.percent,
|
|
214
|
-
})] })), alacarte.removedCouponCode && (() => {
|
|
211
|
+
|| formatMessage(messages.paymentProcessingDescription) })), appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (_jsxs(Alert, { variant: "success", icon: CheckCircle, dismissible: true, closeLabel: formatMessage(messages.dismiss), onClose: () => setDismissedCouponAlert(appliedCouponCode), className: "coupon-applied-alert", "data-testid": "coupon-applied-alert", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83C\uDF89 " }), formatMessage(messages.couponAppliedAlert)] })), alacarte.removedCouponCode && (() => {
|
|
215
212
|
const removedCode = alacarte.removedCouponCode;
|
|
216
213
|
const removedText = formatMessage(messages.couponRemovedAlert, { code: removedCode });
|
|
217
214
|
// Bold the interpolated code without baking markup into the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useCallback, useEffect, useState } from 'react';\nimport { Alert, Button, Col, Row } from '@openedx/paragon';\nimport { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport { useAlacarteCheckout } from '../../data/hooks/useAlacarteCheckout';\nimport { ALLOWED_CHECKOUT_COUNTRIES, GATE_CHECK_COUNTRY } from '../../constants';\nimport CartSummary from '../CartSummary/CartSummary';\nimport EnrollmentSummary from '../EnrollmentSummary/EnrollmentSummary';\nimport PaymentForm from '../PaymentForm/PaymentForm';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport AlacarteNotice from '../AlacarteNotice/AlacarteNotice';\nimport AlacarteConfirmation from '../AlacarteConfirmation/AlacarteConfirmation';\nimport AlacarteDebugPanel from '../AlacarteDebugPanel/AlacarteDebugPanel'; // TEMPORARY — remove alongside AlacarteDebugPanel\nimport { getTrialDateRange } from '@src/utils';\nimport { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';\nimport { appliedDiscountPercent } from '../../utils/discount';\nimport { confirmPaymentWithMethodId, getLearnerFacingStripeMessage } from '../../utils/stripeCard';\nimport { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle, CourseDetail } from '../../types';\nimport type { ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\n\n// TEMPORARY — redirect-home-on-noActiveCheckout disabled per request; shows a\n// banner instead so the case is visible while the flow is being verified.\n// Flip back to true to restore the redirect.\nconst REDIRECT_ON_NO_ACTIVE_CHECKOUT = false;\n\nexport type CheckoutProps = UseCheckoutOptions & {\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n /** Endpoint used to look up the representative IP for a selected country. */\n geolocationUrl?: string,\n /** Learner dashboard / order history URL for confirmation CTAs. */\n orderHistoryUrl?: string,\n};\n\nconst Checkout = ({\n homeUrl = '/',\n orderHistoryUrl,\n ...props\n}: CheckoutProps) => {\n const { formatMessage } = useIntl();\n const alacarte = useAlacarteCheckout({ apiUrl: props.apiUrl, edxSessionUrl: props.edxSessionUrl });\n const c = useCheckout(props);\n const [promoCodeValue, setPromoCodeValue] = useState('');\n const [dismissedCouponAlert, setDismissedCouponAlert] = useState<string | null>(null);\n const [showValidationBanner, setShowValidationBanner] = useState(false);\n // Last code we successfully priced — drives the \"Save {percent}% with code\n // {code}\" hint on the empty field while a coupon is not yet applied.\n const [advertisedOffer, setAdvertisedOffer] = useState<{ code: string, percent: number } | null>(null);\n const isAlacarteReady = (alacarte.phase === 'ready' || alacarte.phase === 'alreadyOwned') && !!alacarte.checkout;\n const appliedCouponCode = alacarte.checkout?.couponCode?.trim()\n ? alacarte.checkout.couponCode.trim()\n : null;\n const discountPercent = appliedDiscountPercent(alacarte.line);\n // Only treat the line discount as a removable coupon when the backend says\n // so (or a coupon code is present). Automatic offers stay on CartSummary\n // via discountName and must not drive PromoCode's applied/remove UI.\n const isCouponDiscount = !!alacarte.line?.discount\n && alacarte.line.discountSource !== 'automatic'\n && (alacarte.line.discountSource === 'coupon' || !!appliedCouponCode);\n const appliedDiscount = isCouponDiscount && discountPercent != null\n ? {\n percent: discountPercent,\n amount: alacarte.line!.discount!.amount,\n currency: alacarte.line!.discount!.currency,\n }\n : null;\n\n // Keep the coupon field in sync when a code is applied via URL or API.\n // Don't overwrite the field with empty when only an automatic offer applies.\n useEffect(() => {\n if (alacarte.checkout?.couponCode) {\n setPromoCodeValue(alacarte.checkout.couponCode);\n }\n }, [alacarte.checkout?.couponCode]);\n\n useEffect(() => {\n if (appliedCouponCode && isCouponDiscount && discountPercent != null) {\n setAdvertisedOffer({ code: appliedCouponCode, percent: discountPercent });\n }\n }, [appliedCouponCode, isCouponDiscount, discountPercent]);\n\n // No query params and no active cart — nothing to check out, send the\n // learner home rather than showing a placeholder.\n useEffect(() => {\n if (REDIRECT_ON_NO_ACTIVE_CHECKOUT && alacarte.phase === 'noActiveCheckout') {\n window.location.href = homeUrl;\n }\n }, [alacarte.phase, homeUrl]);\n\n // Express checkout (Apple Pay / Google Pay): runs the full place-order flow\n // using a wallet-provided PaymentMethod id instead of the mounted card element.\n const handleExpressPayment = useCallback(async (paymentMethodId: string, walletCountry?: string) => {\n if (!c.stripe) throw new Error('Stripe not loaded');\n const stripeInstance = c.stripe;\n const country = (c.billing.country || walletCountry || c.userCountry || '').trim().toUpperCase();\n if (!country) {\n throw new Error('Please select a country before using Apple Pay or Google Pay.');\n }\n\n await alacarte.placeOrderAsync({\n billing: {\n ...c.billing,\n country,\n },\n confirmCardPayment: async (clientSecret: string) => {\n await confirmPaymentWithMethodId({\n stripe: stripeInstance,\n clientSecret,\n paymentMethodId,\n });\n },\n });\n }, [alacarte, c.billing, c.userCountry, c.stripe]);\n\n const showProcessing\n = c.returningFromStripe\n && c.processingSubscription\n && c.phase !== 'success'\n && c.phase !== 'blocked'\n && c.phase !== 'failed';\n const showForm\n = !showProcessing\n && (c.phase === 'init'\n || c.phase === 'ready'\n || c.phase === 'failed'\n || c.phase === 'processing');\n const failedMessage = getLearnerFacingCheckoutMessage(c.lastComment);\n\n const { displayStart, displayEnd } = getTrialDateRange();\n\n const isBillingCountryBlocked = GATE_CHECK_COUNTRY\n && !!c.billing.country\n && !ALLOWED_CHECKOUT_COUNTRIES.includes(c.billing.country);\n\n const selectedCycle: BillingCycle = c.selectedCycle;\n\n // Prefer the à la carte course's own detail (skills/badge/image, richer than\n // the subscription flow's ?course_run_key= lookup) when we selected a course.\n // When already-owned the line may be null, so fall back to the selector kind.\n const isCourseProduct = alacarte.line?.kind === 'course' || alacarte.selector?.kind === 'course';\n const isProgramProduct = alacarte.line?.kind === 'program' || alacarte.selector?.kind === 'program';\n\n const alacarteCourseDetail: CourseDetail | null\n = isCourseProduct && alacarte.productDetail && 'maId' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n const courseForSummary = alacarteCourseDetail ?? c.course;\n\n const alacarteProgramDetail: ProgramDetail | null\n = isProgramProduct && alacarte.productDetail && 'tCourses' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n\n const handleStartCourse = () => {\n const alacarteCourseUrl = alacarteCourseDetail?.tUrlCourse\n || (alacarte.productDetail && 'tUrlCourse' in alacarte.productDetail\n ? alacarte.productDetail.tUrlCourse\n : null);\n if (alacarteCourseUrl) {\n window.location.href = alacarteCourseUrl;\n } else if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = homeUrl || '/';\n }\n };\n\n const courseLabel = c.course\n ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')\n : null;\n\n if (showProcessing) {\n return <ProcessingScreen />;\n }\n\n // TEMPORARY — flip to true to dump à la carte hook state while validating\n // against the real backend. Remove AlacarteDebugPanel once the flow is solid.\n const SHOW_ALACARTE_DEBUG = false;\n const alacarteDebug = SHOW_ALACARTE_DEBUG\n ? <AlacarteDebugPanel data={alacarte} />\n : null;\n\n // Confirmation: show once the order is ready (or failed to load). While\n // tPlacedOrder is still polling, keep the checkout form so Pay now can show\n // \"Processing...\" instead of a full-page loader.\n if (alacarte.placedOrder || alacarte.placedOrderError) {\n const confirmationTitle = alacarte.line?.kind === 'program'\n ? alacarte.line.programName\n : alacarte.line?.kind === 'course'\n ? alacarte.line.courseName\n : null;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container alacarte-confirmation__container bg-light-200\">\n <Col xs={12}>\n <AlacarteConfirmation\n order={alacarte.placedOrder}\n awaiting={false}\n error={alacarte.placedOrderError}\n onContinue={handleStartCourse}\n productDetail={alacarte.productDetail}\n productTitle={confirmationTitle}\n ownedCourses={\n alacarte.line?.kind === 'program'\n ? (alacarte.line.ownedCourses ?? null)\n : null\n }\n orderHistoryUrl={orderHistoryUrl ?? null}\n placedAt={alacarte.placedOrderPlacedAt}\n />\n </Col>\n </Row>\n </>\n );\n }\n\n if (alacarte.phase === 'loading') {\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout' && REDIRECT_ON_NO_ACTIVE_CHECKOUT) {\n // Redirects home via the effect above — show a spinner rather than a\n // notice while that happens.\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout') {\n return (\n <>\n {alacarteDebug}\n <AlacarteNotice messageId={alacarte.noticeMessageId!} />\n </>\n );\n }\n // Product unavailable / not found / not purchasable — banner only, no checkout form.\n if (alacarte.phase === 'notPurchasable' || alacarte.phase === 'error') {\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <Col xs={12}>\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n data-testid=\"alacarte-error-banner\"\n >\n {alacarte.error || 'Something went wrong with checkout.'}\n </Alert>\n </Col>\n </Row>\n </>\n );\n }\n\n const isAlreadyOwned = alacarte.isAlreadyOwned;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <CountryUnavailableModal\n isOpen={isBillingCountryBlocked}\n homeUrl={homeUrl}\n />\n <Col xs={12} className=\"d-lg-none\">\n <h1 className=\"font-weight-bold text-primary-900\">Checkout</h1>\n </Col>\n\n <Col xs={12} className={isAlreadyOwned ? 'checkout__owned-overlay' : ''}>\n <Row className=\"flex-column-reverse gap-5 flex-lg-row\">\n {/* ── LEFT ── */}\n <Col xs={12} lg={7} xxl={9} className=\"payment__options\">\n <div>\n <h1 className=\"font-weight-bold text-primary-900 d-none d-lg-block mb-4\">\n Checkout\n </h1>\n\n {isAlreadyOwned && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n className=\"checkout__already-owned-alert mb-3\"\n data-testid=\"already-owned-alert\"\n >\n <p className=\"mb-3\">\n {formatMessage(\n alacarte.selector?.kind === 'program'\n ? messages.alreadyPaidProgram\n : messages.alreadyPaidCourse,\n )}\n </p>\n <div className=\"d-flex align-items-center justify-content-end gap-3 flex-wrap\">\n {orderHistoryUrl && (\n <a href={orderHistoryUrl} className=\"text-gray-900 font-weight-bold\">\n {formatMessage(messages.viewOrderHistory)}\n </a>\n )}\n <Button variant=\"brand\" className=\"rounded-pill\" onClick={handleStartCourse}>\n {formatMessage(messages.goToCourse)}\n </Button>\n </div>\n </Alert>\n )}\n\n {c.error && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={c.clearError}\n >\n {c.error}\n </Alert>\n )}\n\n {alacarte.placeOrderError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearPlaceOrderError}\n className=\"mb-3\"\n >\n {alacarte.placeOrderError}\n </Alert>\n )}\n\n {alacarte.paymentDecline?.message && !alacarte.placeOrderError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n className=\"mb-3\"\n data-testid=\"payment-decline-alert\"\n >\n {getLearnerFacingStripeMessage({\n message: alacarte.paymentDecline.message,\n code: alacarte.paymentDecline.code ?? undefined,\n })}\n </Alert>\n )}\n\n {alacarte.couponError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearCouponError}\n className=\"mb-3\"\n data-testid=\"coupon-error-alert\"\n >\n {alacarte.couponError}\n </Alert>\n )}\n\n {!alacarte.priceAccepted && isAlacarteReady && alacarte.line?.amountDue && (\n <Alert variant=\"info\" className=\"mb-3\" data-testid=\"price-accept-notice\">\n {formatMessage(messages.priceChangedNotice, {\n amount: alacarte.line.amountDue.amount.toFixed(2),\n currency: alacarte.line.amountDue.currency,\n })}\n </Alert>\n )}\n\n {showForm && (\n <>\n {showValidationBanner && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {formatMessage(messages.validationBanner)}\n </Alert>\n )}\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {failedMessage\n || formatMessage(messages.paymentProcessingDescription)}\n </Alert>\n )}\n {appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (\n <Alert\n variant=\"success\"\n icon={CheckCircle}\n dismissible\n closeLabel={formatMessage(messages.dismiss)}\n onClose={() => setDismissedCouponAlert(appliedCouponCode)}\n className=\"coupon-applied-alert\"\n data-testid=\"coupon-applied-alert\"\n >\n <span aria-hidden=\"true\">🎉 </span>\n {formatMessage(messages.couponAppliedAlert, {\n code: appliedCouponCode,\n percent: appliedDiscount.percent,\n })}\n </Alert>\n )}\n {alacarte.removedCouponCode && (() => {\n const removedCode = alacarte.removedCouponCode;\n const removedText = formatMessage(\n messages.couponRemovedAlert,\n { code: removedCode },\n );\n // Bold the interpolated code without baking markup into the\n // translation string — find it in the formatted sentence.\n const codeIndex = removedText.indexOf(removedCode);\n return (\n <Alert\n variant=\"success\"\n icon={CheckCircle}\n dismissible\n closeLabel={formatMessage(messages.dismiss)}\n onClose={alacarte.clearRemovedCouponCode}\n className=\"coupon-removed-alert\"\n data-testid=\"coupon-removed-alert\"\n >\n {codeIndex === -1 ? removedText : (\n <>\n {removedText.slice(0, codeIndex)}\n <strong>{removedCode}</strong>\n {removedText.slice(codeIndex + removedCode.length)}\n </>\n )}\n </Alert>\n );\n })()}\n <PaymentForm\n billing={c.billing}\n setField={c.setField}\n numberMountRef={c.numberMountRef}\n expiryMountRef={c.expiryMountRef}\n cvcMountRef={c.cvcMountRef}\n cardError={c.cardError}\n cardFieldStates={c.cardFieldStates}\n stripeReady={c.stripeReady}\n sessionReady={c.sessionReady}\n submitting={\n isAlacarteReady\n ? (alacarte.placingOrder || alacarte.awaitingPlacedOrder)\n : c.processingSubscription\n }\n onPlaceOrder={\n isAlacarteReady\n ? () => {\n alacarte.placeOrder({\n billing: {\n ...c.billing,\n // Country is required on tCheckoutPlaceOrder (two-letter ISO).\n // Prefer the form selection; fall back to gate geolocation.\n country: c.billing.country || c.userCountry || '',\n },\n confirmCardPayment: c.confirmCardPayment,\n });\n }\n : c.placeOrder\n }\n onValidationError={setShowValidationBanner}\n trialEnd={selectedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={null}\n cycleToShow={selectedCycle === 'monthly' ? 'monthly' : 'annual'}\n userCountry={c.userCountry}\n geolocationUrl={props.geolocationUrl}\n alacarteLine={alacarte.line}\n stripe={c.stripe}\n expressCountry={c.billing.country || c.userCountry || 'US'}\n onExpressPayment={isAlacarteReady ? handleExpressPayment : undefined}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n courseLabel={courseLabel}\n onFindCourse={handleStartCourse}\n />\n )}\n\n {c.phase === 'blocked' && (\n <BlockedScreen detail={c.lastComment} />\n )}\n </div>\n </Col>\n\n {/* ── RIGHT — swaps to EnrollmentSummary on success ── */}\n <Col xs={12} lg={5} xxl={3} className=\"payment__summary\">\n {c.phase === 'success' ? (\n <EnrollmentSummary\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n defaultCycle={selectedCycle}\n onCycleChange={c.processingSubscription ? undefined : c.setCycle}\n course={courseForSummary}\n alacarteLine={alacarte.line}\n programDetail={alacarteProgramDetail}\n programName={alacarte.ownedProductName}\n promoCode={isAlacarteReady ? {\n value: promoCodeValue,\n onChange: (next) => {\n setPromoCodeValue(next);\n if (alacarte.couponError) alacarte.clearCouponError();\n },\n onApply: () => alacarte.applyCoupon(promoCodeValue),\n onRemove: () => {\n alacarte.removeCoupon();\n setPromoCodeValue('');\n setAdvertisedOffer(null);\n },\n appliedCode: appliedCouponCode,\n discount: appliedDiscount,\n offer: !appliedCouponCode ? advertisedOffer : null,\n error: alacarte.couponError,\n isApplying: alacarte.couponApplying,\n disabled: alacarte.placingOrder,\n } : null}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n </>\n );\n};\n\nexport default Checkout;\n"]}
|
|
1
|
+
{"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useCallback, useEffect, useState } from 'react';\nimport { Alert, Button, Col, Row } from '@openedx/paragon';\nimport { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport { useAlacarteCheckout } from '../../data/hooks/useAlacarteCheckout';\nimport { ALLOWED_CHECKOUT_COUNTRIES, GATE_CHECK_COUNTRY } from '../../constants';\nimport CartSummary from '../CartSummary/CartSummary';\nimport EnrollmentSummary from '../EnrollmentSummary/EnrollmentSummary';\nimport PaymentForm from '../PaymentForm/PaymentForm';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport AlacarteNotice from '../AlacarteNotice/AlacarteNotice';\nimport AlacarteConfirmation from '../AlacarteConfirmation/AlacarteConfirmation';\nimport AlacarteDebugPanel from '../AlacarteDebugPanel/AlacarteDebugPanel'; // TEMPORARY — remove alongside AlacarteDebugPanel\nimport { getTrialDateRange } from '@src/utils';\nimport { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';\nimport { appliedDiscountPercent } from '../../utils/discount';\nimport { confirmPaymentWithMethodId, getLearnerFacingStripeMessage } from '../../utils/stripeCard';\nimport { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle, CourseDetail } from '../../types';\nimport type { ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\n\n// TEMPORARY — redirect-home-on-noActiveCheckout disabled per request; shows a\n// banner instead so the case is visible while the flow is being verified.\n// Flip back to true to restore the redirect.\nconst REDIRECT_ON_NO_ACTIVE_CHECKOUT = false;\n\nexport type CheckoutProps = UseCheckoutOptions & {\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n /** Endpoint used to look up the representative IP for a selected country. */\n geolocationUrl?: string,\n /** Learner dashboard / order history URL for confirmation CTAs. */\n orderHistoryUrl?: string,\n};\n\nconst Checkout = ({\n homeUrl = '/',\n orderHistoryUrl,\n ...props\n}: CheckoutProps) => {\n const { formatMessage } = useIntl();\n const alacarte = useAlacarteCheckout({ apiUrl: props.apiUrl, edxSessionUrl: props.edxSessionUrl });\n const c = useCheckout(props);\n const [promoCodeValue, setPromoCodeValue] = useState('');\n const [dismissedCouponAlert, setDismissedCouponAlert] = useState<string | null>(null);\n const [showValidationBanner, setShowValidationBanner] = useState(false);\n // Last code we successfully priced — drives the \"Save {percent}% with code\n // {code}\" hint on the empty field while a coupon is not yet applied.\n const [advertisedOffer, setAdvertisedOffer] = useState<{ code: string, percent: number } | null>(null);\n const isAlacarteReady = (alacarte.phase === 'ready' || alacarte.phase === 'alreadyOwned') && !!alacarte.checkout;\n const appliedCouponCode = alacarte.checkout?.couponCode?.trim()\n ? alacarte.checkout.couponCode.trim()\n : null;\n const discountPercent = appliedDiscountPercent(alacarte.line);\n // Only treat the line discount as a removable coupon when the backend says\n // so (or a coupon code is present). Automatic offers stay on CartSummary\n // via discountName and must not drive PromoCode's applied/remove UI.\n const isCouponDiscount = !!alacarte.line?.discount\n && alacarte.line.discountSource !== 'automatic'\n && (alacarte.line.discountSource === 'coupon' || !!appliedCouponCode);\n const appliedDiscount = isCouponDiscount && discountPercent != null\n ? {\n percent: discountPercent,\n amount: alacarte.line!.discount!.amount,\n currency: alacarte.line!.discount!.currency,\n }\n : null;\n\n // Keep the coupon field in sync when a code is applied via URL or API.\n // Don't overwrite the field with empty when only an automatic offer applies.\n useEffect(() => {\n if (alacarte.checkout?.couponCode) {\n setPromoCodeValue(alacarte.checkout.couponCode);\n }\n }, [alacarte.checkout?.couponCode]);\n\n useEffect(() => {\n if (appliedCouponCode && isCouponDiscount && discountPercent != null) {\n setAdvertisedOffer({ code: appliedCouponCode, percent: discountPercent });\n }\n }, [appliedCouponCode, isCouponDiscount, discountPercent]);\n\n // No query params and no active cart — nothing to check out, send the\n // learner home rather than showing a placeholder.\n useEffect(() => {\n if (REDIRECT_ON_NO_ACTIVE_CHECKOUT && alacarte.phase === 'noActiveCheckout') {\n window.location.href = homeUrl;\n }\n }, [alacarte.phase, homeUrl]);\n\n // Express checkout (Apple Pay / Google Pay): runs the full place-order flow\n // using a wallet-provided PaymentMethod id instead of the mounted card element.\n const handleExpressPayment = useCallback(async (paymentMethodId: string, walletCountry?: string) => {\n if (!c.stripe) throw new Error('Stripe not loaded');\n const stripeInstance = c.stripe;\n const country = (c.billing.country || walletCountry || c.userCountry || '').trim().toUpperCase();\n if (!country) {\n throw new Error('Please select a country before using Apple Pay or Google Pay.');\n }\n\n await alacarte.placeOrderAsync({\n billing: {\n ...c.billing,\n country,\n },\n confirmCardPayment: async (clientSecret: string) => {\n await confirmPaymentWithMethodId({\n stripe: stripeInstance,\n clientSecret,\n paymentMethodId,\n });\n },\n });\n }, [alacarte, c.billing, c.userCountry, c.stripe]);\n\n const showProcessing\n = c.returningFromStripe\n && c.processingSubscription\n && c.phase !== 'success'\n && c.phase !== 'blocked'\n && c.phase !== 'failed';\n const showForm\n = !showProcessing\n && (c.phase === 'init'\n || c.phase === 'ready'\n || c.phase === 'failed'\n || c.phase === 'processing');\n const failedMessage = getLearnerFacingCheckoutMessage(c.lastComment);\n\n const { displayStart, displayEnd } = getTrialDateRange();\n\n const isBillingCountryBlocked = GATE_CHECK_COUNTRY\n && !!c.billing.country\n && !ALLOWED_CHECKOUT_COUNTRIES.includes(c.billing.country);\n\n const selectedCycle: BillingCycle = c.selectedCycle;\n\n // Prefer the à la carte course's own detail (skills/badge/image, richer than\n // the subscription flow's ?course_run_key= lookup) when we selected a course.\n // When already-owned the line may be null, so fall back to the selector kind.\n const isCourseProduct = alacarte.line?.kind === 'course' || alacarte.selector?.kind === 'course';\n const isProgramProduct = alacarte.line?.kind === 'program' || alacarte.selector?.kind === 'program';\n\n const alacarteCourseDetail: CourseDetail | null\n = isCourseProduct && alacarte.productDetail && 'maId' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n const courseForSummary = alacarteCourseDetail ?? c.course;\n\n const alacarteProgramDetail: ProgramDetail | null\n = isProgramProduct && alacarte.productDetail && 'tCourses' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n\n const handleStartCourse = () => {\n const alacarteCourseUrl = alacarteCourseDetail?.tUrlCourse\n || (alacarte.productDetail && 'tUrlCourse' in alacarte.productDetail\n ? alacarte.productDetail.tUrlCourse\n : null);\n if (alacarteCourseUrl) {\n window.location.href = alacarteCourseUrl;\n } else if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = homeUrl || '/';\n }\n };\n\n const courseLabel = c.course\n ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')\n : null;\n\n if (showProcessing) {\n return <ProcessingScreen />;\n }\n\n // TEMPORARY — flip to true to dump à la carte hook state while validating\n // against the real backend. Remove AlacarteDebugPanel once the flow is solid.\n const SHOW_ALACARTE_DEBUG = false;\n const alacarteDebug = SHOW_ALACARTE_DEBUG\n ? <AlacarteDebugPanel data={alacarte} />\n : null;\n\n // Confirmation: show once the order is ready (or failed to load). While\n // tPlacedOrder is still polling, keep the checkout form so Pay now can show\n // \"Processing...\" instead of a full-page loader.\n if (alacarte.placedOrder || alacarte.placedOrderError) {\n const confirmationTitle = alacarte.line?.kind === 'program'\n ? alacarte.line.programName\n : alacarte.line?.kind === 'course'\n ? alacarte.line.courseName\n : null;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container alacarte-confirmation__container bg-light-200\">\n <Col xs={12}>\n <AlacarteConfirmation\n order={alacarte.placedOrder}\n awaiting={false}\n error={alacarte.placedOrderError}\n onContinue={handleStartCourse}\n productDetail={alacarte.productDetail}\n productTitle={confirmationTitle}\n ownedCourses={\n alacarte.line?.kind === 'program'\n ? (alacarte.line.ownedCourses ?? null)\n : null\n }\n orderHistoryUrl={orderHistoryUrl ?? null}\n placedAt={alacarte.placedOrderPlacedAt}\n />\n </Col>\n </Row>\n </>\n );\n }\n\n if (alacarte.phase === 'loading') {\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout' && REDIRECT_ON_NO_ACTIVE_CHECKOUT) {\n // Redirects home via the effect above — show a spinner rather than a\n // notice while that happens.\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout') {\n return (\n <>\n {alacarteDebug}\n <AlacarteNotice messageId={alacarte.noticeMessageId!} />\n </>\n );\n }\n // Product unavailable / not found / not purchasable — banner only, no checkout form.\n if (alacarte.phase === 'notPurchasable' || alacarte.phase === 'error') {\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <Col xs={12}>\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n data-testid=\"alacarte-error-banner\"\n >\n {alacarte.error || 'Something went wrong with checkout.'}\n </Alert>\n </Col>\n </Row>\n </>\n );\n }\n\n const isAlreadyOwned = alacarte.isAlreadyOwned;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <CountryUnavailableModal\n isOpen={isBillingCountryBlocked}\n homeUrl={homeUrl}\n />\n <Col xs={12} className=\"d-lg-none\">\n <h1 className=\"font-weight-bold text-primary-900\">Checkout</h1>\n </Col>\n\n <Col xs={12} className={isAlreadyOwned ? 'checkout__owned-overlay' : ''}>\n <Row className=\"flex-column-reverse gap-5 flex-lg-row\">\n {/* ── LEFT ── */}\n <Col xs={12} lg={7} xxl={9} className=\"payment__options\">\n <div>\n <h1 className=\"font-weight-bold text-primary-900 d-none d-lg-block mb-4\">\n Checkout\n </h1>\n\n {isAlreadyOwned && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n className=\"checkout__already-owned-alert mb-3\"\n data-testid=\"already-owned-alert\"\n >\n <p className=\"mb-3\">\n {formatMessage(\n alacarte.selector?.kind === 'program'\n ? messages.alreadyPaidProgram\n : messages.alreadyPaidCourse,\n )}\n </p>\n <div className=\"d-flex align-items-center justify-content-end gap-3 flex-wrap\">\n {orderHistoryUrl && (\n <a href={orderHistoryUrl} className=\"text-gray-900 font-weight-bold\">\n {formatMessage(messages.viewOrderHistory)}\n </a>\n )}\n <Button variant=\"brand\" className=\"rounded-pill\" onClick={handleStartCourse}>\n {formatMessage(messages.goToCourse)}\n </Button>\n </div>\n </Alert>\n )}\n\n {c.error && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={c.clearError}\n >\n {c.error}\n </Alert>\n )}\n\n {alacarte.placeOrderError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearPlaceOrderError}\n className=\"mb-3\"\n >\n {alacarte.placeOrderError}\n </Alert>\n )}\n\n {alacarte.paymentDecline?.message && !alacarte.placeOrderError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n className=\"mb-3\"\n data-testid=\"payment-decline-alert\"\n >\n {getLearnerFacingStripeMessage({\n message: alacarte.paymentDecline.message,\n code: alacarte.paymentDecline.code ?? undefined,\n })}\n </Alert>\n )}\n\n {alacarte.couponError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearCouponError}\n className=\"mb-3\"\n data-testid=\"coupon-error-alert\"\n >\n {alacarte.couponError}\n </Alert>\n )}\n\n {!alacarte.priceAccepted && isAlacarteReady && alacarte.line?.amountDue && (\n <Alert variant=\"info\" className=\"mb-3\" data-testid=\"price-accept-notice\">\n {formatMessage(messages.priceChangedNotice, {\n amount: alacarte.line.amountDue.amount.toFixed(2),\n currency: alacarte.line.amountDue.currency,\n })}\n </Alert>\n )}\n\n {showForm && (\n <>\n {showValidationBanner && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {formatMessage(messages.validationBanner)}\n </Alert>\n )}\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {failedMessage\n || formatMessage(messages.paymentProcessingDescription)}\n </Alert>\n )}\n {appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (\n <Alert\n variant=\"success\"\n icon={CheckCircle}\n dismissible\n closeLabel={formatMessage(messages.dismiss)}\n onClose={() => setDismissedCouponAlert(appliedCouponCode)}\n className=\"coupon-applied-alert\"\n data-testid=\"coupon-applied-alert\"\n >\n <span aria-hidden=\"true\">🎉 </span>\n {formatMessage(messages.couponAppliedAlert)}\n </Alert>\n )}\n {alacarte.removedCouponCode && (() => {\n const removedCode = alacarte.removedCouponCode;\n const removedText = formatMessage(\n messages.couponRemovedAlert,\n { code: removedCode },\n );\n // Bold the interpolated code without baking markup into the\n // translation string — find it in the formatted sentence.\n const codeIndex = removedText.indexOf(removedCode);\n return (\n <Alert\n variant=\"success\"\n icon={CheckCircle}\n dismissible\n closeLabel={formatMessage(messages.dismiss)}\n onClose={alacarte.clearRemovedCouponCode}\n className=\"coupon-removed-alert\"\n data-testid=\"coupon-removed-alert\"\n >\n {codeIndex === -1 ? removedText : (\n <>\n {removedText.slice(0, codeIndex)}\n <strong>{removedCode}</strong>\n {removedText.slice(codeIndex + removedCode.length)}\n </>\n )}\n </Alert>\n );\n })()}\n <PaymentForm\n billing={c.billing}\n setField={c.setField}\n numberMountRef={c.numberMountRef}\n expiryMountRef={c.expiryMountRef}\n cvcMountRef={c.cvcMountRef}\n cardError={c.cardError}\n cardFieldStates={c.cardFieldStates}\n stripeReady={c.stripeReady}\n sessionReady={c.sessionReady}\n submitting={\n isAlacarteReady\n ? (alacarte.placingOrder || alacarte.awaitingPlacedOrder)\n : c.processingSubscription\n }\n onPlaceOrder={\n isAlacarteReady\n ? () => {\n alacarte.placeOrder({\n billing: {\n ...c.billing,\n // Country is required on tCheckoutPlaceOrder (two-letter ISO).\n // Prefer the form selection; fall back to gate geolocation.\n country: c.billing.country || c.userCountry || '',\n },\n confirmCardPayment: c.confirmCardPayment,\n });\n }\n : c.placeOrder\n }\n onValidationError={setShowValidationBanner}\n trialEnd={selectedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={null}\n cycleToShow={selectedCycle === 'monthly' ? 'monthly' : 'annual'}\n userCountry={c.userCountry}\n geolocationUrl={props.geolocationUrl}\n alacarteLine={alacarte.line}\n stripe={c.stripe}\n expressCountry={c.billing.country || c.userCountry || 'US'}\n onExpressPayment={isAlacarteReady ? handleExpressPayment : undefined}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n courseLabel={courseLabel}\n onFindCourse={handleStartCourse}\n />\n )}\n\n {c.phase === 'blocked' && (\n <BlockedScreen detail={c.lastComment} />\n )}\n </div>\n </Col>\n\n {/* ── RIGHT — swaps to EnrollmentSummary on success ── */}\n <Col xs={12} lg={5} xxl={3} className=\"payment__summary\">\n {c.phase === 'success' ? (\n <EnrollmentSummary\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n defaultCycle={selectedCycle}\n onCycleChange={c.processingSubscription ? undefined : c.setCycle}\n course={courseForSummary}\n alacarteLine={alacarte.line}\n programDetail={alacarteProgramDetail}\n programName={alacarte.ownedProductName}\n promoCode={isAlacarteReady ? {\n value: promoCodeValue,\n onChange: (next) => {\n setPromoCodeValue(next);\n if (alacarte.couponError) alacarte.clearCouponError();\n },\n onApply: () => alacarte.applyCoupon(promoCodeValue),\n onRemove: () => {\n alacarte.removeCoupon();\n setPromoCodeValue('');\n setAdvertisedOffer(null);\n },\n appliedCode: appliedCouponCode,\n discount: appliedDiscount,\n offer: !appliedCouponCode ? advertisedOffer : null,\n error: alacarte.couponError,\n isApplying: alacarte.couponApplying,\n disabled: alacarte.placingOrder,\n } : null}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n </>\n );\n};\n\nexport default Checkout;\n"]}
|
|
@@ -12,8 +12,8 @@ const messages = defineMessages({
|
|
|
12
12
|
},
|
|
13
13
|
couponAppliedAlert: {
|
|
14
14
|
id: 'checkout.couponAppliedAlert',
|
|
15
|
-
defaultMessage: '
|
|
16
|
-
description: 'Success alert shown above payment methods after a
|
|
15
|
+
defaultMessage: 'Discount applied',
|
|
16
|
+
description: 'Success alert shown above payment methods after a discount is applied',
|
|
17
17
|
},
|
|
18
18
|
couponRemovedAlert: {
|
|
19
19
|
id: 'checkout.couponRemovedAlert',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/Checkout/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,4BAA4B,EAAE;QAC5B,EAAE,EAAE,mDAAmD;QACvD,cAAc,EAAE,oFAAoF;QACpG,WAAW,EAAE,gCAAgC;KAC9C;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,0FAA0F;QAC1G,WAAW,EAAE,2FAA2F;KACzG;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,6BAA6B;QACjC,cAAc,EAAE,
|
|
1
|
+
{"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/Checkout/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,4BAA4B,EAAE;QAC5B,EAAE,EAAE,mDAAmD;QACvD,cAAc,EAAE,oFAAoF;QACpG,WAAW,EAAE,gCAAgC;KAC9C;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,0FAA0F;QAC1G,WAAW,EAAE,2FAA2F;KACzG;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,6BAA6B;QACjC,cAAc,EAAE,kBAAkB;QAClC,WAAW,EAAE,uEAAuE;KACrF;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,6BAA6B;QACjC,cAAc,EAAE,gDAAgD;QAChE,WAAW,EAAE,qEAAqE;KACnF;IACD,OAAO,EAAE;QACP,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,yCAAyC;KACvD;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,2BAA2B;QAC/B,cAAc,EAAE,wDAAwD;QACxE,WAAW,EAAE,mEAAmE;KACjF;IACD,iBAAiB,EAAE;QACjB,EAAE,EAAE,6BAA6B;QACjC,cAAc,EAAE,sEAAsE;QACtF,WAAW,EAAE,sDAAsD;KACpE;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,8BAA8B;QAClC,cAAc,EAAE,uEAAuE;QACvF,WAAW,EAAE,uDAAuD;KACrE;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,uCAAuC;QAC3C,cAAc,EAAE,oBAAoB;QACpC,WAAW,EAAE,iDAAiD;KAC/D;IACD,UAAU,EAAE;QACV,EAAE,EAAE,iCAAiC;QACrC,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,4DAA4D;KAC1E;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n paymentProcessingDescription: {\n id: 'checkout.paymentForm.paymentProcessingdescription',\n defaultMessage: 'Your payment could not be processed. Please check your card details and try again.',\n description: 'Payment processing description',\n },\n priceChangedNotice: {\n id: 'checkout.alacarte.priceChangedNotice',\n defaultMessage: 'The price is now ${amount} {currency}. Click Pay now to accept this amount and continue.',\n description: 'Shown when checkout.priceAccepted is false so the learner confirms the current amount due',\n },\n couponAppliedAlert: {\n id: 'checkout.couponAppliedAlert',\n defaultMessage: 'Discount applied',\n description: 'Success alert shown above payment methods after a discount is applied',\n },\n couponRemovedAlert: {\n id: 'checkout.couponRemovedAlert',\n defaultMessage: 'Coupon code {code} was removed from your cart.',\n description: 'Success alert shown above payment methods after a coupon is removed',\n },\n dismiss: {\n id: 'checkout.couponAppliedAlert.dismiss',\n defaultMessage: 'Dismiss',\n description: 'Closes the coupon-applied success alert',\n },\n validationBanner: {\n id: 'checkout.validationBanner',\n defaultMessage: 'Please complete the required fields highlighted below.',\n description: 'Error banner shown when required payment fields are not filled in',\n },\n alreadyPaidCourse: {\n id: 'checkout.alreadyPaid.course',\n defaultMessage: \"You've already paid for this course. You haven't been charged again.\",\n description: 'Alert shown when the learner already owns the course',\n },\n alreadyPaidProgram: {\n id: 'checkout.alreadyPaid.program',\n defaultMessage: \"You've already paid for this program. You haven't been charged again.\",\n description: 'Alert shown when the learner already owns the program',\n },\n viewOrderHistory: {\n id: 'checkout.alreadyPaid.viewOrderHistory',\n defaultMessage: 'View order history',\n description: 'Link to order history on the already-paid alert',\n },\n goToCourse: {\n id: 'checkout.alreadyPaid.goToCourse',\n defaultMessage: 'Go to course',\n description: 'Button to navigate to the course on the already-paid alert',\n },\n});\n\nexport default messages;\n"]}
|
|
@@ -96,6 +96,8 @@ const ExpressCheckout = ({ stripe, totalAmountCents, currency, onPaymentAuthoriz
|
|
|
96
96
|
return;
|
|
97
97
|
}
|
|
98
98
|
const walletLabel = formatMessage(slot === 'apple' ? messages.applePay : messages.googlePay);
|
|
99
|
+
let paymentMethodId;
|
|
100
|
+
let walletCountry;
|
|
99
101
|
try {
|
|
100
102
|
const { error: submitError } = yield elements.submit();
|
|
101
103
|
if (submitError) {
|
|
@@ -113,14 +115,24 @@ const ExpressCheckout = ({ stripe, totalAmountCents, currency, onPaymentAuthoriz
|
|
|
113
115
|
event.paymentFailed({ reason: 'fail' });
|
|
114
116
|
return;
|
|
115
117
|
}
|
|
116
|
-
|
|
118
|
+
paymentMethodId = paymentMethod.id;
|
|
119
|
+
walletCountry = ((_b = (_a = event.billingDetails) === null || _a === void 0 ? void 0 : _a.address) === null || _b === void 0 ? void 0 : _b.country)
|
|
117
120
|
|| ((_d = (_c = paymentMethod.billing_details) === null || _c === void 0 ? void 0 : _c.address) === null || _d === void 0 ? void 0 : _d.country);
|
|
118
|
-
yield onPaymentAuthorizedRef.current(paymentMethod.id, walletCountry);
|
|
119
121
|
}
|
|
120
122
|
catch (err) {
|
|
121
|
-
console.warn(`[ExpressCheckout]
|
|
123
|
+
console.warn(`[ExpressCheckout] wallet setup failed (${slot})`, err);
|
|
122
124
|
setWalletError(formatMessage(messages.walletUnavailable, { wallet: walletLabel }));
|
|
123
125
|
event.paymentFailed({ reason: 'fail' });
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
try {
|
|
129
|
+
yield onPaymentAuthorizedRef.current(paymentMethodId, walletCountry);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
// The wallet worked; the payment itself failed (e.g. card declined).
|
|
133
|
+
// The checkout page already shows that error, so don't blame the wallet.
|
|
134
|
+
console.warn(`[ExpressCheckout] payment failed (${slot})`, err);
|
|
135
|
+
event.paymentFailed({ reason: 'fail' });
|
|
124
136
|
}
|
|
125
137
|
}));
|
|
126
138
|
expressElement.mount(mountNode);
|