@edx/frontend-app-alacarte-checkout 1.13.0 → 1.14.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.
Files changed (48) hide show
  1. package/dist/components/AlacarteConfirmation/AlacarteConfirmation.d.ts +4 -2
  2. package/dist/components/AlacarteConfirmation/AlacarteConfirmation.js +18 -7
  3. package/dist/components/AlacarteConfirmation/AlacarteConfirmation.js.map +1 -1
  4. package/dist/components/AlacarteConfirmation/Messages.d.ts +5 -0
  5. package/dist/components/AlacarteConfirmation/Messages.js +6 -1
  6. package/dist/components/AlacarteConfirmation/Messages.js.map +1 -1
  7. package/dist/components/CartSummary/CartSummary.js +17 -16
  8. package/dist/components/CartSummary/CartSummary.js.map +1 -1
  9. package/dist/components/CartSummary/Messages.js +1 -1
  10. package/dist/components/CartSummary/Messages.js.map +1 -1
  11. package/dist/components/Checkout/Checkout.js +24 -15
  12. package/dist/components/Checkout/Checkout.js.map +1 -1
  13. package/dist/components/PaymentForm/PaymentForm.js +17 -2
  14. package/dist/components/PaymentForm/PaymentForm.js.map +1 -1
  15. package/dist/components/ProgramEnrollmentCard/Messages.d.ts +5 -0
  16. package/dist/components/ProgramEnrollmentCard/Messages.js +6 -1
  17. package/dist/components/ProgramEnrollmentCard/Messages.js.map +1 -1
  18. package/dist/components/ProgramEnrollmentCard/ProgramEnrollmentCard.d.ts +4 -2
  19. package/dist/components/ProgramEnrollmentCard/ProgramEnrollmentCard.js +42 -10
  20. package/dist/components/ProgramEnrollmentCard/ProgramEnrollmentCard.js.map +1 -1
  21. package/dist/data/hooks/useAlacarteCheckout.d.ts +3 -1
  22. package/dist/data/hooks/useAlacarteCheckout.js +79 -21
  23. package/dist/data/hooks/useAlacarteCheckout.js.map +1 -1
  24. package/dist/data/hooks/useAlacarteQueries.d.ts +6 -2
  25. package/dist/data/hooks/useAlacarteQueries.js +10 -6
  26. package/dist/data/hooks/useAlacarteQueries.js.map +1 -1
  27. package/dist/data/hooks/useCheckout.js +2 -1
  28. package/dist/data/hooks/useCheckout.js.map +1 -1
  29. package/dist/styles/components/alacarteConfirmation.scss +12 -3
  30. package/dist/styles/components/programEnrollmentCard.scss +6 -1
  31. package/dist/types/alacarte.d.ts +32 -2
  32. package/dist/types/alacarte.js.map +1 -1
  33. package/dist/types.d.ts +1 -0
  34. package/dist/types.js.map +1 -1
  35. package/dist/utils/alacarteApi.js +7 -5
  36. package/dist/utils/alacarteApi.js.map +1 -1
  37. package/dist/utils/alacarteQueries.d.ts +2 -2
  38. package/dist/utils/alacarteQueries.js +3 -0
  39. package/dist/utils/alacarteQueries.js.map +1 -1
  40. package/dist/utils/couponErrors.d.ts +11 -0
  41. package/dist/utils/couponErrors.js +18 -0
  42. package/dist/utils/couponErrors.js.map +1 -1
  43. package/dist/utils/provision.js +2 -2
  44. package/dist/utils/provision.js.map +1 -1
  45. package/dist/utils/stripeCard.d.ts +12 -1
  46. package/dist/utils/stripeCard.js +55 -3
  47. package/dist/utils/stripeCard.js.map +1 -1
  48. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import type { PlacedOrder, ProductDetail } from '../../types/alacarte';
1
+ import type { AlacarteProgramOwnedCourse, PlacedOrder, ProductDetail } from '../../types/alacarte';
2
2
  export interface AlacarteConfirmationProps {
3
3
  order: PlacedOrder | null;
4
4
  awaiting: boolean;
@@ -8,6 +8,8 @@ export interface AlacarteConfirmationProps {
8
8
  productDetail?: ProductDetail | null;
9
9
  /** Display title from the checkout line (course or program name). */
10
10
  productTitle?: string | null;
11
+ /** Program member courses already owned before this order. */
12
+ ownedCourses?: AlacarteProgramOwnedCourse[] | null;
11
13
  /** Learner order-history / dashboard URL for the secondary CTA. */
12
14
  orderHistoryUrl?: string | null;
13
15
  /** ISO timestamp the order was placed (tPlacedAt, falling back to
@@ -15,5 +17,5 @@ export interface AlacarteConfirmationProps {
15
17
  * (placedAt + 14 days); the refund line is hidden once that date passes. */
16
18
  placedAt?: string | null;
17
19
  }
18
- declare const AlacarteConfirmation: ({ order, awaiting, error, onContinue, productDetail, productTitle, orderHistoryUrl, placedAt, }: AlacarteConfirmationProps) => import("react/jsx-runtime").JSX.Element | null;
20
+ declare const AlacarteConfirmation: ({ order, awaiting, error, onContinue, productDetail, productTitle, ownedCourses, orderHistoryUrl, placedAt, }: AlacarteConfirmationProps) => import("react/jsx-runtime").JSX.Element | null;
19
21
  export default AlacarteConfirmation;
@@ -5,6 +5,9 @@ import { Error as ErrorIcon, Monitor } from '@openedx/paragon/icons';
5
5
  import { useIntl } from '@openedx/frontend-base';
6
6
  import { ProcessingScreen } from '../StatusScreens/StatusScreens';
7
7
  import messages from './Messages';
8
+ function normalizeName(name) {
9
+ return name.trim().toLowerCase().replace(/\s+/g, ' ');
10
+ }
8
11
  function isProgramOrder(order) {
9
12
  return order.lines.some((line) => line.kind === 'program' || !!line.programId);
10
13
  }
@@ -20,8 +23,8 @@ function addDays(from, days) {
20
23
  result.setDate(result.getDate() + days);
21
24
  return result;
22
25
  }
23
- const AlacarteConfirmation = ({ order, awaiting, error, onContinue, productDetail = null, productTitle = null, orderHistoryUrl = null, placedAt = null, }) => {
24
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t;
26
+ const AlacarteConfirmation = ({ order, awaiting, error, onContinue, productDetail = null, productTitle = null, ownedCourses = null, orderHistoryUrl = null, placedAt = null, }) => {
27
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
25
28
  const { formatMessage, locale = 'en-US' } = useIntl();
26
29
  const placedAtDate = useMemo(() => {
27
30
  const parsed = placedAt ? new Date(placedAt) : null;
@@ -62,21 +65,29 @@ const AlacarteConfirmation = ({ order, awaiting, error, onContinue, productDetai
62
65
  || ((_h = (_g = programDetail === null || programDetail === void 0 ? void 0 : programDetail.tCourses) === null || _g === void 0 ? void 0 : _g[0]) === null || _h === void 0 ? void 0 : _h.imageUrl)
63
66
  || null;
64
67
  const currency = (_k = (_j = order.totals) === null || _j === void 0 ? void 0 : _j.currency) !== null && _k !== void 0 ? _k : 'USD';
65
- const subtotal = (_m = (_l = order.totals) === null || _l === void 0 ? void 0 : _l.price) !== null && _m !== void 0 ? _m : 0;
66
- const discount = ((_p = (_o = order.totals) === null || _o === void 0 ? void 0 : _o.discount) !== null && _p !== void 0 ? _p : 0) + ((_r = (_q = order.totals) === null || _q === void 0 ? void 0 : _q.deduction) !== null && _r !== void 0 ? _r : 0);
68
+ // Subtotal is price less owned-course deduction (before coupon/offer discount).
69
+ const subtotal = Math.max(0, ((_m = (_l = order.totals) === null || _l === void 0 ? void 0 : _l.price) !== null && _m !== void 0 ? _m : 0) - ((_p = (_o = order.totals) === null || _o === void 0 ? void 0 : _o.deduction) !== null && _p !== void 0 ? _p : 0));
70
+ const discount = (_r = (_q = order.totals) === null || _q === void 0 ? void 0 : _q.discount) !== null && _r !== void 0 ? _r : 0;
67
71
  const paid = (_t = (_s = order.totals) === null || _s === void 0 ? void 0 : _s.paid) !== null && _t !== void 0 ? _t : 0;
68
72
  const money = (amount) => formatMessage(messages.moneyAmount, {
69
73
  amount: amount.toFixed(2),
70
74
  currency,
71
75
  });
72
- const programCourses = programOrder
73
- ? order.lines.map((line) => line.name).filter(Boolean)
76
+ const purchasedCourseNames = programOrder
77
+ ? order.lines.map((line) => { var _a; return (_a = line.name) === null || _a === void 0 ? void 0 : _a.trim(); }).filter((name) => !!name)
74
78
  : [];
79
+ const purchasedNameSet = new Set(purchasedCourseNames.map(normalizeName));
80
+ const previouslyPurchased = (ownedCourses !== null && ownedCourses !== void 0 ? ownedCourses : [])
81
+ .map((c) => { var _a; return (_a = c.name) === null || _a === void 0 ? void 0 : _a.trim(); })
82
+ .filter((name) => !!name && !purchasedNameSet.has(normalizeName(name)));
83
+ const programCourseTotal = (_v = (_u = programDetail === null || programDetail === void 0 ? void 0 : programDetail.tCourses) === null || _u === void 0 ? void 0 : _u.length) !== null && _v !== void 0 ? _v : (purchasedCourseNames.length + previouslyPurchased.length);
75
84
  const summaryCard = (_jsxs("aside", { className: "alacarte-confirmation__summary", "aria-label": formatMessage(messages.purchaseSummary), children: [_jsx("h2", { className: "alacarte-confirmation__summary-title font-weight-bold mb-0", children: formatMessage(messages.purchaseSummary) }), _jsx("hr", { className: "alacarte-confirmation__summary-divider" }), _jsxs("div", { className: "alacarte-confirmation__product d-flex align-items-start", children: [orgLogo ? (_jsx("img", { src: orgLogo, alt: "", className: "alacarte-confirmation__org-logo" })) : (_jsx("div", { className: "alacarte-confirmation__org-logo alacarte-confirmation__org-logo--placeholder", "aria-hidden": true })), _jsxs("div", { children: [_jsxs("span", { className: "alacarte-confirmation__badge", children: [_jsx(Icon, { src: Monitor, className: "alacarte-confirmation__badge-icon" }), formatMessage(messages.verifiedCertificate)] }), _jsx("p", { className: "alacarte-confirmation__product-name font-weight-bold mb-0", children: productLabel })] })] }), _jsx("hr", { className: "alacarte-confirmation__summary-divider" }), _jsxs("div", { className: "alacarte-confirmation__price-row", children: [_jsx("span", { children: formatMessage(messages.subtotal) }), _jsx("span", { children: money(subtotal) })] }), discount > 0 && (_jsxs("div", { className: "alacarte-confirmation__price-row alacarte-confirmation__price-row--discount", children: [_jsx("span", { children: formatMessage(messages.discountsApplied) }), _jsx("span", { children: formatMessage(messages.discountAmount, {
76
85
  amount: discount.toFixed(2),
77
86
  currency,
78
87
  }) })] })), _jsxs("div", { className: "alacarte-confirmation__price-row alacarte-confirmation__price-row--paid", children: [_jsx("span", { children: formatMessage(messages.amountPaid) }), _jsx("span", { children: money(paid) })] })] }));
79
- return (_jsx("div", { className: "alacarte-confirmation", "data-testid": "alacarte-confirmation", children: _jsxs(Row, { className: "alacarte-confirmation__row", children: [_jsxs(Col, { xs: 12, lg: 7, className: "alacarte-confirmation__main", children: [_jsx("h1", { className: "alacarte-confirmation__headline font-weight-bold mb-4", children: formatMessage(messages.headline) }), _jsx("p", { className: "alacarte-confirmation__subheadline mb-4", children: formatMessage(programOrder ? messages.subheadlineProgram : messages.subheadlineCourse, { productLabel }) }), _jsxs("div", { className: "alacarte-confirmation__ctas d-flex align-items-center flex-wrap mb-4.5", children: [_jsx(Button, { variant: "brand", className: "alacarte-confirmation__cta-primary", onClick: onContinue, children: formatMessage(programOrder ? messages.startProgram : messages.startCourse) }), orderHistoryUrl && (_jsx("a", { href: orderHistoryUrl, className: "alacarte-confirmation__order-history", children: formatMessage(messages.viewOrderHistory) }))] }), _jsx("div", { className: "alacarte-confirmation__summary-mobile d-lg-none", children: summaryCard }), programOrder && programCourses.length > 0 && (_jsxs("div", { className: "alacarte-confirmation__about-program mb-5", children: [_jsx("h2", { className: "alacarte-confirmation__about-title", children: formatMessage(messages.aboutProgram) }), _jsx("p", { className: "alacarte-confirmation__courses-included", children: formatMessage(messages.coursesIncluded, { count: programCourses.length }) }), _jsx("ul", { className: "alacarte-confirmation__course-list mb-0", children: programCourses.map((name) => (_jsx("li", { children: name }, name))) })] })), _jsxs(Row, { children: [_jsxs(Col, { xs: 12, md: 7, className: "mb-4", children: [_jsx("h2", { className: "h5 font-weight-bold mb-3", children: formatMessage(messages.whatsIncluded) }), _jsxs("ul", { className: "alacarte-confirmation__benefits", children: [_jsx("li", { children: formatMessage(messages.benefit1) }), _jsx("li", { children: formatMessage(messages.benefit2) }), _jsx("li", { children: formatMessage(messages.benefit3) })] })] }), _jsxs(Col, { xs: 12, md: 5, className: "mb-4", children: [_jsxs("div", { className: "mb-3", children: [_jsx("p", { className: "font-weight-bold mb-0", children: formatMessage(messages.orderNumber) }), _jsx("p", { className: "text-muted mb-0", children: order.orderNumber })] }), _jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-0", children: formatMessage(messages.purchaseDate) }), _jsx("p", { className: "text-muted mb-0", children: purchaseDate !== null && purchaseDate !== void 0 ? purchaseDate : '' })] })] })] }), refundWindowOpen && refundBy && (_jsxs(_Fragment, { children: [_jsx("hr", { className: "alacarte-confirmation__divider" }), _jsx("p", { className: "alacarte-confirmation__refund text-muted small mb-0", children: formatMessage(messages.refundNotice, { date: refundBy }) })] }))] }), _jsx(Col, { xs: 12, lg: 5, className: "alacarte-confirmation__summary-col d-none d-lg-block", children: summaryCard })] }) }));
88
+ return (_jsx("div", { className: "alacarte-confirmation", "data-testid": "alacarte-confirmation", children: _jsxs(Row, { className: "alacarte-confirmation__row", children: [_jsxs(Col, { xs: 12, lg: 7, className: "alacarte-confirmation__main", children: [_jsx("h1", { className: "alacarte-confirmation__headline font-weight-bold mb-4", children: formatMessage(messages.headline) }), _jsx("p", { className: "alacarte-confirmation__subheadline mb-4", children: formatMessage(programOrder ? messages.subheadlineProgram : messages.subheadlineCourse, { productLabel }) }), _jsxs("div", { className: "alacarte-confirmation__ctas d-flex align-items-center flex-wrap mb-4.5", children: [_jsx(Button, { variant: "brand", className: "alacarte-confirmation__cta-primary", onClick: onContinue, children: formatMessage(programOrder ? messages.startProgram : messages.startCourse) }), orderHistoryUrl && (_jsx("a", { href: orderHistoryUrl, className: "alacarte-confirmation__order-history", children: formatMessage(messages.viewOrderHistory) }))] }), _jsx("div", { className: "alacarte-confirmation__summary-mobile d-lg-none", children: summaryCard }), programOrder && (purchasedCourseNames.length > 0 || previouslyPurchased.length > 0) && (_jsxs("div", { className: "alacarte-confirmation__about-program mb-5", children: [_jsx("h2", { className: "alacarte-confirmation__about-title", children: formatMessage(messages.aboutProgram) }), purchasedCourseNames.length > 0 && (_jsxs(_Fragment, { children: [_jsx("p", { className: "alacarte-confirmation__courses-included", children: formatMessage(messages.coursesIncluded, { count: programCourseTotal }) }), _jsx("ul", { className: "alacarte-confirmation__course-list mb-0", children: purchasedCourseNames.map((name) => (_jsx("li", { children: name }, name))) })] })), previouslyPurchased.length > 0 && (_jsxs("div", { className: "alacarte-confirmation__previously-purchased", "data-testid": "confirmation-previously-purchased", children: [_jsx("p", { className: "alacarte-confirmation__previously-purchased-heading", children: formatMessage(messages.previouslyPurchased, {
89
+ count: previouslyPurchased.length,
90
+ }) }), _jsx("ul", { className: "alacarte-confirmation__course-list mb-0", children: previouslyPurchased.map((name) => (_jsx("li", { children: name }, name))) })] }))] })), _jsxs(Row, { children: [_jsxs(Col, { xs: 12, md: 7, className: "mb-4", children: [_jsx("h2", { className: "h5 font-weight-bold mb-3", children: formatMessage(messages.whatsIncluded) }), _jsxs("ul", { className: "alacarte-confirmation__benefits", children: [_jsx("li", { children: formatMessage(messages.benefit1) }), _jsx("li", { children: formatMessage(messages.benefit2) }), _jsx("li", { children: formatMessage(messages.benefit3) })] })] }), _jsxs(Col, { xs: 12, md: 5, className: "mb-4", children: [_jsxs("div", { className: "mb-3", children: [_jsx("p", { className: "font-weight-bold mb-0", children: formatMessage(messages.orderNumber) }), _jsx("p", { className: "text-muted mb-0", children: order.orderNumber })] }), _jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-0", children: formatMessage(messages.purchaseDate) }), _jsx("p", { className: "text-muted mb-0", children: purchaseDate !== null && purchaseDate !== void 0 ? purchaseDate : '' })] })] })] }), refundWindowOpen && refundBy && (_jsxs(_Fragment, { children: [_jsx("hr", { className: "alacarte-confirmation__divider" }), _jsx("p", { className: "alacarte-confirmation__refund text-muted small mb-0", children: formatMessage(messages.refundNotice, { date: refundBy }) })] }))] }), _jsx(Col, { xs: 12, lg: 5, className: "alacarte-confirmation__summary-col d-none d-lg-block", children: summaryCard })] }) }));
80
91
  };
81
92
  export default AlacarteConfirmation;
82
93
  //# sourceMappingURL=AlacarteConfirmation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AlacarteConfirmation.js","sourceRoot":"","sources":["../../../src/components/AlacarteConfirmation/AlacarteConfirmation.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useMemo } from 'react';\nimport { Alert, Button, Col, Icon, Row } from '@openedx/paragon';\nimport { Error as ErrorIcon, Monitor } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport { ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { CourseDetail } from '../../types';\nimport type { PlacedOrder, ProductDetail, ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\n\nexport interface AlacarteConfirmationProps {\n order: PlacedOrder | null,\n awaiting: boolean,\n error: string | null,\n onContinue?: () => void,\n /** Optional catalog detail from checkout — logo / brand. */\n productDetail?: ProductDetail | null,\n /** Display title from the checkout line (course or program name). */\n productTitle?: string | null,\n /** Learner order-history / dashboard URL for the secondary CTA. */\n orderHistoryUrl?: string | null,\n /** ISO timestamp the order was placed (tPlacedAt, falling back to\n * biCreatedAt). Drives both the purchase date and the refund deadline\n * (placedAt + 14 days); the refund line is hidden once that date passes. */\n placedAt?: string | null,\n}\n\nfunction isProgramOrder(order: PlacedOrder): boolean {\n return order.lines.some((line) => line.kind === 'program' || !!line.programId);\n}\n\nfunction formatPurchaseDate(locale: string, date: Date): string {\n return new Intl.DateTimeFormat(locale, {\n month: 'long',\n day: 'numeric',\n year: 'numeric',\n }).format(date);\n}\n\nfunction addDays(from: Date, days: number): Date {\n const result = new Date(from);\n result.setDate(result.getDate() + days);\n return result;\n}\n\nconst AlacarteConfirmation = ({\n order,\n awaiting,\n error,\n onContinue,\n productDetail = null,\n productTitle = null,\n orderHistoryUrl = null,\n placedAt = null,\n}: AlacarteConfirmationProps) => {\n const { formatMessage, locale = 'en-US' } = useIntl();\n\n const placedAtDate = useMemo(() => {\n const parsed = placedAt ? new Date(placedAt) : null;\n return parsed && !Number.isNaN(parsed.getTime()) ? parsed : null;\n }, [placedAt]);\n\n const purchaseDate = useMemo(\n () => (placedAtDate ? formatPurchaseDate(locale, placedAtDate) : null),\n [locale, placedAtDate],\n );\n const refundDeadlineDate = useMemo(\n () => (placedAtDate ? addDays(placedAtDate, 14) : null),\n [placedAtDate],\n );\n const refundBy = useMemo(\n () => (refundDeadlineDate ? formatPurchaseDate(locale, refundDeadlineDate) : null),\n [locale, refundDeadlineDate],\n );\n const refundWindowOpen = !!refundDeadlineDate && refundDeadlineDate.getTime() >= Date.now();\n\n if (awaiting && !order) {\n return <ProcessingScreen />;\n }\n\n if (error && !order) {\n return (\n <Alert variant=\"danger\" icon={ErrorIcon}>\n {formatMessage(messages.orderLoadError, { error })}\n </Alert>\n );\n }\n\n if (!order) {\n return null;\n }\n\n const programOrder = isProgramOrder(order);\n const courseDetail = productDetail && 'maId' in productDetail\n ? (productDetail as CourseDetail)\n : null;\n const programDetail = productDetail && 'tCourses' in productDetail\n ? (productDetail as ProgramDetail)\n : null;\n\n const firstLine = order.lines[0];\n const brand = courseDetail?.tBrandText?.trim()\n || programDetail?.tCourses?.[0]?.tBrandText?.trim()\n || '';\n const baseName = productTitle\n || firstLine?.name\n || (programOrder ? 'your program' : 'your course');\n\n const productLabel = !programOrder && brand && !productTitle?.includes(brand)\n ? `${brand}: ${baseName}`\n : baseName;\n\n const orgLogo = courseDetail?.imageUrl\n || programDetail?.tCourses?.[0]?.tOrgLogoUrl\n || programDetail?.tCourses?.[0]?.imageUrl\n || null;\n\n const currency = order.totals?.currency ?? 'USD';\n const subtotal = order.totals?.price ?? 0;\n const discount = (order.totals?.discount ?? 0) + (order.totals?.deduction ?? 0);\n const paid = order.totals?.paid ?? 0;\n\n const money = (amount: number) => formatMessage(messages.moneyAmount, {\n amount: amount.toFixed(2),\n currency,\n });\n\n const programCourses = programOrder\n ? order.lines.map((line) => line.name).filter(Boolean)\n : [];\n\n const summaryCard = (\n <aside\n className=\"alacarte-confirmation__summary\"\n aria-label={formatMessage(messages.purchaseSummary)}\n >\n <h2 className=\"alacarte-confirmation__summary-title font-weight-bold mb-0\">\n {formatMessage(messages.purchaseSummary)}\n </h2>\n <hr className=\"alacarte-confirmation__summary-divider\" />\n\n <div className=\"alacarte-confirmation__product d-flex align-items-start\">\n {orgLogo ? (\n <img\n src={orgLogo}\n alt=\"\"\n className=\"alacarte-confirmation__org-logo\"\n />\n ) : (\n <div\n className=\"alacarte-confirmation__org-logo alacarte-confirmation__org-logo--placeholder\"\n aria-hidden\n />\n )}\n <div>\n <span className=\"alacarte-confirmation__badge\">\n <Icon src={Monitor} className=\"alacarte-confirmation__badge-icon\" />\n {formatMessage(messages.verifiedCertificate)}\n </span>\n <p className=\"alacarte-confirmation__product-name font-weight-bold mb-0\">\n {productLabel}\n </p>\n </div>\n </div>\n\n <hr className=\"alacarte-confirmation__summary-divider\" />\n\n <div className=\"alacarte-confirmation__price-row\">\n <span>{formatMessage(messages.subtotal)}</span>\n <span>{money(subtotal)}</span>\n </div>\n {discount > 0 && (\n <div className=\"alacarte-confirmation__price-row alacarte-confirmation__price-row--discount\">\n <span>{formatMessage(messages.discountsApplied)}</span>\n <span>\n {formatMessage(messages.discountAmount, {\n amount: discount.toFixed(2),\n currency,\n })}\n </span>\n </div>\n )}\n <div className=\"alacarte-confirmation__price-row alacarte-confirmation__price-row--paid\">\n <span>{formatMessage(messages.amountPaid)}</span>\n <span>{money(paid)}</span>\n </div>\n </aside>\n );\n\n return (\n <div className=\"alacarte-confirmation\" data-testid=\"alacarte-confirmation\">\n <Row className=\"alacarte-confirmation__row\">\n <Col xs={12} lg={7} className=\"alacarte-confirmation__main\">\n <h1 className=\"alacarte-confirmation__headline font-weight-bold mb-4\">\n {formatMessage(messages.headline)}\n </h1>\n <p className=\"alacarte-confirmation__subheadline mb-4\">\n {formatMessage(\n programOrder ? messages.subheadlineProgram : messages.subheadlineCourse,\n { productLabel },\n )}\n </p>\n\n <div className=\"alacarte-confirmation__ctas d-flex align-items-center flex-wrap mb-4.5\">\n <Button\n variant=\"brand\"\n className=\"alacarte-confirmation__cta-primary\"\n onClick={onContinue}\n >\n {formatMessage(programOrder ? messages.startProgram : messages.startCourse)}\n </Button>\n {orderHistoryUrl && (\n <a href={orderHistoryUrl} className=\"alacarte-confirmation__order-history\">\n {formatMessage(messages.viewOrderHistory)}\n </a>\n )}\n </div>\n\n <div className=\"alacarte-confirmation__summary-mobile d-lg-none\">\n {summaryCard}\n </div>\n\n {programOrder && programCourses.length > 0 && (\n <div className=\"alacarte-confirmation__about-program mb-5\">\n <h2 className=\"alacarte-confirmation__about-title\">\n {formatMessage(messages.aboutProgram)}\n </h2>\n <p className=\"alacarte-confirmation__courses-included\">\n {formatMessage(messages.coursesIncluded, { count: programCourses.length })}\n </p>\n <ul className=\"alacarte-confirmation__course-list mb-0\">\n {programCourses.map((name) => (\n <li key={name}>{name}</li>\n ))}\n </ul>\n </div>\n )}\n\n <Row>\n <Col xs={12} md={7} className=\"mb-4\">\n <h2 className=\"h5 font-weight-bold mb-3\">\n {formatMessage(messages.whatsIncluded)}\n </h2>\n <ul className=\"alacarte-confirmation__benefits\">\n <li>{formatMessage(messages.benefit1)}</li>\n <li>{formatMessage(messages.benefit2)}</li>\n <li>{formatMessage(messages.benefit3)}</li>\n </ul>\n </Col>\n <Col xs={12} md={5} className=\"mb-4\">\n <div className=\"mb-3\">\n <p className=\"font-weight-bold mb-0\">\n {formatMessage(messages.orderNumber)}\n </p>\n <p className=\"text-muted mb-0\">{order.orderNumber}</p>\n </div>\n <div>\n <p className=\"font-weight-bold mb-0\">\n {formatMessage(messages.purchaseDate)}\n </p>\n <p className=\"text-muted mb-0\">{purchaseDate ?? ''}</p>\n </div>\n </Col>\n </Row>\n\n {refundWindowOpen && refundBy && (\n <>\n <hr className=\"alacarte-confirmation__divider\" />\n <p className=\"alacarte-confirmation__refund text-muted small mb-0\">\n {formatMessage(messages.refundNotice, { date: refundBy })}\n </p>\n </>\n )}\n </Col>\n\n <Col xs={12} lg={5} className=\"alacarte-confirmation__summary-col d-none d-lg-block\">\n {summaryCard}\n </Col>\n </Row>\n </div>\n );\n};\n\nexport default AlacarteConfirmation;\n"]}
1
+ {"version":3,"file":"AlacarteConfirmation.js","sourceRoot":"","sources":["../../../src/components/AlacarteConfirmation/AlacarteConfirmation.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useMemo } from 'react';\nimport { Alert, Button, Col, Icon, Row } from '@openedx/paragon';\nimport { Error as ErrorIcon, Monitor } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport { ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { CourseDetail } from '../../types';\nimport type {\n AlacarteProgramOwnedCourse, PlacedOrder, ProductDetail, ProgramDetail,\n} from '../../types/alacarte';\nimport messages from './Messages';\n\nexport interface AlacarteConfirmationProps {\n order: PlacedOrder | null,\n awaiting: boolean,\n error: string | null,\n onContinue?: () => void,\n /** Optional catalog detail from checkout — logo / brand. */\n productDetail?: ProductDetail | null,\n /** Display title from the checkout line (course or program name). */\n productTitle?: string | null,\n /** Program member courses already owned before this order. */\n ownedCourses?: AlacarteProgramOwnedCourse[] | null,\n /** Learner order-history / dashboard URL for the secondary CTA. */\n orderHistoryUrl?: string | null,\n /** ISO timestamp the order was placed (tPlacedAt, falling back to\n * biCreatedAt). Drives both the purchase date and the refund deadline\n * (placedAt + 14 days); the refund line is hidden once that date passes. */\n placedAt?: string | null,\n}\n\nfunction normalizeName(name: string): string {\n return name.trim().toLowerCase().replace(/\\s+/g, ' ');\n}\n\nfunction isProgramOrder(order: PlacedOrder): boolean {\n return order.lines.some((line) => line.kind === 'program' || !!line.programId);\n}\n\nfunction formatPurchaseDate(locale: string, date: Date): string {\n return new Intl.DateTimeFormat(locale, {\n month: 'long',\n day: 'numeric',\n year: 'numeric',\n }).format(date);\n}\n\nfunction addDays(from: Date, days: number): Date {\n const result = new Date(from);\n result.setDate(result.getDate() + days);\n return result;\n}\n\nconst AlacarteConfirmation = ({\n order,\n awaiting,\n error,\n onContinue,\n productDetail = null,\n productTitle = null,\n ownedCourses = null,\n orderHistoryUrl = null,\n placedAt = null,\n}: AlacarteConfirmationProps) => {\n const { formatMessage, locale = 'en-US' } = useIntl();\n\n const placedAtDate = useMemo(() => {\n const parsed = placedAt ? new Date(placedAt) : null;\n return parsed && !Number.isNaN(parsed.getTime()) ? parsed : null;\n }, [placedAt]);\n\n const purchaseDate = useMemo(\n () => (placedAtDate ? formatPurchaseDate(locale, placedAtDate) : null),\n [locale, placedAtDate],\n );\n const refundDeadlineDate = useMemo(\n () => (placedAtDate ? addDays(placedAtDate, 14) : null),\n [placedAtDate],\n );\n const refundBy = useMemo(\n () => (refundDeadlineDate ? formatPurchaseDate(locale, refundDeadlineDate) : null),\n [locale, refundDeadlineDate],\n );\n const refundWindowOpen = !!refundDeadlineDate && refundDeadlineDate.getTime() >= Date.now();\n\n if (awaiting && !order) {\n return <ProcessingScreen />;\n }\n\n if (error && !order) {\n return (\n <Alert variant=\"danger\" icon={ErrorIcon}>\n {formatMessage(messages.orderLoadError, { error })}\n </Alert>\n );\n }\n\n if (!order) {\n return null;\n }\n\n const programOrder = isProgramOrder(order);\n const courseDetail = productDetail && 'maId' in productDetail\n ? (productDetail as CourseDetail)\n : null;\n const programDetail = productDetail && 'tCourses' in productDetail\n ? (productDetail as ProgramDetail)\n : null;\n\n const firstLine = order.lines[0];\n const brand = courseDetail?.tBrandText?.trim()\n || programDetail?.tCourses?.[0]?.tBrandText?.trim()\n || '';\n const baseName = productTitle\n || firstLine?.name\n || (programOrder ? 'your program' : 'your course');\n\n const productLabel = !programOrder && brand && !productTitle?.includes(brand)\n ? `${brand}: ${baseName}`\n : baseName;\n\n const orgLogo = courseDetail?.imageUrl\n || programDetail?.tCourses?.[0]?.tOrgLogoUrl\n || programDetail?.tCourses?.[0]?.imageUrl\n || null;\n\n const currency = order.totals?.currency ?? 'USD';\n // Subtotal is price less owned-course deduction (before coupon/offer discount).\n const subtotal = Math.max(\n 0,\n (order.totals?.price ?? 0) - (order.totals?.deduction ?? 0),\n );\n const discount = order.totals?.discount ?? 0;\n const paid = order.totals?.paid ?? 0;\n\n const money = (amount: number) => formatMessage(messages.moneyAmount, {\n amount: amount.toFixed(2),\n currency,\n });\n\n const purchasedCourseNames = programOrder\n ? order.lines.map((line) => line.name?.trim()).filter((name): name is string => !!name)\n : [];\n const purchasedNameSet = new Set(purchasedCourseNames.map(normalizeName));\n\n const previouslyPurchased = (ownedCourses ?? [])\n .map((c) => c.name?.trim())\n .filter((name): name is string => !!name && !purchasedNameSet.has(normalizeName(name)));\n\n const programCourseTotal = programDetail?.tCourses?.length\n ?? (purchasedCourseNames.length + previouslyPurchased.length);\n\n const summaryCard = (\n <aside\n className=\"alacarte-confirmation__summary\"\n aria-label={formatMessage(messages.purchaseSummary)}\n >\n <h2 className=\"alacarte-confirmation__summary-title font-weight-bold mb-0\">\n {formatMessage(messages.purchaseSummary)}\n </h2>\n <hr className=\"alacarte-confirmation__summary-divider\" />\n\n <div className=\"alacarte-confirmation__product d-flex align-items-start\">\n {orgLogo ? (\n <img\n src={orgLogo}\n alt=\"\"\n className=\"alacarte-confirmation__org-logo\"\n />\n ) : (\n <div\n className=\"alacarte-confirmation__org-logo alacarte-confirmation__org-logo--placeholder\"\n aria-hidden\n />\n )}\n <div>\n <span className=\"alacarte-confirmation__badge\">\n <Icon src={Monitor} className=\"alacarte-confirmation__badge-icon\" />\n {formatMessage(messages.verifiedCertificate)}\n </span>\n <p className=\"alacarte-confirmation__product-name font-weight-bold mb-0\">\n {productLabel}\n </p>\n </div>\n </div>\n\n <hr className=\"alacarte-confirmation__summary-divider\" />\n\n <div className=\"alacarte-confirmation__price-row\">\n <span>{formatMessage(messages.subtotal)}</span>\n <span>{money(subtotal)}</span>\n </div>\n {discount > 0 && (\n <div className=\"alacarte-confirmation__price-row alacarte-confirmation__price-row--discount\">\n <span>{formatMessage(messages.discountsApplied)}</span>\n <span>\n {formatMessage(messages.discountAmount, {\n amount: discount.toFixed(2),\n currency,\n })}\n </span>\n </div>\n )}\n <div className=\"alacarte-confirmation__price-row alacarte-confirmation__price-row--paid\">\n <span>{formatMessage(messages.amountPaid)}</span>\n <span>{money(paid)}</span>\n </div>\n </aside>\n );\n\n return (\n <div className=\"alacarte-confirmation\" data-testid=\"alacarte-confirmation\">\n <Row className=\"alacarte-confirmation__row\">\n <Col xs={12} lg={7} className=\"alacarte-confirmation__main\">\n <h1 className=\"alacarte-confirmation__headline font-weight-bold mb-4\">\n {formatMessage(messages.headline)}\n </h1>\n <p className=\"alacarte-confirmation__subheadline mb-4\">\n {formatMessage(\n programOrder ? messages.subheadlineProgram : messages.subheadlineCourse,\n { productLabel },\n )}\n </p>\n\n <div className=\"alacarte-confirmation__ctas d-flex align-items-center flex-wrap mb-4.5\">\n <Button\n variant=\"brand\"\n className=\"alacarte-confirmation__cta-primary\"\n onClick={onContinue}\n >\n {formatMessage(programOrder ? messages.startProgram : messages.startCourse)}\n </Button>\n {orderHistoryUrl && (\n <a href={orderHistoryUrl} className=\"alacarte-confirmation__order-history\">\n {formatMessage(messages.viewOrderHistory)}\n </a>\n )}\n </div>\n\n <div className=\"alacarte-confirmation__summary-mobile d-lg-none\">\n {summaryCard}\n </div>\n\n {programOrder && (purchasedCourseNames.length > 0 || previouslyPurchased.length > 0) && (\n <div className=\"alacarte-confirmation__about-program mb-5\">\n <h2 className=\"alacarte-confirmation__about-title\">\n {formatMessage(messages.aboutProgram)}\n </h2>\n {purchasedCourseNames.length > 0 && (\n <>\n <p className=\"alacarte-confirmation__courses-included\">\n {formatMessage(messages.coursesIncluded, { count: programCourseTotal })}\n </p>\n <ul className=\"alacarte-confirmation__course-list mb-0\">\n {purchasedCourseNames.map((name) => (\n <li key={name}>{name}</li>\n ))}\n </ul>\n </>\n )}\n {previouslyPurchased.length > 0 && (\n <div\n className=\"alacarte-confirmation__previously-purchased\"\n data-testid=\"confirmation-previously-purchased\"\n >\n <p className=\"alacarte-confirmation__previously-purchased-heading\">\n {formatMessage(messages.previouslyPurchased, {\n count: previouslyPurchased.length,\n })}\n </p>\n <ul className=\"alacarte-confirmation__course-list mb-0\">\n {previouslyPurchased.map((name) => (\n <li key={name}>{name}</li>\n ))}\n </ul>\n </div>\n )}\n </div>\n )}\n\n <Row>\n <Col xs={12} md={7} className=\"mb-4\">\n <h2 className=\"h5 font-weight-bold mb-3\">\n {formatMessage(messages.whatsIncluded)}\n </h2>\n <ul className=\"alacarte-confirmation__benefits\">\n <li>{formatMessage(messages.benefit1)}</li>\n <li>{formatMessage(messages.benefit2)}</li>\n <li>{formatMessage(messages.benefit3)}</li>\n </ul>\n </Col>\n <Col xs={12} md={5} className=\"mb-4\">\n <div className=\"mb-3\">\n <p className=\"font-weight-bold mb-0\">\n {formatMessage(messages.orderNumber)}\n </p>\n <p className=\"text-muted mb-0\">{order.orderNumber}</p>\n </div>\n <div>\n <p className=\"font-weight-bold mb-0\">\n {formatMessage(messages.purchaseDate)}\n </p>\n <p className=\"text-muted mb-0\">{purchaseDate ?? ''}</p>\n </div>\n </Col>\n </Row>\n\n {refundWindowOpen && refundBy && (\n <>\n <hr className=\"alacarte-confirmation__divider\" />\n <p className=\"alacarte-confirmation__refund text-muted small mb-0\">\n {formatMessage(messages.refundNotice, { date: refundBy })}\n </p>\n </>\n )}\n </Col>\n\n <Col xs={12} lg={5} className=\"alacarte-confirmation__summary-col d-none d-lg-block\">\n {summaryCard}\n </Col>\n </Row>\n </div>\n );\n};\n\nexport default AlacarteConfirmation;\n"]}
@@ -44,6 +44,11 @@ declare const messages: {
44
44
  defaultMessage: string;
45
45
  description: string;
46
46
  };
47
+ previouslyPurchased: {
48
+ id: string;
49
+ defaultMessage: string;
50
+ description: string;
51
+ };
47
52
  whatsIncluded: {
48
53
  id: string;
49
54
  defaultMessage: string;
@@ -43,7 +43,12 @@ const messages = defineMessages({
43
43
  coursesIncluded: {
44
44
  id: 'checkout.alacarteConfirmation.coursesIncluded',
45
45
  defaultMessage: 'Courses included in this program ({count}):',
46
- description: 'Subheading above the program course list',
46
+ description: 'Subheading above newly purchased program courses; count is the full program size',
47
+ },
48
+ previouslyPurchased: {
49
+ id: 'checkout.alacarteConfirmation.previouslyPurchased',
50
+ defaultMessage: 'Previously purchased ({count}):',
51
+ description: 'Heading above member courses the learner already owned before this order',
47
52
  },
48
53
  whatsIncluded: {
49
54
  id: 'checkout.alacarteConfirmation.whatsIncluded',
@@ -1 +1 @@
1
- {"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/AlacarteConfirmation/Messages.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n orderLoadError: {\n id: 'checkout.alacarteConfirmation.orderLoadError',\n defaultMessage: 'We could not load your order ({error}). Please refresh or check your learner dashboard.',\n description: 'Error shown when tPlacedOrder fails on the confirmation page',\n },\n headline: {\n id: 'checkout.alacarteConfirmation.headline',\n defaultMessage: \"You're ready to begin learning.\",\n description: 'Main headline on the à la carte order confirmation page',\n },\n subheadlineCourse: {\n id: 'checkout.alacarteConfirmation.subheadlineCourse',\n defaultMessage: \"Your payment is complete and you're enrolled in {productLabel}. Below is a summary of your order.\",\n description: 'Confirmation subheadline for a course purchase',\n },\n subheadlineProgram: {\n id: 'checkout.alacarteConfirmation.subheadlineProgram',\n defaultMessage: \"Your payment is complete and you're enrolled in {productLabel}. Below is a summary of your order.\",\n description: 'Confirmation subheadline for a program purchase',\n },\n startCourse: {\n id: 'checkout.alacarteConfirmation.startCourse',\n defaultMessage: 'Start course',\n description: 'Primary CTA after buying a course',\n },\n startProgram: {\n id: 'checkout.alacarteConfirmation.startProgram',\n defaultMessage: 'Start program',\n description: 'Primary CTA after buying a program',\n },\n viewOrderHistory: {\n id: 'checkout.alacarteConfirmation.viewOrderHistory',\n defaultMessage: 'View order history',\n description: 'Secondary link to the learner order history',\n },\n aboutProgram: {\n id: 'checkout.alacarteConfirmation.aboutProgram',\n defaultMessage: 'About program',\n description: 'Heading for the list of courses included in a program order',\n },\n coursesIncluded: {\n id: 'checkout.alacarteConfirmation.coursesIncluded',\n defaultMessage: 'Courses included in this program ({count}):',\n description: 'Subheading above the program course list',\n },\n whatsIncluded: {\n id: 'checkout.alacarteConfirmation.whatsIncluded',\n defaultMessage: \"What's included\",\n description: 'Benefits section heading on the confirmation page',\n },\n benefit1: {\n id: 'checkout.alacarteConfirmation.benefit1',\n defaultMessage: 'Access to graded assignments and exams',\n description: 'First à la carte purchase benefit',\n },\n benefit2: {\n id: 'checkout.alacarteConfirmation.benefit2',\n defaultMessage: 'Shareable Certificate upon completion',\n description: 'Second à la carte purchase benefit',\n },\n benefit3: {\n id: 'checkout.alacarteConfirmation.benefit3',\n defaultMessage: '24/7 Global Support',\n description: 'Third à la carte purchase benefit',\n },\n orderNumber: {\n id: 'checkout.alacarteConfirmation.orderNumber',\n defaultMessage: 'Order number',\n description: 'Order number label',\n },\n purchaseDate: {\n id: 'checkout.alacarteConfirmation.purchaseDate',\n defaultMessage: 'Purchase date',\n description: 'Purchase date label',\n },\n refundNotice: {\n id: 'checkout.alacarteConfirmation.refundNotice',\n defaultMessage: \"Don't like your course? Unenroll by {date} to get a full refund.\",\n description: 'Refund / unenroll notice at the bottom of the confirmation page',\n },\n purchaseSummary: {\n id: 'checkout.alacarteConfirmation.purchaseSummary',\n defaultMessage: 'Purchase summary',\n description: 'Sidebar heading for the purchase summary card',\n },\n verifiedCertificate: {\n id: 'checkout.alacarteConfirmation.verifiedCertificate',\n defaultMessage: 'Verified certificate',\n description: 'Badge on the purchase summary product',\n },\n subtotal: {\n id: 'checkout.alacarteConfirmation.subtotal',\n defaultMessage: 'Subtotal',\n description: 'Subtotal row label',\n },\n discountsApplied: {\n id: 'checkout.alacarteConfirmation.discountsApplied',\n defaultMessage: 'Discounts applied',\n description: 'Discount row label',\n },\n amountPaid: {\n id: 'checkout.alacarteConfirmation.amountPaid',\n defaultMessage: 'Amount paid',\n description: 'Amount paid row label',\n },\n moneyAmount: {\n id: 'checkout.alacarteConfirmation.moneyAmount',\n defaultMessage: '${amount} {currency}',\n description: 'Formatted money amount e.g. $12.00 USD',\n },\n discountAmount: {\n id: 'checkout.alacarteConfirmation.discountAmount',\n defaultMessage: '-${amount} {currency}',\n description: 'Formatted discount amount e.g. -$10.00 USD',\n },\n});\n\nexport default messages;\n"]}
1
+ {"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/AlacarteConfirmation/Messages.ts"],"names":[],"mappings":"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","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n orderLoadError: {\n id: 'checkout.alacarteConfirmation.orderLoadError',\n defaultMessage: 'We could not load your order ({error}). Please refresh or check your learner dashboard.',\n description: 'Error shown when tPlacedOrder fails on the confirmation page',\n },\n headline: {\n id: 'checkout.alacarteConfirmation.headline',\n defaultMessage: \"You're ready to begin learning.\",\n description: 'Main headline on the à la carte order confirmation page',\n },\n subheadlineCourse: {\n id: 'checkout.alacarteConfirmation.subheadlineCourse',\n defaultMessage: \"Your payment is complete and you're enrolled in {productLabel}. Below is a summary of your order.\",\n description: 'Confirmation subheadline for a course purchase',\n },\n subheadlineProgram: {\n id: 'checkout.alacarteConfirmation.subheadlineProgram',\n defaultMessage: \"Your payment is complete and you're enrolled in {productLabel}. Below is a summary of your order.\",\n description: 'Confirmation subheadline for a program purchase',\n },\n startCourse: {\n id: 'checkout.alacarteConfirmation.startCourse',\n defaultMessage: 'Start course',\n description: 'Primary CTA after buying a course',\n },\n startProgram: {\n id: 'checkout.alacarteConfirmation.startProgram',\n defaultMessage: 'Start program',\n description: 'Primary CTA after buying a program',\n },\n viewOrderHistory: {\n id: 'checkout.alacarteConfirmation.viewOrderHistory',\n defaultMessage: 'View order history',\n description: 'Secondary link to the learner order history',\n },\n aboutProgram: {\n id: 'checkout.alacarteConfirmation.aboutProgram',\n defaultMessage: 'About program',\n description: 'Heading for the list of courses included in a program order',\n },\n coursesIncluded: {\n id: 'checkout.alacarteConfirmation.coursesIncluded',\n defaultMessage: 'Courses included in this program ({count}):',\n description: 'Subheading above newly purchased program courses; count is the full program size',\n },\n previouslyPurchased: {\n id: 'checkout.alacarteConfirmation.previouslyPurchased',\n defaultMessage: 'Previously purchased ({count}):',\n description: 'Heading above member courses the learner already owned before this order',\n },\n whatsIncluded: {\n id: 'checkout.alacarteConfirmation.whatsIncluded',\n defaultMessage: \"What's included\",\n description: 'Benefits section heading on the confirmation page',\n },\n benefit1: {\n id: 'checkout.alacarteConfirmation.benefit1',\n defaultMessage: 'Access to graded assignments and exams',\n description: 'First à la carte purchase benefit',\n },\n benefit2: {\n id: 'checkout.alacarteConfirmation.benefit2',\n defaultMessage: 'Shareable Certificate upon completion',\n description: 'Second à la carte purchase benefit',\n },\n benefit3: {\n id: 'checkout.alacarteConfirmation.benefit3',\n defaultMessage: '24/7 Global Support',\n description: 'Third à la carte purchase benefit',\n },\n orderNumber: {\n id: 'checkout.alacarteConfirmation.orderNumber',\n defaultMessage: 'Order number',\n description: 'Order number label',\n },\n purchaseDate: {\n id: 'checkout.alacarteConfirmation.purchaseDate',\n defaultMessage: 'Purchase date',\n description: 'Purchase date label',\n },\n refundNotice: {\n id: 'checkout.alacarteConfirmation.refundNotice',\n defaultMessage: \"Don't like your course? Unenroll by {date} to get a full refund.\",\n description: 'Refund / unenroll notice at the bottom of the confirmation page',\n },\n purchaseSummary: {\n id: 'checkout.alacarteConfirmation.purchaseSummary',\n defaultMessage: 'Purchase summary',\n description: 'Sidebar heading for the purchase summary card',\n },\n verifiedCertificate: {\n id: 'checkout.alacarteConfirmation.verifiedCertificate',\n defaultMessage: 'Verified certificate',\n description: 'Badge on the purchase summary product',\n },\n subtotal: {\n id: 'checkout.alacarteConfirmation.subtotal',\n defaultMessage: 'Subtotal',\n description: 'Subtotal row label',\n },\n discountsApplied: {\n id: 'checkout.alacarteConfirmation.discountsApplied',\n defaultMessage: 'Discounts applied',\n description: 'Discount row label',\n },\n amountPaid: {\n id: 'checkout.alacarteConfirmation.amountPaid',\n defaultMessage: 'Amount paid',\n description: 'Amount paid row label',\n },\n moneyAmount: {\n id: 'checkout.alacarteConfirmation.moneyAmount',\n defaultMessage: '${amount} {currency}',\n description: 'Formatted money amount e.g. $12.00 USD',\n },\n discountAmount: {\n id: 'checkout.alacarteConfirmation.discountAmount',\n defaultMessage: '-${amount} {currency}',\n description: 'Formatted discount amount e.g. -$10.00 USD',\n },\n});\n\nexport default messages;\n"]}
@@ -10,20 +10,26 @@ import messages from './Messages';
10
10
  import learnersIcon from './world-icon.svg';
11
11
  import institutionsIcon from './student-icon.svg';
12
12
  const CartSummary = ({ productTitle, productBlurb, learnerCount: learnerCountProp, course = null, alacarteLine = null, programDetail = null, programName: programNameProp = null, promoCode = null, }) => {
13
- var _a, _b, _c, _d;
13
+ var _a, _b, _c, _d, _e, _f, _g, _h;
14
14
  const { formatMessage } = useIntl();
15
15
  const title = productTitle !== null && productTitle !== void 0 ? productTitle : formatMessage(messages.productTitle);
16
16
  const blurb = productBlurb !== null && productBlurb !== void 0 ? productBlurb : formatMessage(messages.productBlurb);
17
- // Prefer the course's own enrollment count. Programs have no equivalent field
18
- // — the badge is hidden for those rather than inventing a figure.
17
+ // Prefer the product's own enrollment count (course or program catalog field).
19
18
  const isProgramLine = (alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.kind) === 'program';
20
- const subscriberCount = (_b = (_a = course === null || course === void 0 ? void 0 : course.tEnrollmentCount) !== null && _a !== void 0 ? _a : learnerCountProp) !== null && _b !== void 0 ? _b : null;
19
+ const subscriberCount = (_c = (_b = (_a = course === null || course === void 0 ? void 0 : course.tEnrollmentCount) !== null && _a !== void 0 ? _a : programDetail === null || programDetail === void 0 ? void 0 : programDetail.tEnrollmentCount) !== null && _b !== void 0 ? _b : learnerCountProp) !== null && _c !== void 0 ? _c : null;
21
20
  // No fallback amount — an unresolved à la carte line means we don't know
22
21
  // the price yet, so the row is left blank rather than showing a made-up
23
22
  // number that could be mistaken for the real one.
24
- const subtotalText = (alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.price)
23
+ // Program subtotal is price less owned-course deduction (before coupon/discount).
24
+ const deductionAmount = isProgramLine && alacarteLine.kind === 'program'
25
+ ? ((_e = (_d = alacarteLine.deduction) === null || _d === void 0 ? void 0 : _d.amount) !== null && _e !== void 0 ? _e : 0)
26
+ : 0;
27
+ const subtotalAmount = (alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.price)
28
+ ? Math.max(0, alacarteLine.price.amount - deductionAmount)
29
+ : null;
30
+ const subtotalText = subtotalAmount != null && (alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.price)
25
31
  ? formatMessage(messages.subtotalAmount, {
26
- amount: alacarteLine.price.amount.toFixed(2), currency: alacarteLine.price.currency,
32
+ amount: subtotalAmount.toFixed(2), currency: alacarteLine.price.currency,
27
33
  })
28
34
  : '';
29
35
  const totalText = (alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.amountDue)
@@ -35,22 +41,17 @@ const CartSummary = ({ productTitle, productBlurb, learnerCount: learnerCountPro
35
41
  ? (alacarteLine.kind === 'course' ? alacarteLine.courseName : alacarteLine.programName)
36
42
  : programNameProp;
37
43
  const ownedCourses = isProgramLine && alacarteLine.kind === 'program'
38
- ? ((_c = alacarteLine.ownedCourses) !== null && _c !== void 0 ? _c : [])
44
+ ? ((_f = alacarteLine.ownedCourses) !== null && _f !== void 0 ? _f : [])
39
45
  : [];
40
46
  const ownedCourseNames = ownedCourses
41
47
  .map((c) => c.name)
42
48
  .filter(Boolean)
43
49
  .join(', ');
44
- const discount = (_d = alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.discount) !== null && _d !== void 0 ? _d : null;
45
- const deduction = isProgramLine && alacarteLine.kind === 'program'
46
- ? alacarteLine.deduction
47
- : null;
48
- 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) }), !isProgramLine && (_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, {
50
+ const discount = (_g = alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.discount) !== null && _g !== void 0 ? _g : null;
51
+ 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, {
49
52
  count: subscriberCount.toLocaleString(),
50
- }))] }))] }), _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 : '' })) : 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 })] })] })), 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 })] }), deduction && (_jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center p-0", "data-testid": "deduction-row", children: [_jsx("span", { className: "text-gray-700", children: formatMessage(messages.deduction) }), _jsx("span", { className: "text-gray-700", children: formatMessage(messages.deductionAmount, {
51
- amount: deduction.amount.toFixed(2),
52
- currency: deduction.currency,
53
- }) })] })), 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: formatMessage(messages.discountApplied) }), _jsx("span", { className: "text-success font-weight-bold", children: formatMessage(messages.discountAmount, {
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: ((_h = alacarteLine === null || alacarteLine === void 0 ? void 0 : alacarteLine.discountName) === null || _h === void 0 ? void 0 : _h.trim())
54
+ || formatMessage(messages.discountApplied) }), _jsx("span", { className: "text-success font-weight-bold", children: formatMessage(messages.discountAmount, {
54
55
  amount: discount.amount.toFixed(2),
55
56
  currency: discount.currency,
56
57
  }) })] })), _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":";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,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,8EAA8E;IAC9E,kEAAkE;IAClE,MAAM,aAAa,GAAG,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,IAAI,MAAK,SAAS,CAAC;IACvD,MAAM,eAAe,GAAG,MAAA,MAAA,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,gBAAgB,mCAAI,gBAAgB,mCAAI,IAAI,CAAC;IAE7E,yEAAyE;IACzE,wEAAwE;IACxE,kDAAkD;IAClD,MAAM,YAAY,GAAG,CAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,KAAK;QACtC,CAAC,CAAC,aAAa,CAAC,QAAQ,CAAC,cAAc,EAAE;YACrC,MAAM,EAAE,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,KAAK,CAAC,QAAQ;SACpF,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,MAAM,SAAS,GAAG,aAAa,IAAI,YAAY,CAAC,IAAI,KAAK,SAAS;QAChE,CAAC,CAAC,YAAY,CAAC,SAAS;QACxB,CAAC,CAAC,IAAI,CAAC;IAET,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,EACN,CAAC,aAAa,IAAI,CACjB,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,CACT,IACY,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,IAAC,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,aAAR,QAAQ,cAAR,QAAQ,GAAI,EAAE,GAAI,CAC/E,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,EACA,gBAAgB,IAAI,CACnB,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,SAAS,IAAI,CACZ,MAAC,IAAI,CAAC,OAAO,IACX,SAAS,EAAC,uDAAuD,iBACrD,eAAe,aAE3B,eAAM,SAAS,EAAC,eAAe,YAC5B,aAAa,CAAC,QAAQ,CAAC,SAAS,CAAC,GAC7B,EACP,eAAM,SAAS,EAAC,eAAe,YAC5B,aAAa,CAAC,QAAQ,CAAC,eAAe,EAAE;oCACvC,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;oCACnC,QAAQ,EAAE,SAAS,CAAC,QAAQ;iCAC7B,CAAC,GACG,IACM,CAChB,EAEA,QAAQ,IAAI,CACX,MAAC,IAAI,CAAC,OAAO,IACX,SAAS,EAAC,uDAAuD,iBACrD,cAAc,aAE1B,eAAM,SAAS,EAAC,+BAA+B,YAC5C,aAAa,CAAC,QAAQ,CAAC,eAAe,CAAC,GACnC,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\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 course's own enrollment count. Programs have no equivalent field\n // — the badge is hidden for those rather than inventing a figure.\n const isProgramLine = alacarteLine?.kind === 'program';\n const subscriberCount = course?.tEnrollmentCount ?? learnerCountProp ?? 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 const subtotalText = alacarteLine?.price\n ? formatMessage(messages.subtotalAmount, {\n amount: alacarteLine.price.amount.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 const deduction = isProgramLine && alacarteLine.kind === 'program'\n ? alacarteLine.deduction\n : 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 {!isProgramLine && (\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 )}\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 program={programDetail} programName={lineName ?? ''} />\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 {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 {deduction && (\n <Card.Section\n className=\"d-flex justify-content-between align-items-center p-0\"\n data-testid=\"deduction-row\"\n >\n <span className=\"text-gray-700\">\n {formatMessage(messages.deduction)}\n </span>\n <span className=\"text-gray-700\">\n {formatMessage(messages.deductionAmount, {\n amount: deduction.amount.toFixed(2),\n currency: deduction.currency,\n })}\n </span>\n </Card.Section>\n )}\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 {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":"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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"]}
@@ -54,7 +54,7 @@ const messages = defineMessages({
54
54
  discountApplied: {
55
55
  id: 'checkout.cartSummary.discountApplied',
56
56
  defaultMessage: 'Discounts applied',
57
- description: 'Label for the coupon discount row shown below the subtotal',
57
+ description: 'Fallback label for the discount row 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,mBAAmB;QACnC,WAAW,EAAE,4DAA4D;KAC1E;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: 'Discounts applied',\n description: 'Label for the coupon discount row shown below the subtotal',\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"]}
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,mBAAmB;QACnC,WAAW,EAAE,iEAAiE;KAC/E;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: 'Discounts applied',\n description: 'Fallback label for the discount row 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"]}