@edx/frontend-app-subscription-checkout 1.2.0 → 1.4.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 (52) hide show
  1. package/dist/app.js +6 -3
  2. package/dist/app.js.map +1 -1
  3. package/dist/checkoutPage/CheckoutPage.js +2 -1
  4. package/dist/checkoutPage/CheckoutPage.js.map +1 -1
  5. package/dist/components/CartSummary/CartSummary.d.ts +12 -2
  6. package/dist/components/CartSummary/CartSummary.js +17 -17
  7. package/dist/components/CartSummary/CartSummary.js.map +1 -1
  8. package/dist/components/CartSummary/Messages.d.ts +10 -0
  9. package/dist/components/CartSummary/Messages.js +12 -2
  10. package/dist/components/CartSummary/Messages.js.map +1 -1
  11. package/dist/components/Checkout/Checkout.d.ts +10 -2
  12. package/dist/components/Checkout/Checkout.js +43 -5
  13. package/dist/components/Checkout/Checkout.js.map +1 -1
  14. package/dist/components/CheckoutGate/CheckoutGate.d.ts +4 -2
  15. package/dist/components/CheckoutGate/CheckoutGate.js +5 -4
  16. package/dist/components/CheckoutGate/CheckoutGate.js.map +1 -1
  17. package/dist/components/CountryUnavailableModal/CountryUnavailableModal.d.ts +9 -0
  18. package/dist/components/CountryUnavailableModal/CountryUnavailableModal.js +10 -0
  19. package/dist/components/CountryUnavailableModal/CountryUnavailableModal.js.map +1 -0
  20. package/dist/components/CountryUnavailableModal/Messages.d.ts +18 -0
  21. package/dist/components/CountryUnavailableModal/Messages.js +20 -0
  22. package/dist/components/CountryUnavailableModal/Messages.js.map +1 -0
  23. package/dist/components/EnrollmentSummary/EnrollmentSummary.d.ts +3 -2
  24. package/dist/components/EnrollmentSummary/EnrollmentSummary.js +5 -3
  25. package/dist/components/EnrollmentSummary/EnrollmentSummary.js.map +1 -1
  26. package/dist/components/EnrollmentSummary/Messages.js +2 -2
  27. package/dist/components/EnrollmentSummary/Messages.js.map +1 -1
  28. package/dist/components/PaymentForm/PaymentForm.js +3 -3
  29. package/dist/components/PaymentForm/PaymentForm.js.map +1 -1
  30. package/dist/constants.d.ts +2 -1
  31. package/dist/constants.js +201 -196
  32. package/dist/constants.js.map +1 -1
  33. package/dist/data/hooks/useCheckout.js +5 -0
  34. package/dist/data/hooks/useCheckout.js.map +1 -1
  35. package/dist/data/hooks/useCheckoutMutations.d.ts +3 -1
  36. package/dist/data/hooks/useCheckoutMutations.js +7 -4
  37. package/dist/data/hooks/useCheckoutMutations.js.map +1 -1
  38. package/dist/routes.js +1 -1
  39. package/dist/routes.js.map +1 -1
  40. package/dist/styles/components/cartSummary.scss +4 -0
  41. package/dist/styles/components/paymentForm.scss +4 -0
  42. package/dist/utils/billingDetails.d.ts +1 -1
  43. package/dist/utils/billingDetails.js +6 -5
  44. package/dist/utils/billingDetails.js.map +1 -1
  45. package/dist/utils/cartApi.js +11 -9
  46. package/dist/utils/cartApi.js.map +1 -1
  47. package/dist/utils/provision.js +2 -2
  48. package/dist/utils/provision.js.map +1 -1
  49. package/dist/utils/queries.d.ts +1 -1
  50. package/dist/utils/queries.js +0 -2
  51. package/dist/utils/queries.js.map +1 -1
  52. package/package.json +2 -2
package/dist/app.js CHANGED
@@ -5,9 +5,12 @@ const app = {
5
5
  appId,
6
6
  routes,
7
7
  slots,
8
- // Environment-specific values (STRIPE_PUBLIC_KEY, VIAX_API_URL) are supplied
9
- // by site.config.{dev,stage,prod}.tsx at site build time.
10
- config: {},
8
+ // Environment-specific values (STRIPE_PUBLIC_KEY, VIAX_API_URL,
9
+ // ENABLE_ANNUAL_BILLING) are supplied by site.config.{dev,stage,prod}.tsx
10
+ // at site build time.
11
+ config: {
12
+ ENABLE_ANNUAL_BILLING: false,
13
+ },
11
14
  };
12
15
  export default app;
13
16
  //# sourceMappingURL=app.js.map
package/dist/app.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,MAAM,GAAG,GAAQ;IACf,KAAK;IACL,MAAM;IACN,KAAK;IACL,6EAA6E;IAC7E,0DAA0D;IAC1D,MAAM,EAAE,EAAE;CACX,CAAC;AAEF,eAAe,GAAG,CAAC","sourcesContent":["import { App } from '@openedx/frontend-base';\nimport { appId } from '@src/constants';\nimport routes from '@src/routes';\nimport slots from '@src/slots';\n\nconst app: App = {\n appId,\n routes,\n slots,\n // Environment-specific values (STRIPE_PUBLIC_KEY, VIAX_API_URL) are supplied\n // by site.config.{dev,stage,prod}.tsx at site build time.\n config: {},\n};\n\nexport default app;\n"]}
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AACvC,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,KAAK,MAAM,YAAY,CAAC;AAE/B,MAAM,GAAG,GAAQ;IACf,KAAK;IACL,MAAM;IACN,KAAK;IACL,gEAAgE;IAChE,0EAA0E;IAC1E,sBAAsB;IACtB,MAAM,EAAE;QACN,qBAAqB,EAAE,KAAK;KAC7B;CACF,CAAC;AAEF,eAAe,GAAG,CAAC","sourcesContent":["import { App } from '@openedx/frontend-base';\nimport { appId } from '@src/constants';\nimport routes from '@src/routes';\nimport slots from '@src/slots';\n\nconst app: App = {\n appId,\n routes,\n slots,\n // Environment-specific values (STRIPE_PUBLIC_KEY, VIAX_API_URL,\n // ENABLE_ANNUAL_BILLING) are supplied by site.config.{dev,stage,prod}.tsx\n // at site build time.\n config: {\n ENABLE_ANNUAL_BILLING: false,\n },\n};\n\nexport default app;\n"]}
@@ -10,6 +10,7 @@ const CheckoutPage = () => {
10
10
  const LMS_BASE_URL = siteConfig.lmsBaseUrl.replace(/\/$/, '');
11
11
  const stripePublishableKey = config.STRIPE_PUBLIC_KEY;
12
12
  const apiUrl = config.VIAX_API_URL;
13
+ const enableAnnualBilling = Boolean(config.ENABLE_ANNUAL_BILLING);
13
14
  const isDevelopment = siteConfig.environment === 'development';
14
15
  const edxSessionUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/edx-session`;
15
16
  console.log('siteConfig ===> ', siteConfig);
@@ -20,7 +21,7 @@ const CheckoutPage = () => {
20
21
  if (!stripePublishableKey || !apiUrl || !edxSessionUrl) {
21
22
  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" }), "."] }) }));
22
23
  }
23
- return (_jsx(PageWrapper, { children: _jsx(CheckoutGate, { stripePublishableKey: stripePublishableKey, apiUrl: apiUrl, edxSessionUrl: edxSessionUrl, learnerDashboardUrl: `${LMS_BASE_URL}/dashboard`, onComplete: (_subscriptionBiId) => {
24
+ return (_jsx(PageWrapper, { children: _jsx(CheckoutGate, { stripePublishableKey: stripePublishableKey, apiUrl: apiUrl, edxSessionUrl: edxSessionUrl, enableAnnualBilling: enableAnnualBilling, learnerDashboardUrl: `${LMS_BASE_URL}/dashboard`, homeUrl: LMS_BASE_URL || '/', onComplete: (_subscriptionBiId) => {
24
25
  console.log(_subscriptionBiId);
25
26
  // window.location.href = `/checkout/confirmation?sub=${_subscriptionBiId ?? ''}`;
26
27
  // Success screen.
@@ -1 +1 @@
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;IAE1G,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,4BAA4B,EAAE,oBAAoB,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,aAAa,CAAC,CAAC;IAElD,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,mBAAmB,EAAE,GAAG,YAAY,YAAY,EAChD,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\n console.log('siteConfig ===> ', siteConfig);\n console.log('config ====> ', config);\n console.log('stripePublishableKey ===> ', stripePublishableKey);\n console.log('apiURL ==> ', apiUrl);\n console.log('edxSessionUrl ===> ', edxSessionUrl);\n\n if (!stripePublishableKey || !apiUrl || !edxSessionUrl) {\n return (\n <PageWrapper>\n <Alert variant=\"danger\">\n We&apos;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 learnerDashboardUrl={`${LMS_BASE_URL}/dashboard`}\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"]}
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,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAAC,CAAC;IAClE,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;IAE1G,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,UAAU,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IACrC,OAAO,CAAC,GAAG,CAAC,4BAA4B,EAAE,oBAAoB,CAAC,CAAC;IAChE,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;IACnC,OAAO,CAAC,GAAG,CAAC,qBAAqB,EAAE,aAAa,CAAC,CAAC;IAElD,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,mBAAmB,EAAE,mBAAmB,EACxC,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 enableAnnualBilling = Boolean(config.ENABLE_ANNUAL_BILLING);\n const isDevelopment = siteConfig.environment === 'development';\n const edxSessionUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/edx-session`;\n\n console.log('siteConfig ===> ', siteConfig);\n console.log('config ====> ', config);\n console.log('stripePublishableKey ===> ', stripePublishableKey);\n console.log('apiURL ==> ', apiUrl);\n console.log('edxSessionUrl ===> ', edxSessionUrl);\n\n if (!stripePublishableKey || !apiUrl || !edxSessionUrl) {\n return (\n <PageWrapper>\n <Alert variant=\"danger\">\n We&apos;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 enableAnnualBilling={enableAnnualBilling}\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"]}
@@ -3,8 +3,12 @@ export type BillingCycle = 'monthly' | 'annual';
3
3
  export interface CartSummaryProps {
4
4
  productTitle?: string;
5
5
  productBlurb?: string;
6
- /** Monthly price amount e.g. "39" */
6
+ /** Promotional monthly price amount e.g. "39" */
7
7
  monthlyAmount?: string;
8
+ /** Regular monthly price after the promo period e.g. "59" */
9
+ monthlyRegularAmount?: string;
10
+ /** Length of the promotional period in months */
11
+ promoMonths?: number;
8
12
  /** Annual price amount e.g. "299" */
9
13
  annualAmount?: string;
10
14
  currency?: string;
@@ -12,10 +16,16 @@ export interface CartSummaryProps {
12
16
  defaultCycle?: BillingCycle;
13
17
  monthlyRenewalLabel?: string;
14
18
  annualRenewalLabel?: string;
19
+ /** Called when the learner switches monthly ↔ annual (requires enableAnnualBilling). */
15
20
  onCycleChange?: (cycle: BillingCycle) => void;
21
+ /**
22
+ * When true, shows the Switch to annual/monthly control.
23
+ * Controlled via ENABLE_ANNUAL_BILLING in site.config.*.tsx.
24
+ */
25
+ enableAnnualBilling?: boolean;
16
26
  learnerCount?: number;
17
27
  /** When set, shows the course enrollment preview card below the summary card. */
18
28
  course?: CourseDetail | null;
19
29
  }
20
- declare const CartSummary: ({ productTitle, productBlurb, monthlyAmount, annualAmount, currency, activeSubscriberCount, defaultCycle, onCycleChange, learnerCount: learnerCountProp, course, }: CartSummaryProps) => import("react/jsx-runtime").JSX.Element;
30
+ declare const CartSummary: ({ productTitle, productBlurb, monthlyAmount, monthlyRegularAmount, promoMonths, annualAmount, currency, activeSubscriberCount, defaultCycle, onCycleChange, enableAnnualBilling, learnerCount: learnerCountProp, course, }: CartSummaryProps) => import("react/jsx-runtime").JSX.Element;
21
31
  export default CartSummary;
@@ -1,30 +1,30 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
- import { Badge, Card, Icon } from '@openedx/paragon';
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Badge, Button, Card, Icon } from '@openedx/paragon';
4
3
  import { Group, People, Institution } from '@openedx/paragon/icons';
5
4
  import { useIntl } from '@openedx/frontend-base';
6
5
  import { EdxUnlimitedLogo } from '../BrandMarks/BrandMarks';
7
6
  import CourseEnrollmentCard from '../CourseEnrollmentCard/CourseEnrollmentCard';
8
7
  import messages from './Messages';
9
- const CartSummary = ({ productTitle = 'edX Subscription', productBlurb = 'Subscribe and get access to hundreds of courses and programs.', monthlyAmount = '39', annualAmount = '299', currency = 'USD', activeSubscriberCount, defaultCycle = 'monthly', onCycleChange, learnerCount: learnerCountProp = 2203, course = null, }) => {
8
+ const CartSummary = ({ productTitle = 'edX Subscription', productBlurb = 'Subscribe and get access to hundreds of courses and programs.', monthlyAmount = '39', monthlyRegularAmount = '59', promoMonths = 3, annualAmount = '299', currency = 'USD', activeSubscriberCount, defaultCycle = 'monthly', onCycleChange, enableAnnualBilling = false, learnerCount: learnerCountProp = 2203, course = null, }) => {
10
9
  const { formatMessage } = useIntl();
11
- const [cycle, setCycle] = useState(defaultCycle);
12
- const isMonthly = cycle === 'monthly';
13
- const handleToggle = () => {
14
- const next = isMonthly ? 'annual' : 'monthly';
15
- setCycle(next);
16
- onCycleChange === null || onCycleChange === void 0 ? void 0 : onCycleChange(next);
17
- };
18
- const frequency = isMonthly ? 'month' : 'year';
19
- const amount = isMonthly ? monthlyAmount : annualAmount;
10
+ const isMonthly = defaultCycle === 'monthly';
11
+ const showCycleToggle = enableAnnualBilling && !!onCycleChange;
20
12
  const learnerCount = (activeSubscriberCount != null ? activeSubscriberCount : learnerCountProp).toLocaleString();
21
13
  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' } }), formatMessage(messages.learnersSubscribed, {
22
14
  count: learnerCount,
23
- })] })] }), _jsx(Card.Divider, {}), _jsx(Card.Section, { className: "p-0 py-3", children: _jsxs("div", { className: "d-flex align-items-start", children: [_jsx(EdxUnlimitedLogo, {}), _jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-0 text-primary-500", children: productTitle }), _jsx("p", { className: "small mb-0 text-gray-900", children: productBlurb })] })] }) }), _jsx(Card.Divider, {}), _jsxs(Card.Section, { className: "p-0 py-3", children: [_jsxs("div", { className: "d-flex justify-content-between align-items-center mb-1", children: [_jsx("span", { className: "small text-gray-900", children: isMonthly
15
+ })] })] }), _jsx(Card.Divider, {}), _jsx(Card.Section, { className: "p-0 py-3", children: _jsxs("div", { className: "d-flex align-items-start", children: [_jsx(EdxUnlimitedLogo, {}), _jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-0 text-primary-500", children: productTitle }), _jsx("p", { className: "small mb-0 text-gray-900", children: productBlurb })] })] }) }), _jsx(Card.Divider, {}), _jsxs(Card.Section, { className: "p-0 py-3", children: [_jsxs("div", { className: "d-flex justify-content-between align-items-start mb-1", children: [_jsx("span", { className: "small text-gray-900", children: isMonthly
24
16
  ? formatMessage(messages.monthlySubscription)
25
- : formatMessage(messages.annualSubscription) }), _jsx("span", { className: "text-success font-weight-bold small", children: formatMessage(messages.trialLabel) })] }), _jsxs("div", { className: "d-flex justify-content-between align-items-center", children: [_jsx("button", { type: "button", className: "btn btn-link p-0 small", onClick: handleToggle, children: isMonthly
26
- ? formatMessage(messages.switchToAnnual)
27
- : formatMessage(messages.switchToMonthly) }), _jsxs("span", { className: "small text-gray-900 font-weight-bold", children: ["$", amount, "/", frequency, " ", currency] })] })] }), _jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center px-0", children: [_jsx("span", { className: "font-weight-bold text-gray-900", children: formatMessage(messages.todaysTotal) }), _jsx("span", { className: "font-weight-bold mb-0 text-gray-900", children: formatMessage(messages.todaysTotalValue) })] })] }), course && (_jsx(CourseEnrollmentCard, { course: course, variant: "checkout" })), _jsxs("div", { className: "cart-summary__stats d-none d-lg-flex mt-3", children: [_jsxs("div", { className: "cart-summary__stat d-flex align-items-center mr-4", children: [_jsx(Icon, { src: People, className: "mr-2 text-muted", style: { width: '1.5rem', height: '1.5rem' } }), _jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-0", children: formatMessage(messages.learnersCount) }), _jsx("p", { className: "text-muted small mb-0", children: formatMessage(messages.learnersLabel) })] })] }), _jsxs("div", { className: "cart-summary__stat d-flex align-items-center", children: [_jsx(Icon, { src: Institution, className: "mr-2 text-muted", style: { width: '1.5rem', height: '1.5rem' } }), _jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-0", children: formatMessage(messages.institutionsCount) }), _jsx("p", { className: "text-muted small mb-0", children: formatMessage(messages.institutionsLabel) })] })] })] })] }));
17
+ : formatMessage(messages.annualSubscription) }), _jsxs("div", { className: "cart-summary__pricing text-right", children: [_jsx("div", { className: "text-success font-weight-bold small", children: formatMessage(messages.trialLabel) }), isMonthly ? (_jsxs(_Fragment, { children: [_jsx("div", { className: "small text-gray-900 font-weight-bold", children: formatMessage(messages.promotionalPrice, {
18
+ amount: monthlyAmount,
19
+ months: promoMonths,
20
+ }) }), _jsx("div", { className: "small text-gray-900 font-weight-bold", children: formatMessage(messages.regularPrice, {
21
+ amount: monthlyRegularAmount,
22
+ currency,
23
+ }) })] })) : (_jsx("div", { className: "small text-gray-900 font-weight-bold", children: formatMessage(messages.priceDisplay, {
24
+ amount: annualAmount,
25
+ frequency: 'year',
26
+ currency,
27
+ }) }))] })] }), showCycleToggle && (_jsx("div", { className: "mt-2", children: _jsx(Button, { variant: "link", className: "cart-summary__cycle-toggle p-0", onClick: () => onCycleChange === null || onCycleChange === void 0 ? void 0 : onCycleChange(isMonthly ? 'annual' : 'monthly'), children: formatMessage(isMonthly ? messages.switchToAnnual : messages.switchToMonthly) }) }))] }), _jsxs(Card.Section, { className: "d-flex justify-content-between align-items-center px-0", children: [_jsx("span", { className: "font-weight-bold text-gray-900", children: formatMessage(messages.todaysTotal) }), _jsx("span", { className: "font-weight-bold mb-0 text-gray-900", children: formatMessage(messages.todaysTotalValue) })] })] }), course && (_jsx(CourseEnrollmentCard, { course: course, variant: "checkout" })), _jsxs("div", { className: "cart-summary__stats d-none d-lg-flex mt-3", children: [_jsxs("div", { className: "cart-summary__stat d-flex align-items-center mr-4", children: [_jsx(Icon, { src: People, className: "mr-2 text-muted", style: { width: '1.5rem', height: '1.5rem' } }), _jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-0", children: formatMessage(messages.learnersCount) }), _jsx("p", { className: "text-muted small mb-0", children: formatMessage(messages.learnersLabel) })] })] }), _jsxs("div", { className: "cart-summary__stat d-flex align-items-center", children: [_jsx(Icon, { src: Institution, className: "mr-2 text-muted", style: { width: '1.5rem', height: '1.5rem' } }), _jsxs("div", { children: [_jsx("p", { className: "font-weight-bold mb-0", children: formatMessage(messages.institutionsCount) }), _jsx("p", { className: "text-muted small mb-0", children: formatMessage(messages.institutionsLabel) })] })] })] })] }));
28
28
  };
29
29
  export default CartSummary;
30
30
  //# sourceMappingURL=CartSummary.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CartSummary.js","sourceRoot":"","sources":["../../../src/components/CartSummary/CartSummary.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useState } from 'react';\nimport { Badge, Card, Icon } from '@openedx/paragon';\nimport { Group, People, Institution } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport { EdxUnlimitedLogo } from '../BrandMarks/BrandMarks';\nimport CourseEnrollmentCard from '../CourseEnrollmentCard/CourseEnrollmentCard';\nimport type { CourseDetail } from '../../types';\nimport messages from './Messages';\n\nexport type BillingCycle = 'monthly' | 'annual';\n\nexport interface CartSummaryProps {\n productTitle?: string,\n productBlurb?: string,\n /** Monthly price amount e.g. \"39\" */\n monthlyAmount?: string,\n /** Annual price amount e.g. \"299\" */\n annualAmount?: string,\n currency?: string,\n activeSubscriberCount?: number | null,\n defaultCycle?: BillingCycle,\n monthlyRenewalLabel?: string,\n annualRenewalLabel?: string,\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}\n\nconst CartSummary = ({\n productTitle = 'edX Subscription',\n productBlurb = 'Subscribe and get access to hundreds of courses and programs.',\n monthlyAmount = '39',\n annualAmount = '299',\n currency = 'USD',\n activeSubscriberCount,\n defaultCycle = 'monthly',\n onCycleChange,\n learnerCount: learnerCountProp = 2203,\n course = null,\n}: CartSummaryProps) => {\n const { formatMessage } = useIntl();\n const [cycle, setCycle] = useState<BillingCycle>(defaultCycle);\n\n const isMonthly = cycle === 'monthly';\n\n const handleToggle = () => {\n const next: BillingCycle = isMonthly ? 'annual' : 'monthly';\n setCycle(next);\n onCycleChange?.(next);\n };\n\n const frequency = isMonthly ? 'month' : 'year';\n const amount = isMonthly ? monthlyAmount : annualAmount;\n\n const learnerCount = (\n activeSubscriberCount != null ? activeSubscriberCount : learnerCountProp\n ).toLocaleString();\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 {formatMessage(messages.learnersSubscribed, {\n count: learnerCount,\n })}\n </Badge>\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"p-0 py-3\">\n <div className=\"d-flex align-items-start\">\n <EdxUnlimitedLogo />\n <div>\n <p className=\"font-weight-bold mb-0 text-primary-500\">\n {productTitle}\n </p>\n <p className=\"small mb-0 text-gray-900\">{productBlurb}</p>\n </div>\n </div>\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"p-0 py-3\">\n <div className=\"d-flex justify-content-between align-items-center mb-1\">\n <span className=\"small text-gray-900\">\n {isMonthly\n ? formatMessage(messages.monthlySubscription)\n : formatMessage(messages.annualSubscription)}\n </span>\n <span className=\"text-success font-weight-bold small\">\n {formatMessage(messages.trialLabel)}\n </span>\n </div>\n <div className=\"d-flex justify-content-between align-items-center\">\n <button\n type=\"button\"\n className=\"btn btn-link p-0 small\"\n onClick={handleToggle}\n >\n {isMonthly\n ? formatMessage(messages.switchToAnnual)\n : formatMessage(messages.switchToMonthly)}\n </button>\n <span className=\"small text-gray-900 font-weight-bold\">\n ${amount}/{frequency} {currency}\n </span>\n </div>\n </Card.Section>\n\n <Card.Section className=\"d-flex justify-content-between align-items-center px-0\">\n <span className=\"font-weight-bold text-gray-900\">\n {formatMessage(messages.todaysTotal)}\n </span>\n <span className=\"font-weight-bold mb-0 text-gray-900\">\n {formatMessage(messages.todaysTotalValue)}\n </span>\n </Card.Section>\n </Card>\n\n {course && (\n <CourseEnrollmentCard course={course} variant=\"checkout\" />\n )}\n\n {/* Stats row */}\n <div className=\"cart-summary__stats d-none d-lg-flex mt-3\">\n <div className=\"cart-summary__stat d-flex align-items-center mr-4\">\n <Icon\n src={People}\n className=\"mr-2 text-muted\"\n style={{ width: '1.5rem', height: '1.5rem' }}\n />\n <div>\n <p className=\"font-weight-bold mb-0\">\n {formatMessage(messages.learnersCount)}\n </p>\n <p className=\"text-muted small mb-0\">\n {formatMessage(messages.learnersLabel)}\n </p>\n </div>\n </div>\n <div className=\"cart-summary__stat d-flex align-items-center\">\n <Icon\n src={Institution}\n className=\"mr-2 text-muted\"\n style={{ width: '1.5rem', height: '1.5rem' }}\n />\n <div>\n <p className=\"font-weight-bold mb-0\">\n {formatMessage(messages.institutionsCount)}\n </p>\n <p className=\"text-muted small 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, Button, Card, Icon } from '@openedx/paragon';\nimport { Group, People, Institution } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport { EdxUnlimitedLogo } from '../BrandMarks/BrandMarks';\nimport CourseEnrollmentCard from '../CourseEnrollmentCard/CourseEnrollmentCard';\nimport type { CourseDetail } from '../../types';\nimport messages from './Messages';\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 activeSubscriberCount?: number | null,\n defaultCycle?: BillingCycle,\n monthlyRenewalLabel?: string,\n annualRenewalLabel?: string,\n /** Called when the learner switches monthly ↔ annual (requires enableAnnualBilling). */\n onCycleChange?: (cycle: BillingCycle) => void,\n /**\n * When true, shows the Switch to annual/monthly control.\n * Controlled via ENABLE_ANNUAL_BILLING in site.config.*.tsx.\n */\n enableAnnualBilling?: boolean,\n learnerCount?: number,\n /** When set, shows the course enrollment preview card below the summary card. */\n course?: CourseDetail | null,\n}\n\nconst CartSummary = ({\n productTitle = 'edX Subscription',\n productBlurb = 'Subscribe and get access to hundreds of courses and programs.',\n monthlyAmount = '39',\n monthlyRegularAmount = '59',\n promoMonths = 3,\n annualAmount = '299',\n currency = 'USD',\n activeSubscriberCount,\n defaultCycle = 'monthly',\n onCycleChange,\n enableAnnualBilling = false,\n learnerCount: learnerCountProp = 2203,\n course = null,\n}: CartSummaryProps) => {\n const { formatMessage } = useIntl();\n const isMonthly = defaultCycle === 'monthly';\n const showCycleToggle = enableAnnualBilling && !!onCycleChange;\n\n const learnerCount = (\n activeSubscriberCount != null ? activeSubscriberCount : learnerCountProp\n ).toLocaleString();\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 {formatMessage(messages.learnersSubscribed, {\n count: learnerCount,\n })}\n </Badge>\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"p-0 py-3\">\n <div className=\"d-flex align-items-start\">\n <EdxUnlimitedLogo />\n <div>\n <p className=\"font-weight-bold mb-0 text-primary-500\">\n {productTitle}\n </p>\n <p className=\"small mb-0 text-gray-900\">{productBlurb}</p>\n </div>\n </div>\n </Card.Section>\n\n <Card.Divider />\n\n <Card.Section className=\"p-0 py-3\">\n <div className=\"d-flex justify-content-between align-items-start mb-1\">\n <span className=\"small text-gray-900\">\n {isMonthly\n ? formatMessage(messages.monthlySubscription)\n : formatMessage(messages.annualSubscription)}\n </span>\n <div className=\"cart-summary__pricing text-right\">\n <div className=\"text-success font-weight-bold small\">\n {formatMessage(messages.trialLabel)}\n </div>\n {isMonthly ? (\n <>\n <div className=\"small text-gray-900 font-weight-bold\">\n {formatMessage(messages.promotionalPrice, {\n amount: monthlyAmount,\n months: promoMonths,\n })}\n </div>\n <div className=\"small text-gray-900 font-weight-bold\">\n {formatMessage(messages.regularPrice, {\n amount: monthlyRegularAmount,\n currency,\n })}\n </div>\n </>\n ) : (\n <div className=\"small text-gray-900 font-weight-bold\">\n {formatMessage(messages.priceDisplay, {\n amount: annualAmount,\n frequency: 'year',\n currency,\n })}\n </div>\n )}\n </div>\n </div>\n {showCycleToggle && (\n <div className=\"mt-2\">\n <Button\n variant=\"link\"\n className=\"cart-summary__cycle-toggle p-0\"\n onClick={() => onCycleChange?.(isMonthly ? 'annual' : 'monthly')}\n >\n {formatMessage(\n isMonthly ? messages.switchToAnnual : messages.switchToMonthly,\n )}\n </Button>\n </div>\n )}\n </Card.Section>\n\n <Card.Section className=\"d-flex justify-content-between align-items-center px-0\">\n <span className=\"font-weight-bold text-gray-900\">\n {formatMessage(messages.todaysTotal)}\n </span>\n <span className=\"font-weight-bold mb-0 text-gray-900\">\n {formatMessage(messages.todaysTotalValue)}\n </span>\n </Card.Section>\n </Card>\n\n {course && (\n <CourseEnrollmentCard course={course} variant=\"checkout\" />\n )}\n\n {/* Stats row */}\n <div className=\"cart-summary__stats d-none d-lg-flex mt-3\">\n <div className=\"cart-summary__stat d-flex align-items-center mr-4\">\n <Icon\n src={People}\n className=\"mr-2 text-muted\"\n style={{ width: '1.5rem', height: '1.5rem' }}\n />\n <div>\n <p className=\"font-weight-bold mb-0\">\n {formatMessage(messages.learnersCount)}\n </p>\n <p className=\"text-muted small mb-0\">\n {formatMessage(messages.learnersLabel)}\n </p>\n </div>\n </div>\n <div className=\"cart-summary__stat d-flex align-items-center\">\n <Icon\n src={Institution}\n className=\"mr-2 text-muted\"\n style={{ width: '1.5rem', height: '1.5rem' }}\n />\n <div>\n <p className=\"font-weight-bold mb-0\">\n {formatMessage(messages.institutionsCount)}\n </p>\n <p className=\"text-muted small mb-0\">\n {formatMessage(messages.institutionsLabel)}\n </p>\n </div>\n </div>\n </div>\n </div>\n );\n};\n\nexport default CartSummary;\n"]}
@@ -24,6 +24,16 @@ declare const messages: {
24
24
  defaultMessage: string;
25
25
  description: string;
26
26
  };
27
+ promotionalPrice: {
28
+ id: string;
29
+ defaultMessage: string;
30
+ description: string;
31
+ };
32
+ regularPrice: {
33
+ id: string;
34
+ defaultMessage: string;
35
+ description: string;
36
+ };
27
37
  priceDisplay: {
28
38
  id: string;
29
39
  defaultMessage: string;
@@ -26,10 +26,20 @@ const messages = defineMessages({
26
26
  defaultMessage: '7-day free trial',
27
27
  description: 'Free trial badge label',
28
28
  },
29
+ promotionalPrice: {
30
+ id: 'checkout.cartSummary.promotionalPrice',
31
+ defaultMessage: '${amount}/mo for {months} months',
32
+ description: 'Promotional monthly price for an introductory period e.g. $39/mo for 3 months',
33
+ },
34
+ regularPrice: {
35
+ id: 'checkout.cartSummary.regularPrice',
36
+ defaultMessage: 'then ${amount}/mo {currency}',
37
+ description: 'Regular monthly price after the promotional period e.g. then $59/mo USD',
38
+ },
29
39
  priceDisplay: {
30
40
  id: 'checkout.cartSummary.priceDisplay',
31
- defaultMessage: '${amount}/{frequency} USD',
32
- description: 'Price display e.g. $39/month USD',
41
+ defaultMessage: '${amount}/{frequency} {currency}',
42
+ description: 'Price display e.g. $299/year USD',
33
43
  },
34
44
  todaysTotal: {
35
45
  id: 'checkout.cartSummary.todaysTotal',
@@ -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,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,6BAA6B;QAC7C,WAAW,EAAE,yCAAyC;KACvD;IACD,mBAAmB,EAAE;QACnB,EAAE,EAAE,0CAA0C;QAC9C,cAAc,EAAE,sBAAsB;QACtC,WAAW,EAAE,iCAAiC;KAC/C;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,qBAAqB;QACrC,WAAW,EAAE,gCAAgC;KAC9C;IACD,UAAU,EAAE;QACV,EAAE,EAAE,iCAAiC;QACrC,cAAc,EAAE,kBAAkB;QAClC,WAAW,EAAE,wBAAwB;KACtC;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,2BAA2B;QAC3C,WAAW,EAAE,kCAAkC;KAChD;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,IAAI;QACpB,WAAW,EAAE,mCAAmC;KACjD;IACD,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,kBAAkB;QAClC,WAAW,EAAE,iDAAiD;KAC/D;IACD,eAAe,EAAE;QACf,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,mBAAmB;QACnC,WAAW,EAAE,kDAAkD;KAChE;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 learnersSubscribed: {\n id: 'checkout.cartSummary.learnersSubscribed',\n defaultMessage: '{count} learners subscribed',\n description: 'Badge showing number of active learners',\n },\n monthlySubscription: {\n id: 'checkout.cartSummary.monthlySubscription',\n defaultMessage: 'Monthly subscription',\n description: 'Label for monthly billing cycle',\n },\n annualSubscription: {\n id: 'checkout.cartSummary.annualSubscription',\n defaultMessage: 'Annual subscription',\n description: 'Label for annual billing cycle',\n },\n trialLabel: {\n id: 'checkout.cartSummary.trialLabel',\n defaultMessage: '7-day free trial',\n description: 'Free trial badge label',\n },\n priceDisplay: {\n id: 'checkout.cartSummary.priceDisplay',\n defaultMessage: '${amount}/{frequency} USD',\n description: 'Price display e.g. $39/month 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: '$0',\n description: \"Today's total amount during trial\",\n },\n switchToAnnual: {\n id: 'checkout.cartSummary.switchToAnnual',\n defaultMessage: 'Switch to annual',\n description: 'Toggle button label to switch to annual billing',\n },\n switchToMonthly: {\n id: 'checkout.cartSummary.switchToMonthly',\n defaultMessage: 'Switch to monthly',\n description: 'Toggle button label to switch to monthly billing',\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,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,6BAA6B;QAC7C,WAAW,EAAE,yCAAyC;KACvD;IACD,mBAAmB,EAAE;QACnB,EAAE,EAAE,0CAA0C;QAC9C,cAAc,EAAE,sBAAsB;QACtC,WAAW,EAAE,iCAAiC;KAC/C;IACD,kBAAkB,EAAE;QAClB,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,qBAAqB;QACrC,WAAW,EAAE,gCAAgC;KAC9C;IACD,UAAU,EAAE;QACV,EAAE,EAAE,iCAAiC;QACrC,cAAc,EAAE,kBAAkB;QAClC,WAAW,EAAE,wBAAwB;KACtC;IACD,gBAAgB,EAAE;QAChB,EAAE,EAAE,uCAAuC;QAC3C,cAAc,EAAE,kCAAkC;QAClD,WAAW,EAAE,+EAA+E;KAC7F;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,8BAA8B;QAC9C,WAAW,EAAE,yEAAyE;KACvF;IACD,YAAY,EAAE;QACZ,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,kCAAkC;QAClD,WAAW,EAAE,kCAAkC;KAChD;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,IAAI;QACpB,WAAW,EAAE,mCAAmC;KACjD;IACD,cAAc,EAAE;QACd,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,kBAAkB;QAClC,WAAW,EAAE,iDAAiD;KAC/D;IACD,eAAe,EAAE;QACf,EAAE,EAAE,sCAAsC;QAC1C,cAAc,EAAE,mBAAmB;QACnC,WAAW,EAAE,kDAAkD;KAChE;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 learnersSubscribed: {\n id: 'checkout.cartSummary.learnersSubscribed',\n defaultMessage: '{count} learners subscribed',\n description: 'Badge showing number of active learners',\n },\n monthlySubscription: {\n id: 'checkout.cartSummary.monthlySubscription',\n defaultMessage: 'Monthly subscription',\n description: 'Label for monthly billing cycle',\n },\n annualSubscription: {\n id: 'checkout.cartSummary.annualSubscription',\n defaultMessage: 'Annual subscription',\n description: 'Label for annual billing cycle',\n },\n trialLabel: {\n id: 'checkout.cartSummary.trialLabel',\n defaultMessage: '7-day free trial',\n description: 'Free trial badge label',\n },\n promotionalPrice: {\n id: 'checkout.cartSummary.promotionalPrice',\n defaultMessage: '${amount}/mo for {months} months',\n description: 'Promotional monthly price for an introductory period e.g. $39/mo for 3 months',\n },\n regularPrice: {\n id: 'checkout.cartSummary.regularPrice',\n defaultMessage: 'then ${amount}/mo {currency}',\n description: 'Regular monthly price after the promotional period e.g. then $59/mo USD',\n },\n priceDisplay: {\n id: 'checkout.cartSummary.priceDisplay',\n defaultMessage: '${amount}/{frequency} {currency}',\n description: 'Price display e.g. $299/year 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: '$0',\n description: \"Today's total amount during trial\",\n },\n switchToAnnual: {\n id: 'checkout.cartSummary.switchToAnnual',\n defaultMessage: 'Switch to annual',\n description: 'Toggle button label to switch to annual billing',\n },\n switchToMonthly: {\n id: 'checkout.cartSummary.switchToMonthly',\n defaultMessage: 'Switch to monthly',\n description: 'Toggle button label to switch to monthly billing',\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,4 +1,12 @@
1
1
  import type { UseCheckoutOptions } from '../../data/hooks/useCheckout';
2
- export type CheckoutProps = UseCheckoutOptions;
3
- declare const Checkout: (props: CheckoutProps) => import("react/jsx-runtime").JSX.Element;
2
+ export type CheckoutProps = UseCheckoutOptions & {
3
+ /** Homepage URL for the country-unavailable modal CTA. */
4
+ homeUrl?: string;
5
+ /**
6
+ * When true, learners can switch between monthly and annual billing.
7
+ * Sourced from ENABLE_ANNUAL_BILLING in site.config.*.tsx.
8
+ */
9
+ enableAnnualBilling?: boolean;
10
+ };
11
+ declare const Checkout: ({ homeUrl, enableAnnualBilling, ...props }: CheckoutProps) => import("react/jsx-runtime").JSX.Element;
4
12
  export default Checkout;
@@ -1,16 +1,45 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
1
12
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
13
+ import { useEffect } from 'react';
2
14
  import { Alert, Col, Row } from '@openedx/paragon';
3
15
  import { Error as ErrorIcon } from '@openedx/paragon/icons';
4
16
  import useCheckout from '../../data/hooks/useCheckout';
17
+ import { ALLOWED_CHECKOUT_COUNTRIES, GATE_CHECK_COUNTRY } from '../../constants';
5
18
  import CartSummary from '../CartSummary/CartSummary';
6
19
  import EnrollmentSummary from '../EnrollmentSummary/EnrollmentSummary';
7
20
  import PaymentForm from '../PaymentForm/PaymentForm';
21
+ import CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';
8
22
  import { getPlanPrice, getRenewalLabel } from '../../utils/billingDetails';
9
23
  import { getTrialDateRange } from '../../utils';
10
24
  import { SuccessScreen, BlockedScreen } from '../StatusScreens/StatusScreens';
11
- const Checkout = (props) => {
12
- var _a, _b;
25
+ /** Resolve purchased cycle from subscription details, falling back to checkout selection. */
26
+ function resolvePurchasedCycle(selectedCycle, planName, renewalType) {
27
+ const haystack = `${planName !== null && planName !== void 0 ? planName : ''} ${renewalType !== null && renewalType !== void 0 ? renewalType : ''}`.toLowerCase();
28
+ if (haystack.includes('annual') || haystack.includes('year'))
29
+ return 'annual';
30
+ if (haystack.includes('month'))
31
+ return 'monthly';
32
+ return selectedCycle;
33
+ }
34
+ const Checkout = (_a) => {
35
+ var _b, _c, _d, _e, _f;
36
+ var { homeUrl = '/', enableAnnualBilling = false } = _a, props = __rest(_a, ["homeUrl", "enableAnnualBilling"]);
13
37
  const c = useCheckout(props);
38
+ useEffect(() => {
39
+ if (!enableAnnualBilling && c.selectedCycle !== 'monthly') {
40
+ c.setCycle('monthly');
41
+ }
42
+ }, [enableAnnualBilling, c.selectedCycle, c.setCycle]);
14
43
  const showForm = c.phase === 'init'
15
44
  || c.phase === 'ready'
16
45
  || c.phase === 'failed'
@@ -19,6 +48,15 @@ const Checkout = (props) => {
19
48
  const annualPrice = getPlanPrice(c.billingPlans, 'annual');
20
49
  const isSubmitting = c.processingSubscription;
21
50
  const { displayStart, displayEnd } = getTrialDateRange();
51
+ const isBillingCountryBlocked = GATE_CHECK_COUNTRY
52
+ && !!c.billing.country
53
+ && !ALLOWED_CHECKOUT_COUNTRIES.includes(c.billing.country);
54
+ // Keep annual selection only when the feature flag is on.
55
+ const selectedCycle = enableAnnualBilling
56
+ ? c.selectedCycle
57
+ : 'monthly';
58
+ const purchasedCycle = resolvePurchasedCycle(selectedCycle, (_b = c.subscriptionDetails) === null || _b === void 0 ? void 0 : _b.planName, (_c = c.subscriptionDetails) === null || _c === void 0 ? void 0 : _c.renewalType);
59
+ const purchasedPrice = getPlanPrice(c.billingPlans, purchasedCycle);
22
60
  const handleStartCourse = () => {
23
61
  var _a;
24
62
  if ((_a = c.course) === null || _a === void 0 ? void 0 : _a.tUrlCourse) {
@@ -31,10 +69,10 @@ const Checkout = (props) => {
31
69
  const courseLabel = c.course
32
70
  ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')
33
71
  : null;
34
- return (_jsxs(Row, { className: "checkout__container bg-light-200", children: [_jsx(Col, { xs: 12, className: "d-lg-none", children: _jsx("h1", { className: "font-weight-bold text-primary-900", children: "Checkout" }) }), _jsx(Col, { xs: 12, children: _jsxs(Row, { className: "flex-column-reverse gap-5 flex-lg-row", children: [_jsx(Col, { xs: 12, lg: 7, xxl: 9, className: "payment__options", children: _jsxs("div", { children: [_jsx("h1", { className: "font-weight-bold text-primary-900 d-none d-lg-block", children: "Checkout" }), c.error && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, dismissible: true, onClose: c.clearError, children: c.error })), showForm && (_jsxs(_Fragment, { children: [c.phase === 'failed' && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, className: "mb-3", children: c.lastComment
35
- || 'Your payment could not be processed. Please check your card details and try again.' })), _jsx(PaymentForm, { billing: c.billing, setField: c.setField, numberMountRef: c.numberMountRef, expiryMountRef: c.expiryMountRef, cvcMountRef: c.cvcMountRef, cardError: c.cardError, stripeReady: c.stripeReady, sessionReady: c.sessionReady, submitting: isSubmitting, onPlaceOrder: c.placeOrder, trialEnd: displayEnd, priceToShow: c.selectedCycle === 'monthly' ? monthlyPrice : annualPrice, cycleToShow: c.selectedCycle === 'monthly' ? 'monthly' : 'annual', userCountry: c.userCountry })] })), c.phase === 'success' && (_jsx(SuccessScreen, { subscriptionBiId: ((_a = c.subscriptionDetails) === null || _a === void 0 ? void 0 : _a.biId) || '', cardLast4: ((_b = c.subscriptionDetails) === null || _b === void 0 ? void 0 : _b.cardLast4) || '', trialStart: displayStart, trialEnd: displayEnd, priceToShow: c.selectedCycle === 'monthly'
72
+ return (_jsxs(Row, { className: "checkout__container bg-light-200", children: [_jsx(CountryUnavailableModal, { isOpen: isBillingCountryBlocked, homeUrl: homeUrl }), _jsx(Col, { xs: 12, className: "d-lg-none", children: _jsx("h1", { className: "font-weight-bold text-primary-900", children: "Checkout" }) }), _jsx(Col, { xs: 12, children: _jsxs(Row, { className: "flex-column-reverse gap-5 flex-lg-row", children: [_jsx(Col, { xs: 12, lg: 7, xxl: 9, className: "payment__options", children: _jsxs("div", { children: [_jsx("h1", { className: "font-weight-bold text-primary-900 d-none d-lg-block", children: "Checkout" }), c.error && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, dismissible: true, onClose: c.clearError, children: c.error })), showForm && (_jsxs(_Fragment, { children: [c.phase === 'failed' && (_jsx(Alert, { variant: "danger", icon: ErrorIcon, className: "mb-3", children: c.lastComment
73
+ || 'Your payment could not be processed. Please check your card details and try again.' })), _jsx(PaymentForm, { billing: c.billing, setField: c.setField, numberMountRef: c.numberMountRef, expiryMountRef: c.expiryMountRef, cvcMountRef: c.cvcMountRef, cardError: c.cardError, stripeReady: c.stripeReady, sessionReady: c.sessionReady, submitting: isSubmitting, onPlaceOrder: c.placeOrder, trialEnd: displayEnd, priceToShow: selectedCycle === 'monthly' ? monthlyPrice : annualPrice, cycleToShow: selectedCycle === 'monthly' ? 'monthly' : 'annual', userCountry: c.userCountry })] })), c.phase === 'success' && (_jsx(SuccessScreen, { subscriptionBiId: ((_d = c.subscriptionDetails) === null || _d === void 0 ? void 0 : _d.biId) || '', cardLast4: ((_e = c.subscriptionDetails) === null || _e === void 0 ? void 0 : _e.cardLast4) || '', purchaseDate: displayStart, trialStart: displayStart, trialEnd: displayEnd, priceToShow: purchasedCycle === 'monthly'
36
74
  ? monthlyPrice === null || monthlyPrice === void 0 ? void 0 : monthlyPrice.label
37
- : annualPrice === null || annualPrice === void 0 ? void 0 : annualPrice.label, cycleToShow: c.selectedCycle === 'monthly' ? 'monthly' : 'annual', courseLabel: courseLabel, onFindCourse: handleStartCourse })), c.phase === 'blocked' && (_jsx(BlockedScreen, { detail: c.lastComment }))] }) }), _jsx(Col, { xs: 12, lg: 5, xxl: 3, className: "payment__summary", children: c.phase === 'success' ? (_jsx(EnrollmentSummary, { course: c.course, onStartCourse: handleStartCourse })) : (_jsx(CartSummary, { monthlyRenewalLabel: getRenewalLabel(c.billingPlans, 'monthly'), annualRenewalLabel: getRenewalLabel(c.billingPlans, 'annual'), activeSubscriberCount: c.activeSubscriberCount, defaultCycle: c.selectedCycle, onCycleChange: c.setCycle, course: c.course })) })] }) })] }));
75
+ : annualPrice === null || annualPrice === void 0 ? void 0 : annualPrice.label, cycleToShow: purchasedCycle === 'monthly' ? 'monthly' : 'annual', courseLabel: courseLabel, onFindCourse: handleStartCourse })), c.phase === 'blocked' && (_jsx(BlockedScreen, { detail: c.lastComment }))] }) }), _jsx(Col, { xs: 12, lg: 5, xxl: 3, className: "payment__summary", children: c.phase === 'success' ? (_jsx(EnrollmentSummary, { amount: purchasedPrice === null || purchasedPrice === void 0 ? void 0 : purchasedPrice.amount, currency: purchasedPrice === null || purchasedPrice === void 0 ? void 0 : purchasedPrice.currency, cycleToShow: purchasedCycle, course: c.course, onStartCourse: handleStartCourse })) : (_jsx(CartSummary, { monthlyRegularAmount: monthlyPrice === null || monthlyPrice === void 0 ? void 0 : monthlyPrice.amount, annualAmount: annualPrice === null || annualPrice === void 0 ? void 0 : annualPrice.amount, currency: (_f = monthlyPrice === null || monthlyPrice === void 0 ? void 0 : monthlyPrice.currency) !== null && _f !== void 0 ? _f : annualPrice === null || annualPrice === void 0 ? void 0 : annualPrice.currency, monthlyRenewalLabel: getRenewalLabel(c.billingPlans, 'monthly'), annualRenewalLabel: getRenewalLabel(c.billingPlans, 'annual'), activeSubscriberCount: c.activeSubscriberCount, defaultCycle: selectedCycle, onCycleChange: c.setCycle, enableAnnualBilling: enableAnnualBilling, course: c.course })) })] }) })] }));
38
76
  };
39
77
  export default Checkout;
40
78
  //# sourceMappingURL=Checkout.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport { Alert, Col, Row } from '@openedx/paragon';\nimport { Error as ErrorIcon } from '@openedx/paragon/icons';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport CartSummary from '../CartSummary/CartSummary';\nimport EnrollmentSummary from '../EnrollmentSummary/EnrollmentSummary';\nimport PaymentForm from '../PaymentForm/PaymentForm';\nimport { getPlanPrice, getRenewalLabel } from '../../utils/billingDetails';\nimport { getTrialDateRange } from '@src/utils';\nimport { SuccessScreen, BlockedScreen } from '../StatusScreens/StatusScreens';\n\nexport type CheckoutProps = UseCheckoutOptions;\n\nconst Checkout = (props: CheckoutProps) => {\n const c = useCheckout(props);\n\n const showForm\n = c.phase === 'init'\n || c.phase === 'ready'\n || c.phase === 'failed'\n || c.phase === 'processing';\n const monthlyPrice = getPlanPrice(c.billingPlans, 'monthly');\n const annualPrice = getPlanPrice(c.billingPlans, 'annual');\n const isSubmitting = c.processingSubscription;\n\n const { displayStart, displayEnd } = getTrialDateRange();\n\n const handleStartCourse = () => {\n if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = '/subscription-search';\n }\n };\n\n const courseLabel = c.course\n ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')\n : null;\n\n return (\n <Row className=\"checkout__container bg-light-200\">\n <Col xs={12} className=\"d-lg-none\">\n <h1 className=\"font-weight-bold text-primary-900\">Checkout</h1>\n </Col>\n <Col xs={12}>\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\">\n Checkout\n </h1>\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 {showForm && (\n <>\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {c.lastComment\n || 'Your payment could not be processed. Please check your card details and try again.'}\n </Alert>\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 stripeReady={c.stripeReady}\n sessionReady={c.sessionReady}\n submitting={isSubmitting}\n onPlaceOrder={c.placeOrder}\n trialEnd={displayEnd}\n priceToShow={\n c.selectedCycle === 'monthly' ? monthlyPrice : annualPrice\n }\n cycleToShow={\n c.selectedCycle === 'monthly' ? 'monthly' : 'annual'\n }\n userCountry={c.userCountry}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n trialStart={displayStart}\n trialEnd={displayEnd}\n priceToShow={\n c.selectedCycle === 'monthly'\n ? monthlyPrice?.label\n : annualPrice?.label\n }\n cycleToShow={\n c.selectedCycle === 'monthly' ? 'monthly' : 'annual'\n }\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 monthlyRenewalLabel={getRenewalLabel(c.billingPlans, 'monthly')}\n annualRenewalLabel={getRenewalLabel(c.billingPlans, 'annual')}\n activeSubscriberCount={c.activeSubscriberCount}\n defaultCycle={c.selectedCycle}\n onCycleChange={c.setCycle}\n course={c.course}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n );\n};\n\nexport default Checkout;\n"]}
1
+ {"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,kBAAkB,CAAC;AACnD,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,WAAW,MAAM,8BAA8B,CAAC;AAEvD,OAAO,EAAE,0BAA0B,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AACjF,OAAO,WAAW,MAAM,4BAA4B,CAAC;AACrD,OAAO,iBAAiB,MAAM,wCAAwC,CAAC;AACvE,OAAO,WAAW,MAAM,4BAA4B,CAAC;AACrD,OAAO,uBAAuB,MAAM,oDAAoD,CAAC;AACzF,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG9E,6FAA6F;AAC7F,SAAS,qBAAqB,CAC5B,aAA2B,EAC3B,QAAwB,EACxB,WAA2B;IAE3B,MAAM,QAAQ,GAAG,GAAG,QAAQ,aAAR,QAAQ,cAAR,QAAQ,GAAI,EAAE,IAAI,WAAW,aAAX,WAAW,cAAX,WAAW,GAAI,EAAE,EAAE,CAAC,WAAW,EAAE,CAAC;IACxE,IAAI,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC9E,IAAI,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,SAAS,CAAC;IACjD,OAAO,aAAa,CAAC;AACvB,CAAC;AAYD,MAAM,QAAQ,GAAG,CAAC,EAIF,EAAE,EAAE;;QAJF,EAChB,OAAO,GAAG,GAAG,EACb,mBAAmB,GAAG,KAAK,OAEb,EADX,KAAK,cAHQ,kCAIjB,CADS;IAER,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAE7B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,mBAAmB,IAAI,CAAC,CAAC,aAAa,KAAK,SAAS,EAAE,CAAC;YAC1D,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;QACxB,CAAC;IACH,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,CAAC,aAAa,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEvD,MAAM,QAAQ,GACV,CAAC,CAAC,KAAK,KAAK,MAAM;WACf,CAAC,CAAC,KAAK,KAAK,OAAO;WACnB,CAAC,CAAC,KAAK,KAAK,QAAQ;WACpB,CAAC,CAAC,KAAK,KAAK,YAAY,CAAC;IAChC,MAAM,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;IAC7D,MAAM,WAAW,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IAC3D,MAAM,YAAY,GAAG,CAAC,CAAC,sBAAsB,CAAC;IAE9C,MAAM,EAAE,YAAY,EAAE,UAAU,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAEzD,MAAM,uBAAuB,GAAG,kBAAkB;WAC7C,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO;WACnB,CAAC,0BAA0B,CAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAE7D,0DAA0D;IAC1D,MAAM,aAAa,GAAiB,mBAAmB;QACrD,CAAC,CAAC,CAAC,CAAC,aAAa;QACjB,CAAC,CAAC,SAAS,CAAC;IAEd,MAAM,cAAc,GAAG,qBAAqB,CAC1C,aAAa,EACb,MAAA,CAAC,CAAC,mBAAmB,0CAAE,QAAQ,EAC/B,MAAA,CAAC,CAAC,mBAAmB,0CAAE,WAAW,CACnC,CAAC;IACF,MAAM,cAAc,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,EAAE,cAAc,CAAC,CAAC;IAEpE,MAAM,iBAAiB,GAAG,GAAG,EAAE;;QAC7B,IAAI,MAAA,CAAC,CAAC,MAAM,0CAAE,UAAU,EAAE,CAAC;YACzB,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC;QAC7C,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,sBAAsB,CAAC;QAChD,CAAC;IACH,CAAC,CAAC;IAEF,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM;QAC1B,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;QACnE,CAAC,CAAC,IAAI,CAAC;IAET,OAAO,CACL,MAAC,GAAG,IAAC,SAAS,EAAC,kCAAkC,aAC/C,KAAC,uBAAuB,IACtB,MAAM,EAAE,uBAAuB,EAC/B,OAAO,EAAE,OAAO,GAChB,EACF,KAAC,GAAG,IAAC,EAAE,EAAE,EAAE,EAAE,SAAS,EAAC,WAAW,YAChC,aAAI,SAAS,EAAC,mCAAmC,yBAAc,GAC3D,EACN,KAAC,GAAG,IAAC,EAAE,EAAE,EAAE,YACT,MAAC,GAAG,IAAC,SAAS,EAAC,uCAAuC,aAEpD,KAAC,GAAG,IAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,SAAS,EAAC,kBAAkB,YACtD,0BACE,aAAI,SAAS,EAAC,qDAAqD,yBAE9D,EACJ,CAAC,CAAC,KAAK,IAAI,CACV,KAAC,KAAK,IACJ,OAAO,EAAC,QAAQ,EAChB,IAAI,EAAE,SAAS,EACf,WAAW,QACX,OAAO,EAAE,CAAC,CAAC,UAAU,YAEpB,CAAC,CAAC,KAAK,GACF,CACT,EAEA,QAAQ,IAAI,CACX,8BACG,CAAC,CAAC,KAAK,KAAK,QAAQ,IAAI,CACvB,KAAC,KAAK,IAAC,OAAO,EAAC,QAAQ,EAAC,IAAI,EAAE,SAAS,EAAE,SAAS,EAAC,MAAM,YACtD,CAAC,CAAC,WAAW;uDACX,oFAAoF,GACjF,CACT,EACD,KAAC,WAAW,IACV,OAAO,EAAE,CAAC,CAAC,OAAO,EAClB,QAAQ,EAAE,CAAC,CAAC,QAAQ,EACpB,cAAc,EAAE,CAAC,CAAC,cAAc,EAChC,cAAc,EAAE,CAAC,CAAC,cAAc,EAChC,WAAW,EAAE,CAAC,CAAC,WAAW,EAC1B,SAAS,EAAE,CAAC,CAAC,SAAS,EACtB,WAAW,EAAE,CAAC,CAAC,WAAW,EAC1B,YAAY,EAAE,CAAC,CAAC,YAAY,EAC5B,UAAU,EAAE,YAAY,EACxB,YAAY,EAAE,CAAC,CAAC,UAAU,EAC1B,QAAQ,EAAE,UAAU,EACpB,WAAW,EACT,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,WAAW,EAE1D,WAAW,EACT,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAEpD,WAAW,EAAE,CAAC,CAAC,WAAW,GAC1B,IACD,CACJ,EAEA,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CACxB,KAAC,aAAa,IACZ,gBAAgB,EAAE,CAAA,MAAA,CAAC,CAAC,mBAAmB,0CAAE,IAAI,KAAI,EAAE,EACnD,SAAS,EAAE,CAAA,MAAA,CAAC,CAAC,mBAAmB,0CAAE,SAAS,KAAI,EAAE,EACjD,YAAY,EAAE,YAAY,EAC1B,UAAU,EAAE,YAAY,EACxB,QAAQ,EAAE,UAAU,EACpB,WAAW,EACT,cAAc,KAAK,SAAS;4CAC1B,CAAC,CAAC,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,KAAK;4CACrB,CAAC,CAAC,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,KAAK,EAExB,WAAW,EACT,cAAc,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAErD,WAAW,EAAE,WAAW,EACxB,YAAY,EAAE,iBAAiB,GAC/B,CACH,EAEA,CAAC,CAAC,KAAK,KAAK,SAAS,IAAI,CACxB,KAAC,aAAa,IAAC,MAAM,EAAE,CAAC,CAAC,WAAW,GAAI,CACzC,IACG,GACF,EAGN,KAAC,GAAG,IAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,SAAS,EAAC,kBAAkB,YACrD,CAAC,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CACvB,KAAC,iBAAiB,IAChB,MAAM,EAAE,cAAc,aAAd,cAAc,uBAAd,cAAc,CAAE,MAAM,EAC9B,QAAQ,EAAE,cAAc,aAAd,cAAc,uBAAd,cAAc,CAAE,QAAQ,EAClC,WAAW,EAAE,cAAc,EAC3B,MAAM,EAAE,CAAC,CAAC,MAAM,EAChB,aAAa,EAAE,iBAAiB,GAChC,CACH,CAAC,CAAC,CAAC,CACF,KAAC,WAAW,IACV,oBAAoB,EAAE,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,MAAM,EAC1C,YAAY,EAAE,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,MAAM,EACjC,QAAQ,EAAE,MAAA,YAAY,aAAZ,YAAY,uBAAZ,YAAY,CAAE,QAAQ,mCAAI,WAAW,aAAX,WAAW,uBAAX,WAAW,CAAE,QAAQ,EACzD,mBAAmB,EAAE,eAAe,CAAC,CAAC,CAAC,YAAY,EAAE,SAAS,CAAC,EAC/D,kBAAkB,EAAE,eAAe,CAAC,CAAC,CAAC,YAAY,EAAE,QAAQ,CAAC,EAC7D,qBAAqB,EAAE,CAAC,CAAC,qBAAqB,EAC9C,YAAY,EAAE,aAAa,EAC3B,aAAa,EAAE,CAAC,CAAC,QAAQ,EACzB,mBAAmB,EAAE,mBAAmB,EACxC,MAAM,EAAE,CAAC,CAAC,MAAM,GAChB,CACH,GACG,IACF,GACF,IACF,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,QAAQ,CAAC","sourcesContent":["import React, { useEffect } from 'react';\nimport { Alert, Col, Row } from '@openedx/paragon';\nimport { Error as ErrorIcon } from '@openedx/paragon/icons';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\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 { getPlanPrice, getRenewalLabel } from '../../utils/billingDetails';\nimport { getTrialDateRange } from '@src/utils';\nimport { SuccessScreen, BlockedScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle } from '../../types';\n\n/** Resolve purchased cycle from subscription details, falling back to checkout selection. */\nfunction resolvePurchasedCycle(\n selectedCycle: BillingCycle,\n planName?: string | null,\n renewalType?: string | null,\n): BillingCycle {\n const haystack = `${planName ?? ''} ${renewalType ?? ''}`.toLowerCase();\n if (haystack.includes('annual') || haystack.includes('year')) return 'annual';\n if (haystack.includes('month')) return 'monthly';\n return selectedCycle;\n}\n\nexport type CheckoutProps = UseCheckoutOptions & {\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n /**\n * When true, learners can switch between monthly and annual billing.\n * Sourced from ENABLE_ANNUAL_BILLING in site.config.*.tsx.\n */\n enableAnnualBilling?: boolean,\n};\n\nconst Checkout = ({\n homeUrl = '/',\n enableAnnualBilling = false,\n ...props\n}: CheckoutProps) => {\n const c = useCheckout(props);\n\n useEffect(() => {\n if (!enableAnnualBilling && c.selectedCycle !== 'monthly') {\n c.setCycle('monthly');\n }\n }, [enableAnnualBilling, c.selectedCycle, c.setCycle]);\n\n const showForm\n = c.phase === 'init'\n || c.phase === 'ready'\n || c.phase === 'failed'\n || c.phase === 'processing';\n const monthlyPrice = getPlanPrice(c.billingPlans, 'monthly');\n const annualPrice = getPlanPrice(c.billingPlans, 'annual');\n const isSubmitting = c.processingSubscription;\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 // Keep annual selection only when the feature flag is on.\n const selectedCycle: BillingCycle = enableAnnualBilling\n ? c.selectedCycle\n : 'monthly';\n\n const purchasedCycle = resolvePurchasedCycle(\n selectedCycle,\n c.subscriptionDetails?.planName,\n c.subscriptionDetails?.renewalType,\n );\n const purchasedPrice = getPlanPrice(c.billingPlans, purchasedCycle);\n\n const handleStartCourse = () => {\n if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = '/subscription-search';\n }\n };\n\n const courseLabel = c.course\n ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')\n : null;\n\n return (\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 <Col xs={12}>\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\">\n Checkout\n </h1>\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 {showForm && (\n <>\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {c.lastComment\n || 'Your payment could not be processed. Please check your card details and try again.'}\n </Alert>\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 stripeReady={c.stripeReady}\n sessionReady={c.sessionReady}\n submitting={isSubmitting}\n onPlaceOrder={c.placeOrder}\n trialEnd={displayEnd}\n priceToShow={\n selectedCycle === 'monthly' ? monthlyPrice : annualPrice\n }\n cycleToShow={\n selectedCycle === 'monthly' ? 'monthly' : 'annual'\n }\n userCountry={c.userCountry}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n trialStart={displayStart}\n trialEnd={displayEnd}\n priceToShow={\n purchasedCycle === 'monthly'\n ? monthlyPrice?.label\n : annualPrice?.label\n }\n cycleToShow={\n purchasedCycle === 'monthly' ? 'monthly' : 'annual'\n }\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 amount={purchasedPrice?.amount}\n currency={purchasedPrice?.currency}\n cycleToShow={purchasedCycle}\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n monthlyRegularAmount={monthlyPrice?.amount}\n annualAmount={annualPrice?.amount}\n currency={monthlyPrice?.currency ?? annualPrice?.currency}\n monthlyRenewalLabel={getRenewalLabel(c.billingPlans, 'monthly')}\n annualRenewalLabel={getRenewalLabel(c.billingPlans, 'annual')}\n activeSubscriberCount={c.activeSubscriberCount}\n defaultCycle={selectedCycle}\n onCycleChange={c.setCycle}\n enableAnnualBilling={enableAnnualBilling}\n course={c.course}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n );\n};\n\nexport default Checkout;\n"]}
@@ -1,7 +1,9 @@
1
1
  import type { CheckoutProps } from '../Checkout/Checkout';
2
2
  export interface CheckoutGateProps extends CheckoutProps {
3
- /** URL linked in the "not available in your country" message. */
3
+ /** URL linked in session-error recovery messaging. */
4
4
  learnerDashboardUrl?: string;
5
+ /** Homepage URL for the country-unavailable modal CTA. */
6
+ homeUrl?: string;
5
7
  }
6
- declare const CheckoutGate: ({ learnerDashboardUrl, ...checkoutProps }: CheckoutGateProps) => import("react/jsx-runtime").JSX.Element;
8
+ declare const CheckoutGate: ({ learnerDashboardUrl, homeUrl, ...checkoutProps }: CheckoutGateProps) => import("react/jsx-runtime").JSX.Element;
7
9
  export default CheckoutGate;
@@ -9,13 +9,14 @@ var __rest = (this && this.__rest) || function (s, e) {
9
9
  }
10
10
  return t;
11
11
  };
12
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
13
13
  import { Alert, Spinner } from '@openedx/paragon';
14
14
  import { Error as ErrorIcon } from '@openedx/paragon/icons';
15
15
  import { useCheckoutGate } from '../../data/hooks/useCheckoutGate';
16
16
  import Checkout from '../Checkout/Checkout';
17
+ import CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';
17
18
  const CheckoutGate = (_a) => {
18
- var { learnerDashboardUrl } = _a, checkoutProps = __rest(_a, ["learnerDashboardUrl"]);
19
+ var { learnerDashboardUrl, homeUrl = '/' } = _a, checkoutProps = __rest(_a, ["learnerDashboardUrl", "homeUrl"]);
19
20
  const { gateStatus, userCountry, errorMessage } = useCheckoutGate({
20
21
  edxSessionUrl: checkoutProps.edxSessionUrl,
21
22
  learnerDashboardUrl,
@@ -27,9 +28,9 @@ const CheckoutGate = (_a) => {
27
28
  return (_jsxs(Alert, { variant: "danger", icon: ErrorIcon, children: [errorMessage !== null && errorMessage !== void 0 ? errorMessage : 'We\u2019re having trouble loading checkout. Please refresh and try again.', ' ', learnerDashboardUrl && (_jsx("a", { href: learnerDashboardUrl, className: "alert-link", children: "Go to your learner dashboard" }))] }));
28
29
  }
29
30
  if (gateStatus === 'blocked') {
30
- return (_jsxs(Alert, { variant: "danger", icon: ErrorIcon, children: ["Subscription is not available in your country.", ' ', learnerDashboardUrl && (_jsx("a", { href: learnerDashboardUrl, className: "alert-link", children: "Go to your learner dashboard" }))] }));
31
+ return (_jsx(_Fragment, { children: _jsx(CountryUnavailableModal, { isOpen: true, homeUrl: homeUrl }) }));
31
32
  }
32
- return _jsx(Checkout, Object.assign({}, checkoutProps, { userCountry: userCountry }));
33
+ return (_jsx(Checkout, Object.assign({}, checkoutProps, { userCountry: userCountry, homeUrl: homeUrl })));
33
34
  };
34
35
  export default CheckoutGate;
35
36
  //# sourceMappingURL=CheckoutGate.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CheckoutGate.js","sourceRoot":"","sources":["../../../src/components/CheckoutGate/CheckoutGate.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,QAAQ,MAAM,sBAAsB,CAAC;AAQ5C,MAAM,YAAY,GAAG,CAAC,EAGF,EAAE,EAAE;QAHF,EACpB,mBAAmB,OAED,EADf,aAAa,cAFI,uBAGrB,CADiB;IAEhB,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,eAAe,CAAC;QAChE,aAAa,EAAE,aAAa,CAAC,aAAa;QAC1C,mBAAmB;KACpB,CAAC,CAAC;IAEH,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,CACL,cACE,SAAS,EAAC,kDAAkD,EAC5D,KAAK,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,YAE7B,KAAC,OAAO,IAAC,SAAS,EAAC,QAAQ,EAAC,gBAAgB,EAAC,6BAAwB,GAAG,GACpE,CACP,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;QAC3B,OAAO,CACL,MAAC,KAAK,IAAC,OAAO,EAAC,QAAQ,EAAC,IAAI,EAAE,SAAS,aACpC,YAAY,aAAZ,YAAY,cAAZ,YAAY,GACV,2EAA2E,EAAE,GAAG,EAClF,mBAAmB,IAAI,CACtB,YAAG,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAC,YAAY,6CAEhD,CACL,IACK,CACT,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,CACL,MAAC,KAAK,IAAC,OAAO,EAAC,QAAQ,EAAC,IAAI,EAAE,SAAS,+DACU,GAAG,EACjD,mBAAmB,IAAI,CACtB,YAAG,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAC,YAAY,6CAEhD,CACL,IACK,CACT,CAAC;IACJ,CAAC;IAED,OAAO,KAAC,QAAQ,oBAAK,aAAa,IAAE,WAAW,EAAE,WAAW,IAAI,CAAC;AACnE,CAAC,CAAC;AAEF,eAAe,YAAY,CAAC","sourcesContent":["import { Alert, Spinner } from '@openedx/paragon';\nimport { Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useCheckoutGate } from '../../data/hooks/useCheckoutGate';\nimport Checkout from '../Checkout/Checkout';\nimport type { CheckoutProps } from '../Checkout/Checkout';\n\nexport interface CheckoutGateProps extends CheckoutProps {\n /** URL linked in the \"not available in your country\" message. */\n learnerDashboardUrl?: string,\n}\n\nconst CheckoutGate = ({\n learnerDashboardUrl,\n ...checkoutProps\n}: CheckoutGateProps) => {\n const { gateStatus, userCountry, errorMessage } = useCheckoutGate({\n edxSessionUrl: checkoutProps.edxSessionUrl,\n learnerDashboardUrl,\n });\n\n if (gateStatus === 'loading') {\n return (\n <div\n className=\"d-flex justify-content-center align-items-center\"\n style={{ minHeight: '100vh' }}\n >\n <Spinner animation=\"border\" screenReaderText=\"Checking your account…\" />\n </div>\n );\n }\n\n if (gateStatus === 'error') {\n return (\n <Alert variant=\"danger\" icon={ErrorIcon}>\n {errorMessage\n ?? 'We\\u2019re having trouble loading checkout. Please refresh and try again.'}{' '}\n {learnerDashboardUrl && (\n <a href={learnerDashboardUrl} className=\"alert-link\">\n Go to your learner dashboard\n </a>\n )}\n </Alert>\n );\n }\n\n if (gateStatus === 'blocked') {\n return (\n <Alert variant=\"danger\" icon={ErrorIcon}>\n Subscription is not available in your country.{' '}\n {learnerDashboardUrl && (\n <a href={learnerDashboardUrl} className=\"alert-link\">\n Go to your learner dashboard\n </a>\n )}\n </Alert>\n );\n }\n\n return <Checkout {...checkoutProps} userCountry={userCountry} />;\n};\n\nexport default CheckoutGate;\n"]}
1
+ {"version":3,"file":"CheckoutGate.js","sourceRoot":"","sources":["../../../src/components/CheckoutGate/CheckoutGate.tsx"],"names":[],"mappings":";;;;;;;;;;;;AAAA,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACnE,OAAO,QAAQ,MAAM,sBAAsB,CAAC;AAC5C,OAAO,uBAAuB,MAAM,oDAAoD,CAAC;AAUzF,MAAM,YAAY,GAAG,CAAC,EAIF,EAAE,EAAE;QAJF,EACpB,mBAAmB,EACnB,OAAO,GAAG,GAAG,OAEK,EADf,aAAa,cAHI,kCAIrB,CADiB;IAEhB,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,GAAG,eAAe,CAAC;QAChE,aAAa,EAAE,aAAa,CAAC,aAAa;QAC1C,mBAAmB;KACpB,CAAC,CAAC;IAEH,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,CACL,cACE,SAAS,EAAC,kDAAkD,EAC5D,KAAK,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,YAE7B,KAAC,OAAO,IAAC,SAAS,EAAC,QAAQ,EAAC,gBAAgB,EAAC,6BAAwB,GAAG,GACpE,CACP,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,KAAK,OAAO,EAAE,CAAC;QAC3B,OAAO,CACL,MAAC,KAAK,IAAC,OAAO,EAAC,QAAQ,EAAC,IAAI,EAAE,SAAS,aACpC,YAAY,aAAZ,YAAY,cAAZ,YAAY,GACV,2EAA2E,EAAE,GAAG,EAClF,mBAAmB,IAAI,CACtB,YAAG,IAAI,EAAE,mBAAmB,EAAE,SAAS,EAAC,YAAY,6CAEhD,CACL,IACK,CACT,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,KAAK,SAAS,EAAE,CAAC;QAC7B,OAAO,CACL,4BACE,KAAC,uBAAuB,IAAC,MAAM,QAAC,OAAO,EAAE,OAAO,GAAI,GACnD,CACJ,CAAC;IACJ,CAAC;IAED,OAAO,CACL,KAAC,QAAQ,oBACH,aAAa,IACjB,WAAW,EAAE,WAAW,EACxB,OAAO,EAAE,OAAO,IAChB,CACH,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,YAAY,CAAC","sourcesContent":["import { Alert, Spinner } from '@openedx/paragon';\nimport { Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useCheckoutGate } from '../../data/hooks/useCheckoutGate';\nimport Checkout from '../Checkout/Checkout';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport type { CheckoutProps } from '../Checkout/Checkout';\n\nexport interface CheckoutGateProps extends CheckoutProps {\n /** URL linked in session-error recovery messaging. */\n learnerDashboardUrl?: string,\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n}\n\nconst CheckoutGate = ({\n learnerDashboardUrl,\n homeUrl = '/',\n ...checkoutProps\n}: CheckoutGateProps) => {\n const { gateStatus, userCountry, errorMessage } = useCheckoutGate({\n edxSessionUrl: checkoutProps.edxSessionUrl,\n learnerDashboardUrl,\n });\n\n if (gateStatus === 'loading') {\n return (\n <div\n className=\"d-flex justify-content-center align-items-center\"\n style={{ minHeight: '100vh' }}\n >\n <Spinner animation=\"border\" screenReaderText=\"Checking your account…\" />\n </div>\n );\n }\n\n if (gateStatus === 'error') {\n return (\n <Alert variant=\"danger\" icon={ErrorIcon}>\n {errorMessage\n ?? 'We\\u2019re having trouble loading checkout. Please refresh and try again.'}{' '}\n {learnerDashboardUrl && (\n <a href={learnerDashboardUrl} className=\"alert-link\">\n Go to your learner dashboard\n </a>\n )}\n </Alert>\n );\n }\n\n if (gateStatus === 'blocked') {\n return (\n <>\n <CountryUnavailableModal isOpen homeUrl={homeUrl} />\n </>\n );\n }\n\n return (\n <Checkout\n {...checkoutProps}\n userCountry={userCountry}\n homeUrl={homeUrl}\n />\n );\n};\n\nexport default CheckoutGate;\n"]}
@@ -0,0 +1,9 @@
1
+ export interface CountryUnavailableModalProps {
2
+ isOpen: boolean;
3
+ /** Homepage URL for the "Return to home" action. Defaults to "/". */
4
+ homeUrl?: string;
5
+ /** Optional close handler; ignored when the modal is blocking. */
6
+ onClose?: () => void;
7
+ }
8
+ declare const CountryUnavailableModal: ({ isOpen, homeUrl, onClose, }: CountryUnavailableModalProps) => import("react/jsx-runtime").JSX.Element;
9
+ export default CountryUnavailableModal;
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ActionRow, Button, StandardModal } from '@openedx/paragon';
3
+ import { useIntl } from '@openedx/frontend-base';
4
+ import messages from './Messages';
5
+ const CountryUnavailableModal = ({ isOpen, homeUrl = '/', onClose = () => { }, }) => {
6
+ const { formatMessage } = useIntl();
7
+ return (_jsx(StandardModal, { title: formatMessage(messages.title), isOpen: isOpen, isBlocking: true, hasCloseButton: false, onClose: onClose, size: "md", footerNode: (_jsxs(ActionRow, { children: [_jsx(ActionRow.Spacer, {}), _jsx(Button, { variant: "tertiary", href: homeUrl, children: formatMessage(messages.returnHome) })] })), children: _jsx("p", { className: "mb-0", children: formatMessage(messages.body) }) }));
8
+ };
9
+ export default CountryUnavailableModal;
10
+ //# sourceMappingURL=CountryUnavailableModal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CountryUnavailableModal.js","sourceRoot":"","sources":["../../../src/components/CountryUnavailableModal/CountryUnavailableModal.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,QAAQ,MAAM,YAAY,CAAC;AAUlC,MAAM,uBAAuB,GAAG,CAAC,EAC/B,MAAM,EACN,OAAO,GAAG,GAAG,EACb,OAAO,GAAG,GAAG,EAAE,GAAE,CAAC,GACW,EAAE,EAAE;IACjC,MAAM,EAAE,aAAa,EAAE,GAAG,OAAO,EAAE,CAAC;IAEpC,OAAO,CACL,KAAC,aAAa,IACZ,KAAK,EAAE,aAAa,CAAC,QAAQ,CAAC,KAAK,CAAC,EACpC,MAAM,EAAE,MAAM,EACd,UAAU,QACV,cAAc,EAAE,KAAK,EACrB,OAAO,EAAE,OAAO,EAChB,IAAI,EAAC,IAAI,EACT,UAAU,EAAE,CACV,MAAC,SAAS,eACR,KAAC,SAAS,CAAC,MAAM,KAAG,EACpB,KAAC,MAAM,IAAC,OAAO,EAAC,UAAU,EAAC,IAAI,EAAE,OAAO,YACrC,aAAa,CAAC,QAAQ,CAAC,UAAU,CAAC,GAC5B,IACC,CACb,YAED,YAAG,SAAS,EAAC,MAAM,YAAE,aAAa,CAAC,QAAQ,CAAC,IAAI,CAAC,GAAK,GACxC,CACjB,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,uBAAuB,CAAC","sourcesContent":["import React from 'react';\nimport { ActionRow, Button, StandardModal } from '@openedx/paragon';\nimport { useIntl } from '@openedx/frontend-base';\nimport messages from './Messages';\n\nexport interface CountryUnavailableModalProps {\n isOpen: boolean,\n /** Homepage URL for the \"Return to home\" action. Defaults to \"/\". */\n homeUrl?: string,\n /** Optional close handler; ignored when the modal is blocking. */\n onClose?: () => void,\n}\n\nconst CountryUnavailableModal = ({\n isOpen,\n homeUrl = '/',\n onClose = () => {},\n}: CountryUnavailableModalProps) => {\n const { formatMessage } = useIntl();\n\n return (\n <StandardModal\n title={formatMessage(messages.title)}\n isOpen={isOpen}\n isBlocking\n hasCloseButton={false}\n onClose={onClose}\n size=\"md\"\n footerNode={(\n <ActionRow>\n <ActionRow.Spacer />\n <Button variant=\"tertiary\" href={homeUrl}>\n {formatMessage(messages.returnHome)}\n </Button>\n </ActionRow>\n )}\n >\n <p className=\"mb-0\">{formatMessage(messages.body)}</p>\n </StandardModal>\n );\n};\n\nexport default CountryUnavailableModal;\n"]}
@@ -0,0 +1,18 @@
1
+ declare const messages: {
2
+ title: {
3
+ id: string;
4
+ defaultMessage: string;
5
+ description: string;
6
+ };
7
+ body: {
8
+ id: string;
9
+ defaultMessage: string;
10
+ description: string;
11
+ };
12
+ returnHome: {
13
+ id: string;
14
+ defaultMessage: string;
15
+ description: string;
16
+ };
17
+ };
18
+ export default messages;
@@ -0,0 +1,20 @@
1
+ import { defineMessages } from '@openedx/frontend-base';
2
+ const messages = defineMessages({
3
+ title: {
4
+ id: 'checkout.countryUnavailable.title',
5
+ defaultMessage: 'edX Plus is not available in your country.',
6
+ description: 'Title for the country-unavailable modal',
7
+ },
8
+ body: {
9
+ id: 'checkout.countryUnavailable.body',
10
+ defaultMessage: 'Unfortunately, we are unable to offer our learning products for purchase in your country. Contact edX support for more information.',
11
+ description: 'Body copy for the country-unavailable modal',
12
+ },
13
+ returnHome: {
14
+ id: 'checkout.countryUnavailable.returnHome',
15
+ defaultMessage: 'Return to home',
16
+ description: 'Button that navigates the learner back to the homepage',
17
+ },
18
+ });
19
+ export default messages;
20
+ //# sourceMappingURL=Messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Messages.js","sourceRoot":"","sources":["../../../src/components/CountryUnavailableModal/Messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,KAAK,EAAE;QACL,EAAE,EAAE,mCAAmC;QACvC,cAAc,EAAE,4CAA4C;QAC5D,WAAW,EAAE,yCAAyC;KACvD;IACD,IAAI,EAAE;QACJ,EAAE,EAAE,kCAAkC;QACtC,cAAc,EACZ,qIAAqI;QACvI,WAAW,EAAE,6CAA6C;KAC3D;IACD,UAAU,EAAE;QACV,EAAE,EAAE,wCAAwC;QAC5C,cAAc,EAAE,gBAAgB;QAChC,WAAW,EAAE,wDAAwD;KACtE;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n title: {\n id: 'checkout.countryUnavailable.title',\n defaultMessage: 'edX Plus is not available in your country.',\n description: 'Title for the country-unavailable modal',\n },\n body: {\n id: 'checkout.countryUnavailable.body',\n defaultMessage:\n 'Unfortunately, we are unable to offer our learning products for purchase in your country. Contact edX support for more information.',\n description: 'Body copy for the country-unavailable modal',\n },\n returnHome: {\n id: 'checkout.countryUnavailable.returnHome',\n defaultMessage: 'Return to home',\n description: 'Button that navigates the learner back to the homepage',\n },\n});\n\nexport default messages;\n"]}