@edx/frontend-app-alacarte-checkout 1.10.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/checkoutPage/CheckoutPage.js +12 -0
- package/dist/checkoutPage/CheckoutPage.js.map +1 -1
- package/dist/components/Checkout/Checkout.js +5 -1
- package/dist/components/Checkout/Checkout.js.map +1 -1
- package/dist/components/DemoPdpBanner/DemoPdpBanner.d.ts +8 -0
- package/dist/components/DemoPdpBanner/DemoPdpBanner.js +101 -0
- package/dist/components/DemoPdpBanner/DemoPdpBanner.js.map +1 -0
- package/dist/components/PromoCode/PromoCode.js +5 -2
- package/dist/components/PromoCode/PromoCode.js.map +1 -1
- package/dist/data/hooks/keys.d.ts +1 -0
- package/dist/data/hooks/keys.js +1 -0
- package/dist/data/hooks/keys.js.map +1 -1
- package/dist/data/hooks/useBannerData.d.ts +8 -0
- package/dist/data/hooks/useBannerData.js +61 -0
- package/dist/data/hooks/useBannerData.js.map +1 -0
- package/dist/style.scss +1 -0
- package/dist/styles/components/demoPdpBanner.scss +269 -0
- package/dist/types/alacarte.d.ts +75 -0
- package/dist/types/alacarte.js.map +1 -1
- package/dist/utils/alacarteApi.d.ts +5 -1
- package/dist/utils/alacarteApi.js +102 -1
- package/dist/utils/alacarteApi.js.map +1 -1
- package/dist/utils/alacarteQueries.d.ts +4 -0
- package/dist/utils/alacarteQueries.js +79 -0
- package/dist/utils/alacarteQueries.js.map +1 -1
- package/dist/utils/provision.js +2 -2
- package/dist/utils/provision.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
2
3
|
import { Alert, Container } from '@openedx/paragon';
|
|
3
4
|
import { getAppConfig, getSiteConfig } from '@openedx/frontend-base';
|
|
4
5
|
import { appId } from '../constants';
|
|
5
6
|
import CheckoutGate from '../components/CheckoutGate/CheckoutGate';
|
|
7
|
+
import DemoPdpBanner from '../components/DemoPdpBanner/DemoPdpBanner';
|
|
6
8
|
const PageWrapper = ({ children }) => (_jsx(Container, { children: children }));
|
|
9
|
+
// TEMPORARY — Set to true to show the demo PDP banner before checkout.
|
|
10
|
+
// When true, the banner is displayed first; clicking "Earn a certificate"
|
|
11
|
+
// transitions to the real checkout flow. Set to false to skip the banner.
|
|
12
|
+
const SHOW_DEMO_PDP = true;
|
|
7
13
|
const CheckoutPage = () => {
|
|
8
14
|
const config = getAppConfig(appId);
|
|
9
15
|
const siteConfig = getSiteConfig();
|
|
@@ -13,9 +19,15 @@ const CheckoutPage = () => {
|
|
|
13
19
|
const isDevelopment = siteConfig.environment === 'development';
|
|
14
20
|
const edxSessionUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/edx-session`;
|
|
15
21
|
const geolocationUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/geolocation/lookup`;
|
|
22
|
+
// When the demo banner is enabled, start on the "PDP" view; clicking the
|
|
23
|
+
// CTA flips showCheckout to true and the real checkout renders.
|
|
24
|
+
const [showCheckout, setShowCheckout] = useState(!SHOW_DEMO_PDP);
|
|
16
25
|
if (!stripePublishableKey || !apiUrl || !edxSessionUrl) {
|
|
17
26
|
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" }), "."] }) }));
|
|
18
27
|
}
|
|
28
|
+
if (!showCheckout) {
|
|
29
|
+
return (_jsx(DemoPdpBanner, { apiUrl: apiUrl, edxSessionUrl: edxSessionUrl, onEnroll: () => setShowCheckout(true) }));
|
|
30
|
+
}
|
|
19
31
|
return (_jsx(PageWrapper, { children: _jsx(CheckoutGate, { stripePublishableKey: stripePublishableKey, apiUrl: apiUrl, edxSessionUrl: edxSessionUrl, geolocationUrl: geolocationUrl, learnerDashboardUrl: `${LMS_BASE_URL}/dashboard`, homeUrl: LMS_BASE_URL || '/', onComplete: (_subscriptionBiId) => {
|
|
20
32
|
console.log(_subscriptionBiId);
|
|
21
33
|
// window.location.href = `/checkout/confirmation?sub=${_subscriptionBiId ?? ''}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CheckoutPage.js","sourceRoot":"","sources":["../../src/checkoutPage/CheckoutPage.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"CheckoutPage.js","sourceRoot":"","sources":["../../src/checkoutPage/CheckoutPage.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,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;AACnE,OAAO,aAAa,MAAM,2CAA2C,CAAC;AAEtE,MAAM,WAAW,GAA4C,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAC7E,KAAC,SAAS,cAAE,QAAQ,GAAa,CAClC,CAAC;AAEF,uEAAuE;AACvE,0EAA0E;AAC1E,0EAA0E;AAC1E,MAAM,aAAa,GAAG,IAAI,CAAC;AAE3B,MAAM,YAAY,GAAa,GAAG,EAAE;IAClC,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC;IACnC,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;IACnC,MAAM,YAAY,GAAG,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC9D,MAAM,oBAAoB,GAAG,MAAM,CAAC,iBAA2B,CAAC;IAChE,MAAM,MAAM,GAAG,MAAM,CAAC,YAAsB,CAAC;IAC7C,MAAM,aAAa,GAAG,UAAU,CAAC,WAAW,KAAK,aAAa,CAAC;IAC/D,MAAM,aAAa,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,YAAY,kBAAkB,CAAC;IAC1G,MAAM,cAAc,GAAG,GAAG,aAAa,CAAC,CAAC,CAAC,+BAA+B,CAAC,CAAC,CAAC,YAAY,yBAAyB,CAAC;IAElH,yEAAyE;IACzE,gEAAgE;IAChE,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,CAAC,aAAa,CAAC,CAAC;IAEjE,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,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,CACL,KAAC,aAAa,IACZ,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,GAAG,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,GACrC,CACH,CAAC;IACJ,CAAC;IAED,OAAO,CACL,KAAC,WAAW,cACV,KAAC,YAAY,IACX,oBAAoB,EAAE,oBAAoB,EAC1C,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,EAC5B,cAAc,EAAE,cAAc,EAC9B,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, { useState } 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';\nimport DemoPdpBanner from '../components/DemoPdpBanner/DemoPdpBanner';\n\nconst PageWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => (\n <Container>{children}</Container>\n);\n\n// TEMPORARY — Set to true to show the demo PDP banner before checkout.\n// When true, the banner is displayed first; clicking \"Earn a certificate\"\n// transitions to the real checkout flow. Set to false to skip the banner.\nconst SHOW_DEMO_PDP = true;\n\nconst CheckoutPage: React.FC = () => {\n const config = getAppConfig(appId);\n const siteConfig = getSiteConfig();\n const LMS_BASE_URL = siteConfig.lmsBaseUrl.replace(/\\/$/, '');\n const stripePublishableKey = config.STRIPE_PUBLIC_KEY as string;\n const apiUrl = config.VIAX_API_URL as string;\n const isDevelopment = siteConfig.environment === 'development';\n const edxSessionUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/edx-session`;\n const geolocationUrl = `${isDevelopment ? 'https://courses.stage.edx.org' : LMS_BASE_URL}/api/geolocation/lookup`;\n\n // When the demo banner is enabled, start on the \"PDP\" view; clicking the\n // CTA flips showCheckout to true and the real checkout renders.\n const [showCheckout, setShowCheckout] = useState(!SHOW_DEMO_PDP);\n\n if (!stripePublishableKey || !apiUrl || !edxSessionUrl) {\n return (\n <PageWrapper>\n <Alert variant=\"danger\">\n We're having trouble loading the checkout. Please refresh the\n page or try again in a few moments. If the problem persists,{' '}\n <a href=\"https://courses.edx.org/support/contact_us\">\n contact support\n </a>\n .\n </Alert>\n </PageWrapper>\n );\n }\n\n if (!showCheckout) {\n return (\n <DemoPdpBanner\n apiUrl={apiUrl}\n edxSessionUrl={edxSessionUrl}\n onEnroll={() => setShowCheckout(true)}\n />\n );\n }\n\n return (\n <PageWrapper>\n <CheckoutGate\n stripePublishableKey={stripePublishableKey}\n apiUrl={apiUrl}\n edxSessionUrl={edxSessionUrl}\n geolocationUrl={geolocationUrl}\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"]}
|
|
@@ -60,7 +60,11 @@ const Checkout = (_a) => {
|
|
|
60
60
|
? alacarte.checkout.couponCode.trim()
|
|
61
61
|
: null;
|
|
62
62
|
const discountPercent = appliedDiscountPercent(alacarte.line);
|
|
63
|
-
|
|
63
|
+
// The line may carry a coupon discount even when the checkout response
|
|
64
|
+
// omits the coupon code string (tCheckout doesn't always include it).
|
|
65
|
+
// Decouple the discount display from the code so PromoCode can show the
|
|
66
|
+
// applied state with a Remove button in either case.
|
|
67
|
+
const appliedDiscount = ((_d = alacarte.line) === null || _d === void 0 ? void 0 : _d.discount) && discountPercent != null
|
|
64
68
|
? {
|
|
65
69
|
percent: discountPercent,
|
|
66
70
|
amount: alacarte.line.discount.amount,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useCallback, useEffect, useState } from 'react';\nimport { Alert, Button, Col, Row } from '@openedx/paragon';\nimport { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport { useAlacarteCheckout } from '../../data/hooks/useAlacarteCheckout';\nimport { ALLOWED_CHECKOUT_COUNTRIES, GATE_CHECK_COUNTRY } from '../../constants';\nimport CartSummary from '../CartSummary/CartSummary';\nimport EnrollmentSummary from '../EnrollmentSummary/EnrollmentSummary';\nimport PaymentForm from '../PaymentForm/PaymentForm';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport AlacarteNotice from '../AlacarteNotice/AlacarteNotice';\nimport AlacarteConfirmation from '../AlacarteConfirmation/AlacarteConfirmation';\nimport AlacarteDebugPanel from '../AlacarteDebugPanel/AlacarteDebugPanel'; // TEMPORARY — remove alongside AlacarteDebugPanel\nimport { getTrialDateRange } from '@src/utils';\nimport { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';\nimport { appliedDiscountPercent } from '../../utils/discount';\nimport { confirmPaymentWithMethodId } from '../../utils/stripeCard';\nimport { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle, CourseDetail } from '../../types';\nimport type { ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\n\n// TEMPORARY — redirect-home-on-noActiveCheckout disabled per request; shows a\n// banner instead so the case is visible while the flow is being verified.\n// Flip back to true to restore the redirect.\nconst REDIRECT_ON_NO_ACTIVE_CHECKOUT = false;\n\nexport type CheckoutProps = UseCheckoutOptions & {\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n /** Endpoint used to look up the representative IP for a selected country. */\n geolocationUrl?: string,\n /** Learner dashboard / order history URL for confirmation CTAs. */\n orderHistoryUrl?: string,\n};\n\nconst Checkout = ({\n homeUrl = '/',\n orderHistoryUrl,\n ...props\n}: CheckoutProps) => {\n const { formatMessage } = useIntl();\n const alacarte = useAlacarteCheckout({ apiUrl: props.apiUrl, edxSessionUrl: props.edxSessionUrl });\n const c = useCheckout(props);\n const [promoCodeValue, setPromoCodeValue] = useState('');\n const [dismissedCouponAlert, setDismissedCouponAlert] = useState<string | null>(null);\n const [showValidationBanner, setShowValidationBanner] = useState(false);\n // Last code we successfully priced — drives the \"Save {percent}% with code\n // {code}\" hint on the empty field while a coupon is not yet applied.\n const [advertisedOffer, setAdvertisedOffer] = useState<{ code: string, percent: number } | null>(null);\n const isAlacarteReady = (alacarte.phase === 'ready' || alacarte.phase === 'alreadyOwned') && !!alacarte.checkout;\n const appliedCouponCode = alacarte.checkout?.couponCode?.trim()\n ? alacarte.checkout.couponCode.trim()\n : null;\n const discountPercent = appliedDiscountPercent(alacarte.line);\n const appliedDiscount = appliedCouponCode && alacarte.line?.discount && discountPercent != null\n ? {\n percent: discountPercent,\n amount: alacarte.line.discount.amount,\n currency: alacarte.line.discount.currency,\n }\n : null;\n\n // Keep the coupon field in sync when a code is applied via URL or API.\n useEffect(() => {\n if (alacarte.checkout?.couponCode) {\n setPromoCodeValue(alacarte.checkout.couponCode);\n }\n }, [alacarte.checkout?.couponCode]);\n\n useEffect(() => {\n if (appliedCouponCode && discountPercent != null) {\n setAdvertisedOffer({ code: appliedCouponCode, percent: discountPercent });\n }\n }, [appliedCouponCode, discountPercent]);\n\n // No query params and no active cart — nothing to check out, send the\n // learner home rather than showing a placeholder.\n useEffect(() => {\n if (REDIRECT_ON_NO_ACTIVE_CHECKOUT && alacarte.phase === 'noActiveCheckout') {\n window.location.href = homeUrl;\n }\n }, [alacarte.phase, homeUrl]);\n\n // Express checkout (Apple Pay / Google Pay): runs the full place-order flow\n // using a wallet-provided PaymentMethod id instead of the mounted card element.\n const handleExpressPayment = useCallback(async (paymentMethodId: string, walletCountry?: string) => {\n if (!c.stripe) throw new Error('Stripe not loaded');\n const stripeInstance = c.stripe;\n\n await alacarte.placeOrderAsync({\n billing: {\n ...c.billing,\n country: c.billing.country || walletCountry || c.userCountry || '',\n },\n confirmCardPayment: async (clientSecret: string) => {\n await confirmPaymentWithMethodId({\n stripe: stripeInstance,\n clientSecret,\n paymentMethodId,\n });\n },\n });\n }, [alacarte, c.billing, c.userCountry, c.stripe]);\n\n const showProcessing\n = c.returningFromStripe\n && c.processingSubscription\n && c.phase !== 'success'\n && c.phase !== 'blocked'\n && c.phase !== 'failed';\n const showForm\n = !showProcessing\n && (c.phase === 'init'\n || c.phase === 'ready'\n || c.phase === 'failed'\n || c.phase === 'processing');\n const failedMessage = getLearnerFacingCheckoutMessage(c.lastComment);\n\n const { displayStart, displayEnd } = getTrialDateRange();\n\n const isBillingCountryBlocked = GATE_CHECK_COUNTRY\n && !!c.billing.country\n && !ALLOWED_CHECKOUT_COUNTRIES.includes(c.billing.country);\n\n const selectedCycle: BillingCycle = c.selectedCycle;\n\n // Prefer the à la carte course's own detail (skills/badge/image, richer than\n // the subscription flow's ?course_run_key= lookup) when we selected a course.\n // When already-owned the line may be null, so fall back to the selector kind.\n const isCourseProduct = alacarte.line?.kind === 'course' || alacarte.selector?.kind === 'course';\n const isProgramProduct = alacarte.line?.kind === 'program' || alacarte.selector?.kind === 'program';\n\n const alacarteCourseDetail: CourseDetail | null\n = isCourseProduct && alacarte.productDetail && 'maId' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n const courseForSummary = alacarteCourseDetail ?? c.course;\n\n const alacarteProgramDetail: ProgramDetail | null\n = isProgramProduct && alacarte.productDetail && 'tCourses' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n\n const handleStartCourse = () => {\n const alacarteCourseUrl = alacarteCourseDetail?.tUrlCourse\n || (alacarte.productDetail && 'tUrlCourse' in alacarte.productDetail\n ? alacarte.productDetail.tUrlCourse\n : null);\n if (alacarteCourseUrl) {\n window.location.href = alacarteCourseUrl;\n } else if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = homeUrl || '/';\n }\n };\n\n const courseLabel = c.course\n ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')\n : null;\n\n if (showProcessing) {\n return <ProcessingScreen />;\n }\n\n // TEMPORARY — flip to true to dump à la carte hook state while validating\n // against the real backend. Remove AlacarteDebugPanel once the flow is solid.\n const SHOW_ALACARTE_DEBUG = false;\n const alacarteDebug = SHOW_ALACARTE_DEBUG\n ? <AlacarteDebugPanel data={alacarte} />\n : null;\n\n // Confirmation: show once the order is ready (or failed to load). While\n // tPlacedOrder is still polling, keep the checkout form so Pay now can show\n // \"Processing...\" instead of a full-page loader.\n if (alacarte.placedOrder || alacarte.placedOrderError) {\n const confirmationTitle = alacarte.line?.kind === 'program'\n ? alacarte.line.programName\n : alacarte.line?.kind === 'course'\n ? alacarte.line.courseName\n : null;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container alacarte-confirmation__container bg-light-200\">\n <Col xs={12}>\n <AlacarteConfirmation\n order={alacarte.placedOrder}\n awaiting={false}\n error={alacarte.placedOrderError}\n onContinue={handleStartCourse}\n productDetail={alacarte.productDetail}\n productTitle={confirmationTitle}\n orderHistoryUrl={orderHistoryUrl ?? null}\n placedAt={alacarte.placedOrderPlacedAt}\n />\n </Col>\n </Row>\n </>\n );\n }\n\n if (alacarte.phase === 'loading') {\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout' && REDIRECT_ON_NO_ACTIVE_CHECKOUT) {\n // Redirects home via the effect above — show a spinner rather than a\n // notice while that happens.\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout') {\n return (\n <>\n {alacarteDebug}\n <AlacarteNotice messageId={alacarte.noticeMessageId!} />\n </>\n );\n }\n if (alacarte.phase === 'notPurchasable') {\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <Col xs={12}>\n <Alert variant=\"danger\" icon={ErrorIcon}>\n {alacarte.error}\n </Alert>\n </Col>\n </Row>\n </>\n );\n }\n\n const isAlreadyOwned = alacarte.isAlreadyOwned;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <CountryUnavailableModal\n isOpen={isBillingCountryBlocked}\n homeUrl={homeUrl}\n />\n <Col xs={12} className=\"d-lg-none\">\n <h1 className=\"font-weight-bold text-primary-900\">Checkout</h1>\n </Col>\n\n <Col xs={12} className={isAlreadyOwned ? 'checkout__owned-overlay' : ''}>\n <Row className=\"flex-column-reverse gap-5 flex-lg-row\">\n {/* ── LEFT ── */}\n <Col xs={12} lg={7} xxl={9} className=\"payment__options\">\n <div>\n <h1 className=\"font-weight-bold text-primary-900 d-none d-lg-block mb-4\">\n Checkout\n </h1>\n\n {isAlreadyOwned && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n className=\"checkout__already-owned-alert mb-3\"\n data-testid=\"already-owned-alert\"\n >\n <p className=\"mb-3\">\n {formatMessage(\n alacarte.selector?.kind === 'program'\n ? messages.alreadyPaidProgram\n : messages.alreadyPaidCourse,\n )}\n </p>\n <div className=\"d-flex align-items-center justify-content-end gap-3 flex-wrap\">\n {orderHistoryUrl && (\n <a href={orderHistoryUrl} className=\"text-gray-900 font-weight-bold\">\n {formatMessage(messages.viewOrderHistory)}\n </a>\n )}\n <Button variant=\"brand\" className=\"rounded-pill\" onClick={handleStartCourse}>\n {formatMessage(messages.goToCourse)}\n </Button>\n </div>\n </Alert>\n )}\n\n {c.error && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={c.clearError}\n >\n {c.error}\n </Alert>\n )}\n\n {alacarte.placeOrderError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearPlaceOrderError}\n className=\"mb-3\"\n >\n {alacarte.placeOrderError}\n </Alert>\n )}\n\n {alacarte.couponError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearCouponError}\n className=\"mb-3\"\n data-testid=\"coupon-error-alert\"\n >\n {alacarte.couponError}\n </Alert>\n )}\n\n {!alacarte.priceAccepted && isAlacarteReady && alacarte.line?.amountDue && (\n <Alert variant=\"info\" className=\"mb-3\" data-testid=\"price-accept-notice\">\n {formatMessage(messages.priceChangedNotice, {\n amount: alacarte.line.amountDue.amount.toFixed(2),\n currency: alacarte.line.amountDue.currency,\n })}\n </Alert>\n )}\n\n {showForm && (\n <>\n {showValidationBanner && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {formatMessage(messages.validationBanner)}\n </Alert>\n )}\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {failedMessage\n || formatMessage(messages.paymentProcessingDescription)}\n </Alert>\n )}\n {appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (\n <Alert\n variant=\"success\"\n icon={CheckCircle}\n dismissible\n closeLabel={formatMessage(messages.dismiss)}\n onClose={() => setDismissedCouponAlert(appliedCouponCode)}\n className=\"coupon-applied-alert\"\n data-testid=\"coupon-applied-alert\"\n >\n <span aria-hidden=\"true\">🎉 </span>\n {formatMessage(messages.couponAppliedAlert, {\n code: appliedCouponCode,\n percent: appliedDiscount.percent,\n })}\n </Alert>\n )}\n <PaymentForm\n billing={c.billing}\n setField={c.setField}\n numberMountRef={c.numberMountRef}\n expiryMountRef={c.expiryMountRef}\n cvcMountRef={c.cvcMountRef}\n cardError={c.cardError}\n cardFieldStates={c.cardFieldStates}\n stripeReady={c.stripeReady}\n sessionReady={c.sessionReady}\n submitting={\n isAlacarteReady\n ? (alacarte.placingOrder || alacarte.awaitingPlacedOrder)\n : c.processingSubscription\n }\n onPlaceOrder={\n isAlacarteReady\n ? () => {\n alacarte.placeOrder({\n billing: {\n ...c.billing,\n // Country is required on tCheckoutPlaceOrder (two-letter ISO).\n // Prefer the form selection; fall back to gate geolocation.\n country: c.billing.country || c.userCountry || '',\n },\n confirmCardPayment: c.confirmCardPayment,\n });\n }\n : c.placeOrder\n }\n onValidationError={setShowValidationBanner}\n trialEnd={selectedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={null}\n cycleToShow={selectedCycle === 'monthly' ? 'monthly' : 'annual'}\n userCountry={c.userCountry}\n geolocationUrl={props.geolocationUrl}\n alacarteLine={alacarte.line}\n stripe={c.stripe}\n expressCountry={c.billing.country || c.userCountry || 'US'}\n onExpressPayment={isAlacarteReady ? handleExpressPayment : undefined}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n courseLabel={courseLabel}\n onFindCourse={handleStartCourse}\n />\n )}\n\n {c.phase === 'blocked' && (\n <BlockedScreen detail={c.lastComment} />\n )}\n </div>\n </Col>\n\n {/* ── RIGHT — swaps to EnrollmentSummary on success ── */}\n <Col xs={12} lg={5} xxl={3} className=\"payment__summary\">\n {c.phase === 'success' ? (\n <EnrollmentSummary\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n defaultCycle={selectedCycle}\n onCycleChange={c.processingSubscription ? undefined : c.setCycle}\n course={courseForSummary}\n alacarteLine={alacarte.line}\n programDetail={alacarteProgramDetail}\n programName={alacarte.ownedProductName}\n promoCode={isAlacarteReady ? {\n value: promoCodeValue,\n onChange: (next) => {\n setPromoCodeValue(next);\n if (alacarte.couponError) alacarte.clearCouponError();\n },\n onApply: () => alacarte.applyCoupon(promoCodeValue),\n onRemove: () => {\n alacarte.removeCoupon();\n setPromoCodeValue('');\n setAdvertisedOffer(null);\n },\n appliedCode: appliedCouponCode,\n discount: appliedDiscount,\n offer: !appliedCouponCode ? advertisedOffer : null,\n error: alacarte.couponError,\n isApplying: alacarte.couponApplying,\n disabled: alacarte.placingOrder,\n } : null}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n </>\n );\n};\n\nexport default Checkout;\n"]}
|
|
1
|
+
{"version":3,"file":"Checkout.js","sourceRoot":"","sources":["../../../src/components/Checkout/Checkout.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useCallback, useEffect, useState } from 'react';\nimport { Alert, Button, Col, Row } from '@openedx/paragon';\nimport { CheckCircle, Error as ErrorIcon } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport useCheckout from '../../data/hooks/useCheckout';\nimport type { UseCheckoutOptions } from '../../data/hooks/useCheckout';\nimport { useAlacarteCheckout } from '../../data/hooks/useAlacarteCheckout';\nimport { ALLOWED_CHECKOUT_COUNTRIES, GATE_CHECK_COUNTRY } from '../../constants';\nimport CartSummary from '../CartSummary/CartSummary';\nimport EnrollmentSummary from '../EnrollmentSummary/EnrollmentSummary';\nimport PaymentForm from '../PaymentForm/PaymentForm';\nimport CountryUnavailableModal from '../CountryUnavailableModal/CountryUnavailableModal';\nimport AlacarteNotice from '../AlacarteNotice/AlacarteNotice';\nimport AlacarteConfirmation from '../AlacarteConfirmation/AlacarteConfirmation';\nimport AlacarteDebugPanel from '../AlacarteDebugPanel/AlacarteDebugPanel'; // TEMPORARY — remove alongside AlacarteDebugPanel\nimport { getTrialDateRange } from '@src/utils';\nimport { getLearnerFacingCheckoutMessage } from '../../utils/checkoutErrors';\nimport { appliedDiscountPercent } from '../../utils/discount';\nimport { confirmPaymentWithMethodId } from '../../utils/stripeCard';\nimport { SuccessScreen, BlockedScreen, ProcessingScreen } from '../StatusScreens/StatusScreens';\nimport type { BillingCycle, CourseDetail } from '../../types';\nimport type { ProgramDetail } from '../../types/alacarte';\nimport messages from './Messages';\n\n// TEMPORARY — redirect-home-on-noActiveCheckout disabled per request; shows a\n// banner instead so the case is visible while the flow is being verified.\n// Flip back to true to restore the redirect.\nconst REDIRECT_ON_NO_ACTIVE_CHECKOUT = false;\n\nexport type CheckoutProps = UseCheckoutOptions & {\n /** Homepage URL for the country-unavailable modal CTA. */\n homeUrl?: string,\n /** Endpoint used to look up the representative IP for a selected country. */\n geolocationUrl?: string,\n /** Learner dashboard / order history URL for confirmation CTAs. */\n orderHistoryUrl?: string,\n};\n\nconst Checkout = ({\n homeUrl = '/',\n orderHistoryUrl,\n ...props\n}: CheckoutProps) => {\n const { formatMessage } = useIntl();\n const alacarte = useAlacarteCheckout({ apiUrl: props.apiUrl, edxSessionUrl: props.edxSessionUrl });\n const c = useCheckout(props);\n const [promoCodeValue, setPromoCodeValue] = useState('');\n const [dismissedCouponAlert, setDismissedCouponAlert] = useState<string | null>(null);\n const [showValidationBanner, setShowValidationBanner] = useState(false);\n // Last code we successfully priced — drives the \"Save {percent}% with code\n // {code}\" hint on the empty field while a coupon is not yet applied.\n const [advertisedOffer, setAdvertisedOffer] = useState<{ code: string, percent: number } | null>(null);\n const isAlacarteReady = (alacarte.phase === 'ready' || alacarte.phase === 'alreadyOwned') && !!alacarte.checkout;\n const appliedCouponCode = alacarte.checkout?.couponCode?.trim()\n ? alacarte.checkout.couponCode.trim()\n : null;\n const discountPercent = appliedDiscountPercent(alacarte.line);\n // The line may carry a coupon discount even when the checkout response\n // omits the coupon code string (tCheckout doesn't always include it).\n // Decouple the discount display from the code so PromoCode can show the\n // applied state with a Remove button in either case.\n const appliedDiscount = alacarte.line?.discount && discountPercent != null\n ? {\n percent: discountPercent,\n amount: alacarte.line.discount.amount,\n currency: alacarte.line.discount.currency,\n }\n : null;\n\n // Keep the coupon field in sync when a code is applied via URL or API.\n useEffect(() => {\n if (alacarte.checkout?.couponCode) {\n setPromoCodeValue(alacarte.checkout.couponCode);\n }\n }, [alacarte.checkout?.couponCode]);\n\n useEffect(() => {\n if (appliedCouponCode && discountPercent != null) {\n setAdvertisedOffer({ code: appliedCouponCode, percent: discountPercent });\n }\n }, [appliedCouponCode, discountPercent]);\n\n // No query params and no active cart — nothing to check out, send the\n // learner home rather than showing a placeholder.\n useEffect(() => {\n if (REDIRECT_ON_NO_ACTIVE_CHECKOUT && alacarte.phase === 'noActiveCheckout') {\n window.location.href = homeUrl;\n }\n }, [alacarte.phase, homeUrl]);\n\n // Express checkout (Apple Pay / Google Pay): runs the full place-order flow\n // using a wallet-provided PaymentMethod id instead of the mounted card element.\n const handleExpressPayment = useCallback(async (paymentMethodId: string, walletCountry?: string) => {\n if (!c.stripe) throw new Error('Stripe not loaded');\n const stripeInstance = c.stripe;\n\n await alacarte.placeOrderAsync({\n billing: {\n ...c.billing,\n country: c.billing.country || walletCountry || c.userCountry || '',\n },\n confirmCardPayment: async (clientSecret: string) => {\n await confirmPaymentWithMethodId({\n stripe: stripeInstance,\n clientSecret,\n paymentMethodId,\n });\n },\n });\n }, [alacarte, c.billing, c.userCountry, c.stripe]);\n\n const showProcessing\n = c.returningFromStripe\n && c.processingSubscription\n && c.phase !== 'success'\n && c.phase !== 'blocked'\n && c.phase !== 'failed';\n const showForm\n = !showProcessing\n && (c.phase === 'init'\n || c.phase === 'ready'\n || c.phase === 'failed'\n || c.phase === 'processing');\n const failedMessage = getLearnerFacingCheckoutMessage(c.lastComment);\n\n const { displayStart, displayEnd } = getTrialDateRange();\n\n const isBillingCountryBlocked = GATE_CHECK_COUNTRY\n && !!c.billing.country\n && !ALLOWED_CHECKOUT_COUNTRIES.includes(c.billing.country);\n\n const selectedCycle: BillingCycle = c.selectedCycle;\n\n // Prefer the à la carte course's own detail (skills/badge/image, richer than\n // the subscription flow's ?course_run_key= lookup) when we selected a course.\n // When already-owned the line may be null, so fall back to the selector kind.\n const isCourseProduct = alacarte.line?.kind === 'course' || alacarte.selector?.kind === 'course';\n const isProgramProduct = alacarte.line?.kind === 'program' || alacarte.selector?.kind === 'program';\n\n const alacarteCourseDetail: CourseDetail | null\n = isCourseProduct && alacarte.productDetail && 'maId' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n const courseForSummary = alacarteCourseDetail ?? c.course;\n\n const alacarteProgramDetail: ProgramDetail | null\n = isProgramProduct && alacarte.productDetail && 'tCourses' in alacarte.productDetail\n ? alacarte.productDetail\n : null;\n\n const handleStartCourse = () => {\n const alacarteCourseUrl = alacarteCourseDetail?.tUrlCourse\n || (alacarte.productDetail && 'tUrlCourse' in alacarte.productDetail\n ? alacarte.productDetail.tUrlCourse\n : null);\n if (alacarteCourseUrl) {\n window.location.href = alacarteCourseUrl;\n } else if (c.course?.tUrlCourse) {\n window.location.href = c.course.tUrlCourse;\n } else {\n window.location.href = homeUrl || '/';\n }\n };\n\n const courseLabel = c.course\n ? [c.course.tBrandText, c.course.maName].filter(Boolean).join(': ')\n : null;\n\n if (showProcessing) {\n return <ProcessingScreen />;\n }\n\n // TEMPORARY — flip to true to dump à la carte hook state while validating\n // against the real backend. Remove AlacarteDebugPanel once the flow is solid.\n const SHOW_ALACARTE_DEBUG = false;\n const alacarteDebug = SHOW_ALACARTE_DEBUG\n ? <AlacarteDebugPanel data={alacarte} />\n : null;\n\n // Confirmation: show once the order is ready (or failed to load). While\n // tPlacedOrder is still polling, keep the checkout form so Pay now can show\n // \"Processing...\" instead of a full-page loader.\n if (alacarte.placedOrder || alacarte.placedOrderError) {\n const confirmationTitle = alacarte.line?.kind === 'program'\n ? alacarte.line.programName\n : alacarte.line?.kind === 'course'\n ? alacarte.line.courseName\n : null;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container alacarte-confirmation__container bg-light-200\">\n <Col xs={12}>\n <AlacarteConfirmation\n order={alacarte.placedOrder}\n awaiting={false}\n error={alacarte.placedOrderError}\n onContinue={handleStartCourse}\n productDetail={alacarte.productDetail}\n productTitle={confirmationTitle}\n orderHistoryUrl={orderHistoryUrl ?? null}\n placedAt={alacarte.placedOrderPlacedAt}\n />\n </Col>\n </Row>\n </>\n );\n }\n\n if (alacarte.phase === 'loading') {\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout' && REDIRECT_ON_NO_ACTIVE_CHECKOUT) {\n // Redirects home via the effect above — show a spinner rather than a\n // notice while that happens.\n return (\n <>\n {alacarteDebug}\n <ProcessingScreen />\n </>\n );\n }\n if (alacarte.phase === 'noActiveCheckout') {\n return (\n <>\n {alacarteDebug}\n <AlacarteNotice messageId={alacarte.noticeMessageId!} />\n </>\n );\n }\n if (alacarte.phase === 'notPurchasable') {\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <Col xs={12}>\n <Alert variant=\"danger\" icon={ErrorIcon}>\n {alacarte.error}\n </Alert>\n </Col>\n </Row>\n </>\n );\n }\n\n const isAlreadyOwned = alacarte.isAlreadyOwned;\n\n return (\n <>\n {alacarteDebug}\n <Row className=\"checkout__container bg-light-200\">\n <CountryUnavailableModal\n isOpen={isBillingCountryBlocked}\n homeUrl={homeUrl}\n />\n <Col xs={12} className=\"d-lg-none\">\n <h1 className=\"font-weight-bold text-primary-900\">Checkout</h1>\n </Col>\n\n <Col xs={12} className={isAlreadyOwned ? 'checkout__owned-overlay' : ''}>\n <Row className=\"flex-column-reverse gap-5 flex-lg-row\">\n {/* ── LEFT ── */}\n <Col xs={12} lg={7} xxl={9} className=\"payment__options\">\n <div>\n <h1 className=\"font-weight-bold text-primary-900 d-none d-lg-block mb-4\">\n Checkout\n </h1>\n\n {isAlreadyOwned && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n className=\"checkout__already-owned-alert mb-3\"\n data-testid=\"already-owned-alert\"\n >\n <p className=\"mb-3\">\n {formatMessage(\n alacarte.selector?.kind === 'program'\n ? messages.alreadyPaidProgram\n : messages.alreadyPaidCourse,\n )}\n </p>\n <div className=\"d-flex align-items-center justify-content-end gap-3 flex-wrap\">\n {orderHistoryUrl && (\n <a href={orderHistoryUrl} className=\"text-gray-900 font-weight-bold\">\n {formatMessage(messages.viewOrderHistory)}\n </a>\n )}\n <Button variant=\"brand\" className=\"rounded-pill\" onClick={handleStartCourse}>\n {formatMessage(messages.goToCourse)}\n </Button>\n </div>\n </Alert>\n )}\n\n {c.error && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={c.clearError}\n >\n {c.error}\n </Alert>\n )}\n\n {alacarte.placeOrderError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearPlaceOrderError}\n className=\"mb-3\"\n >\n {alacarte.placeOrderError}\n </Alert>\n )}\n\n {alacarte.couponError && (\n <Alert\n variant=\"danger\"\n icon={ErrorIcon}\n dismissible\n onClose={alacarte.clearCouponError}\n className=\"mb-3\"\n data-testid=\"coupon-error-alert\"\n >\n {alacarte.couponError}\n </Alert>\n )}\n\n {!alacarte.priceAccepted && isAlacarteReady && alacarte.line?.amountDue && (\n <Alert variant=\"info\" className=\"mb-3\" data-testid=\"price-accept-notice\">\n {formatMessage(messages.priceChangedNotice, {\n amount: alacarte.line.amountDue.amount.toFixed(2),\n currency: alacarte.line.amountDue.currency,\n })}\n </Alert>\n )}\n\n {showForm && (\n <>\n {showValidationBanner && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {formatMessage(messages.validationBanner)}\n </Alert>\n )}\n {c.phase === 'failed' && (\n <Alert variant=\"danger\" icon={ErrorIcon} className=\"mb-3\">\n {failedMessage\n || formatMessage(messages.paymentProcessingDescription)}\n </Alert>\n )}\n {appliedCouponCode && appliedDiscount && dismissedCouponAlert !== appliedCouponCode && (\n <Alert\n variant=\"success\"\n icon={CheckCircle}\n dismissible\n closeLabel={formatMessage(messages.dismiss)}\n onClose={() => setDismissedCouponAlert(appliedCouponCode)}\n className=\"coupon-applied-alert\"\n data-testid=\"coupon-applied-alert\"\n >\n <span aria-hidden=\"true\">🎉 </span>\n {formatMessage(messages.couponAppliedAlert, {\n code: appliedCouponCode,\n percent: appliedDiscount.percent,\n })}\n </Alert>\n )}\n <PaymentForm\n billing={c.billing}\n setField={c.setField}\n numberMountRef={c.numberMountRef}\n expiryMountRef={c.expiryMountRef}\n cvcMountRef={c.cvcMountRef}\n cardError={c.cardError}\n cardFieldStates={c.cardFieldStates}\n stripeReady={c.stripeReady}\n sessionReady={c.sessionReady}\n submitting={\n isAlacarteReady\n ? (alacarte.placingOrder || alacarte.awaitingPlacedOrder)\n : c.processingSubscription\n }\n onPlaceOrder={\n isAlacarteReady\n ? () => {\n alacarte.placeOrder({\n billing: {\n ...c.billing,\n // Country is required on tCheckoutPlaceOrder (two-letter ISO).\n // Prefer the form selection; fall back to gate geolocation.\n country: c.billing.country || c.userCountry || '',\n },\n confirmCardPayment: c.confirmCardPayment,\n });\n }\n : c.placeOrder\n }\n onValidationError={setShowValidationBanner}\n trialEnd={selectedCycle === 'monthly' ? displayEnd : undefined}\n priceToShow={null}\n cycleToShow={selectedCycle === 'monthly' ? 'monthly' : 'annual'}\n userCountry={c.userCountry}\n geolocationUrl={props.geolocationUrl}\n alacarteLine={alacarte.line}\n stripe={c.stripe}\n expressCountry={c.billing.country || c.userCountry || 'US'}\n onExpressPayment={isAlacarteReady ? handleExpressPayment : undefined}\n />\n </>\n )}\n\n {c.phase === 'success' && (\n <SuccessScreen\n subscriptionBiId={c.subscriptionDetails?.biId || ''}\n cardLast4={c.subscriptionDetails?.cardLast4 || ''}\n purchaseDate={displayStart}\n courseLabel={courseLabel}\n onFindCourse={handleStartCourse}\n />\n )}\n\n {c.phase === 'blocked' && (\n <BlockedScreen detail={c.lastComment} />\n )}\n </div>\n </Col>\n\n {/* ── RIGHT — swaps to EnrollmentSummary on success ── */}\n <Col xs={12} lg={5} xxl={3} className=\"payment__summary\">\n {c.phase === 'success' ? (\n <EnrollmentSummary\n course={c.course}\n onStartCourse={handleStartCourse}\n />\n ) : (\n <CartSummary\n defaultCycle={selectedCycle}\n onCycleChange={c.processingSubscription ? undefined : c.setCycle}\n course={courseForSummary}\n alacarteLine={alacarte.line}\n programDetail={alacarteProgramDetail}\n programName={alacarte.ownedProductName}\n promoCode={isAlacarteReady ? {\n value: promoCodeValue,\n onChange: (next) => {\n setPromoCodeValue(next);\n if (alacarte.couponError) alacarte.clearCouponError();\n },\n onApply: () => alacarte.applyCoupon(promoCodeValue),\n onRemove: () => {\n alacarte.removeCoupon();\n setPromoCodeValue('');\n setAdvertisedOffer(null);\n },\n appliedCode: appliedCouponCode,\n discount: appliedDiscount,\n offer: !appliedCouponCode ? advertisedOffer : null,\n error: alacarte.couponError,\n isApplying: alacarte.couponApplying,\n disabled: alacarte.placingOrder,\n } : null}\n />\n )}\n </Col>\n </Row>\n </Col>\n </Row>\n </>\n );\n};\n\nexport default Checkout;\n"]}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Spinner } from '@openedx/paragon';
|
|
3
|
+
import { useBannerData } from '../../data/hooks/useBannerData';
|
|
4
|
+
/**
|
|
5
|
+
* Build the checkout URL from the current query parameters.
|
|
6
|
+
* All four supported params (program_uid, course_uid, course_run_key,
|
|
7
|
+
* discount_code) are read exclusively from the URL — nothing is hardcoded.
|
|
8
|
+
*/
|
|
9
|
+
function buildCheckoutUrl() {
|
|
10
|
+
var _a, _b, _c, _d;
|
|
11
|
+
const currentParams = new URLSearchParams(window.location.search);
|
|
12
|
+
const courseUid = (_a = currentParams.get('course_uid')) !== null && _a !== void 0 ? _a : '';
|
|
13
|
+
const courseRunKey = (_b = currentParams.get('course_run_key')) !== null && _b !== void 0 ? _b : '';
|
|
14
|
+
const programUid = (_c = currentParams.get('program_uid')) !== null && _c !== void 0 ? _c : '';
|
|
15
|
+
const discountCode = (_d = currentParams.get('discount_code')) !== null && _d !== void 0 ? _d : '';
|
|
16
|
+
const params = new URLSearchParams();
|
|
17
|
+
if (courseUid && courseRunKey) {
|
|
18
|
+
params.set('course_uid', courseUid);
|
|
19
|
+
params.set('course_run_key', courseRunKey);
|
|
20
|
+
}
|
|
21
|
+
else if (programUid) {
|
|
22
|
+
params.set('program_uid', programUid);
|
|
23
|
+
}
|
|
24
|
+
if (discountCode) {
|
|
25
|
+
params.set('discount_code', discountCode);
|
|
26
|
+
}
|
|
27
|
+
return `/aca-checkout?${params.toString()}`;
|
|
28
|
+
}
|
|
29
|
+
function formatNumber(n) {
|
|
30
|
+
return n.toLocaleString('en-US');
|
|
31
|
+
}
|
|
32
|
+
function formatDuration(low, high, unit) {
|
|
33
|
+
var _a;
|
|
34
|
+
if (!low && !high)
|
|
35
|
+
return null;
|
|
36
|
+
const u = unit !== null && unit !== void 0 ? unit : 'week';
|
|
37
|
+
const label = `${u}${((_a = high !== null && high !== void 0 ? high : low) !== null && _a !== void 0 ? _a : 0) !== 1 ? 's' : ''}`;
|
|
38
|
+
if (low && high && low !== high)
|
|
39
|
+
return `${low}\u2013${high} ${label}`;
|
|
40
|
+
return `${high !== null && high !== void 0 ? high : low} ${label}`;
|
|
41
|
+
}
|
|
42
|
+
function formatEffort(low, high, unit) {
|
|
43
|
+
var _a;
|
|
44
|
+
if (!low && !high)
|
|
45
|
+
return null;
|
|
46
|
+
const u = unit !== null && unit !== void 0 ? unit : 'hour';
|
|
47
|
+
const label = `${u}${((_a = high !== null && high !== void 0 ? high : low) !== null && _a !== void 0 ? _a : 0) !== 1 ? 's' : ''} per week`;
|
|
48
|
+
if (low && high && low !== high)
|
|
49
|
+
return `${low}\u2013${high} ${label}`;
|
|
50
|
+
return `${high !== null && high !== void 0 ? high : low} ${label}`;
|
|
51
|
+
}
|
|
52
|
+
function slugToTitle(slug) {
|
|
53
|
+
return slug
|
|
54
|
+
.split('-')
|
|
55
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
|
|
56
|
+
.join(' ');
|
|
57
|
+
}
|
|
58
|
+
const OrgLogo = ({ logoUrl, brandText }) => {
|
|
59
|
+
if (logoUrl) {
|
|
60
|
+
return (_jsx("img", { src: logoUrl, alt: brandText !== null && brandText !== void 0 ? brandText : 'Institution', className: "demo-pdp__org-logo", width: "30", height: "30" }));
|
|
61
|
+
}
|
|
62
|
+
return (_jsxs("svg", { width: "30", height: "30", viewBox: "0 0 30 30", fill: "none", "aria-hidden": "true", children: [_jsx("rect", { width: "30", height: "30", rx: "2", fill: "#A41034" }), _jsx("text", { x: "15", y: "20", textAnchor: "middle", fill: "#fff", fontSize: "16", fontWeight: "700", fontFamily: "serif", children: (brandText !== null && brandText !== void 0 ? brandText : 'E').charAt(0) })] }));
|
|
63
|
+
};
|
|
64
|
+
// ── Course Banner ──────────────────────────────────────────────────────────────
|
|
65
|
+
const CourseBannerContent = ({ course, onEarnCertificate }) => {
|
|
66
|
+
var _a;
|
|
67
|
+
const duration = formatDuration(course.tDurationLow, course.tDurationHigh, course.tDurationUnit);
|
|
68
|
+
const effort = formatEffort(course.tEffortLow, course.tEffortHigh, course.tEffortUnit);
|
|
69
|
+
const pacing = course.tPacingType === 'self_paced' ? 'Self-paced' : course.tPacingType === 'instructor_paced' ? 'Instructor-paced' : null;
|
|
70
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "demo-pdp__hero", children: [_jsx("div", { className: "demo-pdp__hero-bg" }), _jsxs("div", { className: "demo-pdp__hero-content", children: [_jsxs("div", { className: "demo-pdp__main", children: [_jsxs("div", { className: "demo-pdp__institution", children: [_jsx(OrgLogo, { logoUrl: course.tOrgLogoUrl, brandText: course.tBrandText }), _jsx("span", { className: "demo-pdp__institution-name", children: (_a = course.tBrandText) !== null && _a !== void 0 ? _a : 'edX' })] }), _jsx("h1", { className: "demo-pdp__title", children: course.maName }), course.tShortDescription && (_jsx("p", { className: "demo-pdp__meta", children: course.tShortDescription })), _jsx("button", { type: "button", className: "demo-pdp__cta-primary", onClick: onEarnCertificate, children: "Earn a certificate" })] }), _jsx("div", { className: "demo-pdp__sidebar", children: _jsxs("div", { className: "demo-pdp__card", children: [_jsx("p", { className: "demo-pdp__card-eyebrow", children: "Earn a certificate" }), _jsx("h2", { className: "demo-pdp__card-title", children: course.maName }), course.tEnrollmentCount != null && (_jsxs("p", { className: "demo-pdp__card-enrollments", children: [_jsx("strong", { children: formatNumber(course.tEnrollmentCount) }), " learners enrolled"] })), course.tLevelType && (_jsx("div", { className: "demo-pdp__card-courses", children: _jsxs("p", { className: "demo-pdp__card-courses-label", children: [course.tLevelType, " level", course.tLanguage ? ` \u00B7 ${course.tLanguage}` : ''] }) })), _jsx("button", { type: "button", className: "demo-pdp__cta-sidebar", onClick: onEarnCertificate, children: "Earn a certificate" })] }) })] })] }), _jsxs("div", { className: "demo-pdp__features", children: [course.tLevelType && (_jsxs("div", { className: "demo-pdp__feature", children: [_jsxs("svg", { className: "demo-pdp__feature-icon", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "#fff", strokeWidth: "1.5" }), _jsx("path", { d: "M12 7v5l3 3", stroke: "#fff", strokeWidth: "1.5", strokeLinecap: "round" })] }), _jsxs("div", { children: [_jsx("p", { className: "demo-pdp__feature-title", children: course.tLevelType }), _jsx("p", { className: "demo-pdp__feature-desc", children: course.tLevelType === 'Introductory' ? 'No prior experience required' : `${course.tLevelType} level course` })] })] })), pacing && (_jsxs("div", { className: "demo-pdp__feature", children: [_jsxs("svg", { className: "demo-pdp__feature-icon", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M12 2L2 7l10 5 10-5-10-5z", stroke: "#fff", strokeWidth: "1.5", strokeLinejoin: "round" }), _jsx("path", { d: "M2 17l10 5 10-5M2 12l10 5 10-5", stroke: "#fff", strokeWidth: "1.5", strokeLinejoin: "round" })] }), _jsxs("div", { children: [_jsx("p", { className: "demo-pdp__feature-title", children: pacing }), _jsx("p", { className: "demo-pdp__feature-desc", children: pacing === 'Self-paced' ? 'Progress at your own speed' : 'Follows a schedule' })] })] })), duration && (_jsxs("div", { className: "demo-pdp__feature", children: [_jsxs("svg", { className: "demo-pdp__feature-icon", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: [_jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2", stroke: "#fff", strokeWidth: "1.5" }), _jsx("path", { d: "M3 10h18", stroke: "#fff", strokeWidth: "1.5" }), _jsx("path", { d: "M8 2v4M16 2v4", stroke: "#fff", strokeWidth: "1.5", strokeLinecap: "round" })] }), _jsxs("div", { children: [_jsx("p", { className: "demo-pdp__feature-title", children: duration }), effort && _jsx("p", { className: "demo-pdp__feature-desc", children: effort })] })] }))] })] }));
|
|
71
|
+
};
|
|
72
|
+
// ── Program Banner ─────────────────────────────────────────────────────────────
|
|
73
|
+
const ProgramBannerContent = ({ program, onEarnCertificate }) => {
|
|
74
|
+
var _a, _b, _c;
|
|
75
|
+
const courses = (_a = program.tCourses) !== null && _a !== void 0 ? _a : [];
|
|
76
|
+
const firstCourse = (_b = courses[0]) !== null && _b !== void 0 ? _b : null;
|
|
77
|
+
const programName = program.tMarketingSlug ? slugToTitle(program.tMarketingSlug) : 'Certificate Program';
|
|
78
|
+
const effort = program.tEffortLow ? `${program.tEffortLow} hours per week` : null;
|
|
79
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "demo-pdp__hero", children: [_jsx("div", { className: "demo-pdp__hero-bg" }), _jsxs("div", { className: "demo-pdp__hero-content", children: [_jsxs("div", { className: "demo-pdp__main", children: [firstCourse && (_jsxs("div", { className: "demo-pdp__institution", children: [_jsx(OrgLogo, { logoUrl: firstCourse.tOrgLogoUrl, brandText: firstCourse.tBrandText }), _jsx("span", { className: "demo-pdp__institution-name", children: (_c = firstCourse.tBrandText) !== null && _c !== void 0 ? _c : 'edX' })] })), _jsx("h1", { className: "demo-pdp__title", children: programName }), _jsxs("p", { className: "demo-pdp__meta", children: [_jsxs("strong", { children: [courses.length, " course", courses.length !== 1 ? 's' : ''] }), effort ? `, ${effort}` : ''] }), _jsx("button", { type: "button", className: "demo-pdp__cta-primary", onClick: onEarnCertificate, children: "Earn a certificate" })] }), _jsx("div", { className: "demo-pdp__sidebar", children: _jsxs("div", { className: "demo-pdp__card", children: [_jsx("p", { className: "demo-pdp__card-eyebrow", children: "Earn a certificate" }), _jsx("h2", { className: "demo-pdp__card-title", children: programName }), program.tEnrollmentCount != null && (_jsxs("p", { className: "demo-pdp__card-enrollments", children: [_jsx("strong", { children: formatNumber(program.tEnrollmentCount) }), " learners enrolled"] })), courses.length > 0 && (_jsxs("div", { className: "demo-pdp__card-courses", children: [_jsxs("p", { className: "demo-pdp__card-courses-label", children: ["There ", courses.length === 1 ? 'is 1 course' : `are ${courses.length} courses`, " in this program"] }), _jsx("ul", { className: "demo-pdp__card-courses-list", children: courses.map((c) => {
|
|
80
|
+
var _a;
|
|
81
|
+
return (_jsx("li", { children: (_a = c.maName) !== null && _a !== void 0 ? _a : 'Untitled course' }, c.id));
|
|
82
|
+
}) })] })), _jsx("button", { type: "button", className: "demo-pdp__cta-sidebar", onClick: onEarnCertificate, children: "Earn a certificate" })] }) })] })] }), _jsxs("div", { className: "demo-pdp__features", children: [program.tLevelType && (_jsxs("div", { className: "demo-pdp__feature", children: [_jsxs("svg", { className: "demo-pdp__feature-icon", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: [_jsx("circle", { cx: "12", cy: "12", r: "10", stroke: "#fff", strokeWidth: "1.5" }), _jsx("path", { d: "M12 7v5l3 3", stroke: "#fff", strokeWidth: "1.5", strokeLinecap: "round" })] }), _jsxs("div", { children: [_jsx("p", { className: "demo-pdp__feature-title", children: program.tLevelType }), _jsx("p", { className: "demo-pdp__feature-desc", children: program.tLevelType === 'Introductory' ? 'No prior experience required' : `${program.tLevelType} level program` })] })] })), _jsxs("div", { className: "demo-pdp__feature", children: [_jsxs("svg", { className: "demo-pdp__feature-icon", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: [_jsx("path", { d: "M12 2L2 7l10 5 10-5-10-5z", stroke: "#fff", strokeWidth: "1.5", strokeLinejoin: "round" }), _jsx("path", { d: "M2 17l10 5 10-5M2 12l10 5 10-5", stroke: "#fff", strokeWidth: "1.5", strokeLinejoin: "round" })] }), _jsxs("div", { children: [_jsx("p", { className: "demo-pdp__feature-title", children: "Self-paced" }), _jsx("p", { className: "demo-pdp__feature-desc", children: "Progress at your own speed" })] })] }), courses.length > 0 && (_jsxs("div", { className: "demo-pdp__feature", children: [_jsxs("svg", { className: "demo-pdp__feature-icon", width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", children: [_jsx("rect", { x: "3", y: "4", width: "18", height: "16", rx: "2", stroke: "#fff", strokeWidth: "1.5" }), _jsx("path", { d: "M3 10h18", stroke: "#fff", strokeWidth: "1.5" }), _jsx("path", { d: "M8 2v4M16 2v4", stroke: "#fff", strokeWidth: "1.5", strokeLinecap: "round" })] }), _jsxs("div", { children: [_jsxs("p", { className: "demo-pdp__feature-title", children: [courses.length, " course", courses.length !== 1 ? 's' : ''] }), effort && _jsx("p", { className: "demo-pdp__feature-desc", children: effort })] })] }))] })] }));
|
|
83
|
+
};
|
|
84
|
+
const DemoPdpBanner = ({ apiUrl, edxSessionUrl, onEnroll }) => {
|
|
85
|
+
const { isLoading, isError, bannerData } = useBannerData(apiUrl, edxSessionUrl);
|
|
86
|
+
const handleEarnCertificate = (e) => {
|
|
87
|
+
e.preventDefault();
|
|
88
|
+
const url = buildCheckoutUrl();
|
|
89
|
+
window.history.pushState({}, '', url);
|
|
90
|
+
onEnroll();
|
|
91
|
+
};
|
|
92
|
+
if (isLoading) {
|
|
93
|
+
return (_jsx("div", { className: "demo-pdp", children: _jsxs("div", { className: "demo-pdp__hero", children: [_jsx("div", { className: "demo-pdp__hero-bg" }), _jsx("div", { className: "demo-pdp__hero-content", style: { justifyContent: 'center', minHeight: 300 }, children: _jsx(Spinner, { animation: "border", screenReaderText: "Loading product details\\u2026" }) })] }) }));
|
|
94
|
+
}
|
|
95
|
+
if (isError || !bannerData) {
|
|
96
|
+
return (_jsx("div", { className: "demo-pdp", children: _jsxs("div", { className: "demo-pdp__hero", children: [_jsx("div", { className: "demo-pdp__hero-bg" }), _jsx("div", { className: "demo-pdp__hero-content", children: _jsxs("div", { className: "demo-pdp__main", children: [_jsx("h1", { className: "demo-pdp__title", children: "Earn a Certificate" }), _jsx("p", { className: "demo-pdp__meta", children: "Get started with your learning journey" }), _jsx("button", { type: "button", className: "demo-pdp__cta-primary", onClick: handleEarnCertificate, children: "Earn a certificate" })] }) })] }) }));
|
|
97
|
+
}
|
|
98
|
+
return (_jsx("div", { className: "demo-pdp", children: bannerData.kind === 'course' ? (_jsx(CourseBannerContent, { course: bannerData.data, onEarnCertificate: handleEarnCertificate })) : (_jsx(ProgramBannerContent, { program: bannerData.data, onEarnCertificate: handleEarnCertificate })) }));
|
|
99
|
+
};
|
|
100
|
+
export default DemoPdpBanner;
|
|
101
|
+
//# sourceMappingURL=DemoPdpBanner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DemoPdpBanner.js","sourceRoot":"","sources":["../../../src/components/DemoPdpBanner/DemoPdpBanner.tsx"],"names":[],"mappings":"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","sourcesContent":["import React from 'react';\nimport { Spinner } from '@openedx/paragon';\nimport { useBannerData } from '../../data/hooks/useBannerData';\nimport type { BannerCourseDetail, BannerProgramDetail } from '../../types/alacarte';\n\n/**\n * Build the checkout URL from the current query parameters.\n * All four supported params (program_uid, course_uid, course_run_key,\n * discount_code) are read exclusively from the URL — nothing is hardcoded.\n */\nfunction buildCheckoutUrl(): string {\n const currentParams = new URLSearchParams(window.location.search);\n\n const courseUid = currentParams.get('course_uid') ?? '';\n const courseRunKey = currentParams.get('course_run_key') ?? '';\n const programUid = currentParams.get('program_uid') ?? '';\n const discountCode = currentParams.get('discount_code') ?? '';\n\n const params = new URLSearchParams();\n\n if (courseUid && courseRunKey) {\n params.set('course_uid', courseUid);\n params.set('course_run_key', courseRunKey);\n } else if (programUid) {\n params.set('program_uid', programUid);\n }\n\n if (discountCode) {\n params.set('discount_code', discountCode);\n }\n\n return `/aca-checkout?${params.toString()}`;\n}\n\nfunction formatNumber(n: number): string {\n return n.toLocaleString('en-US');\n}\n\nfunction formatDuration(low: number | null, high: number | null, unit: string | null): string | null {\n if (!low && !high) return null;\n const u = unit ?? 'week';\n const label = `${u}${(high ?? low ?? 0) !== 1 ? 's' : ''}`;\n if (low && high && low !== high) return `${low}\\u2013${high} ${label}`;\n return `${high ?? low} ${label}`;\n}\n\nfunction formatEffort(low: number | null, high: number | null, unit: string | null): string | null {\n if (!low && !high) return null;\n const u = unit ?? 'hour';\n const label = `${u}${(high ?? low ?? 0) !== 1 ? 's' : ''} per week`;\n if (low && high && low !== high) return `${low}\\u2013${high} ${label}`;\n return `${high ?? low} ${label}`;\n}\n\nfunction slugToTitle(slug: string): string {\n return slug\n .split('-')\n .map((word) => word.charAt(0).toUpperCase() + word.slice(1))\n .join(' ');\n}\n\nconst OrgLogo: React.FC<{ logoUrl: string | null, brandText: string | null }> = ({ logoUrl, brandText }) => {\n if (logoUrl) {\n return (\n <img\n src={logoUrl}\n alt={brandText ?? 'Institution'}\n className=\"demo-pdp__org-logo\"\n width=\"30\"\n height=\"30\"\n />\n );\n }\n return (\n <svg width=\"30\" height=\"30\" viewBox=\"0 0 30 30\" fill=\"none\" aria-hidden=\"true\">\n <rect width=\"30\" height=\"30\" rx=\"2\" fill=\"#A41034\" />\n <text x=\"15\" y=\"20\" textAnchor=\"middle\" fill=\"#fff\" fontSize=\"16\" fontWeight=\"700\" fontFamily=\"serif\">\n {(brandText ?? 'E').charAt(0)}\n </text>\n </svg>\n );\n};\n\n// ── Course Banner ──────────────────────────────────────────────────────────────\n\nconst CourseBannerContent: React.FC<{\n course: BannerCourseDetail,\n onEarnCertificate: (e: React.MouseEvent) => void,\n}> = ({ course, onEarnCertificate }) => {\n const duration = formatDuration(course.tDurationLow, course.tDurationHigh, course.tDurationUnit);\n const effort = formatEffort(course.tEffortLow, course.tEffortHigh, course.tEffortUnit);\n const pacing = course.tPacingType === 'self_paced' ? 'Self-paced' : course.tPacingType === 'instructor_paced' ? 'Instructor-paced' : null;\n\n return (\n <>\n <div className=\"demo-pdp__hero\">\n <div className=\"demo-pdp__hero-bg\" />\n <div className=\"demo-pdp__hero-content\">\n <div className=\"demo-pdp__main\">\n <div className=\"demo-pdp__institution\">\n <OrgLogo logoUrl={course.tOrgLogoUrl} brandText={course.tBrandText} />\n <span className=\"demo-pdp__institution-name\">\n {course.tBrandText ?? 'edX'}\n </span>\n </div>\n\n <h1 className=\"demo-pdp__title\">{course.maName}</h1>\n\n {course.tShortDescription && (\n <p className=\"demo-pdp__meta\">{course.tShortDescription}</p>\n )}\n\n <button\n type=\"button\"\n className=\"demo-pdp__cta-primary\"\n onClick={onEarnCertificate}\n >\n Earn a certificate\n </button>\n </div>\n\n <div className=\"demo-pdp__sidebar\">\n <div className=\"demo-pdp__card\">\n <p className=\"demo-pdp__card-eyebrow\">Earn a certificate</p>\n <h2 className=\"demo-pdp__card-title\">{course.maName}</h2>\n\n {course.tEnrollmentCount != null && (\n <p className=\"demo-pdp__card-enrollments\">\n <strong>{formatNumber(course.tEnrollmentCount)}</strong> learners enrolled\n </p>\n )}\n\n {course.tLevelType && (\n <div className=\"demo-pdp__card-courses\">\n <p className=\"demo-pdp__card-courses-label\">\n {course.tLevelType} level\n {course.tLanguage ? ` \\u00B7 ${course.tLanguage}` : ''}\n </p>\n </div>\n )}\n\n <button\n type=\"button\"\n className=\"demo-pdp__cta-sidebar\"\n onClick={onEarnCertificate}\n >\n Earn a certificate\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"demo-pdp__features\">\n {course.tLevelType && (\n <div className=\"demo-pdp__feature\">\n <svg className=\"demo-pdp__feature-icon\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"#fff\" strokeWidth=\"1.5\" />\n <path d=\"M12 7v5l3 3\" stroke=\"#fff\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n <div>\n <p className=\"demo-pdp__feature-title\">{course.tLevelType}</p>\n <p className=\"demo-pdp__feature-desc\">\n {course.tLevelType === 'Introductory' ? 'No prior experience required' : `${course.tLevelType} level course`}\n </p>\n </div>\n </div>\n )}\n {pacing && (\n <div className=\"demo-pdp__feature\">\n <svg className=\"demo-pdp__feature-icon\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M12 2L2 7l10 5 10-5-10-5z\" stroke=\"#fff\" strokeWidth=\"1.5\" strokeLinejoin=\"round\" />\n <path d=\"M2 17l10 5 10-5M2 12l10 5 10-5\" stroke=\"#fff\" strokeWidth=\"1.5\" strokeLinejoin=\"round\" />\n </svg>\n <div>\n <p className=\"demo-pdp__feature-title\">{pacing}</p>\n <p className=\"demo-pdp__feature-desc\">\n {pacing === 'Self-paced' ? 'Progress at your own speed' : 'Follows a schedule'}\n </p>\n </div>\n </div>\n )}\n {duration && (\n <div className=\"demo-pdp__feature\">\n <svg className=\"demo-pdp__feature-icon\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\" stroke=\"#fff\" strokeWidth=\"1.5\" />\n <path d=\"M3 10h18\" stroke=\"#fff\" strokeWidth=\"1.5\" />\n <path d=\"M8 2v4M16 2v4\" stroke=\"#fff\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n <div>\n <p className=\"demo-pdp__feature-title\">{duration}</p>\n {effort && <p className=\"demo-pdp__feature-desc\">{effort}</p>}\n </div>\n </div>\n )}\n </div>\n </>\n );\n};\n\n// ── Program Banner ─────────────────────────────────────────────────────────────\n\nconst ProgramBannerContent: React.FC<{\n program: BannerProgramDetail,\n onEarnCertificate: (e: React.MouseEvent) => void,\n}> = ({ program, onEarnCertificate }) => {\n const courses = program.tCourses ?? [];\n const firstCourse = courses[0] ?? null;\n const programName = program.tMarketingSlug ? slugToTitle(program.tMarketingSlug) : 'Certificate Program';\n const effort = program.tEffortLow ? `${program.tEffortLow} hours per week` : null;\n\n return (\n <>\n <div className=\"demo-pdp__hero\">\n <div className=\"demo-pdp__hero-bg\" />\n <div className=\"demo-pdp__hero-content\">\n <div className=\"demo-pdp__main\">\n {firstCourse && (\n <div className=\"demo-pdp__institution\">\n <OrgLogo logoUrl={firstCourse.tOrgLogoUrl} brandText={firstCourse.tBrandText} />\n <span className=\"demo-pdp__institution-name\">\n {firstCourse.tBrandText ?? 'edX'}\n </span>\n </div>\n )}\n\n <h1 className=\"demo-pdp__title\">{programName}</h1>\n\n <p className=\"demo-pdp__meta\">\n <strong>{courses.length} course{courses.length !== 1 ? 's' : ''}</strong>\n {effort ? `, ${effort}` : ''}\n </p>\n\n <button\n type=\"button\"\n className=\"demo-pdp__cta-primary\"\n onClick={onEarnCertificate}\n >\n Earn a certificate\n </button>\n </div>\n\n <div className=\"demo-pdp__sidebar\">\n <div className=\"demo-pdp__card\">\n <p className=\"demo-pdp__card-eyebrow\">Earn a certificate</p>\n <h2 className=\"demo-pdp__card-title\">{programName}</h2>\n\n {program.tEnrollmentCount != null && (\n <p className=\"demo-pdp__card-enrollments\">\n <strong>{formatNumber(program.tEnrollmentCount)}</strong> learners enrolled\n </p>\n )}\n\n {courses.length > 0 && (\n <div className=\"demo-pdp__card-courses\">\n <p className=\"demo-pdp__card-courses-label\">\n There {courses.length === 1 ? 'is 1 course' : `are ${courses.length} courses`} in this program\n </p>\n <ul className=\"demo-pdp__card-courses-list\">\n {courses.map((c) => (\n <li key={c.id}>{c.maName ?? 'Untitled course'}</li>\n ))}\n </ul>\n </div>\n )}\n\n <button\n type=\"button\"\n className=\"demo-pdp__cta-sidebar\"\n onClick={onEarnCertificate}\n >\n Earn a certificate\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <div className=\"demo-pdp__features\">\n {program.tLevelType && (\n <div className=\"demo-pdp__feature\">\n <svg className=\"demo-pdp__feature-icon\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\">\n <circle cx=\"12\" cy=\"12\" r=\"10\" stroke=\"#fff\" strokeWidth=\"1.5\" />\n <path d=\"M12 7v5l3 3\" stroke=\"#fff\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n <div>\n <p className=\"demo-pdp__feature-title\">{program.tLevelType}</p>\n <p className=\"demo-pdp__feature-desc\">\n {program.tLevelType === 'Introductory' ? 'No prior experience required' : `${program.tLevelType} level program`}\n </p>\n </div>\n </div>\n )}\n <div className=\"demo-pdp__feature\">\n <svg className=\"demo-pdp__feature-icon\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M12 2L2 7l10 5 10-5-10-5z\" stroke=\"#fff\" strokeWidth=\"1.5\" strokeLinejoin=\"round\" />\n <path d=\"M2 17l10 5 10-5M2 12l10 5 10-5\" stroke=\"#fff\" strokeWidth=\"1.5\" strokeLinejoin=\"round\" />\n </svg>\n <div>\n <p className=\"demo-pdp__feature-title\">Self-paced</p>\n <p className=\"demo-pdp__feature-desc\">Progress at your own speed</p>\n </div>\n </div>\n {courses.length > 0 && (\n <div className=\"demo-pdp__feature\">\n <svg className=\"demo-pdp__feature-icon\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\">\n <rect x=\"3\" y=\"4\" width=\"18\" height=\"16\" rx=\"2\" stroke=\"#fff\" strokeWidth=\"1.5\" />\n <path d=\"M3 10h18\" stroke=\"#fff\" strokeWidth=\"1.5\" />\n <path d=\"M8 2v4M16 2v4\" stroke=\"#fff\" strokeWidth=\"1.5\" strokeLinecap=\"round\" />\n </svg>\n <div>\n <p className=\"demo-pdp__feature-title\">{courses.length} course{courses.length !== 1 ? 's' : ''}</p>\n {effort && <p className=\"demo-pdp__feature-desc\">{effort}</p>}\n </div>\n </div>\n )}\n </div>\n </>\n );\n};\n\n// ── Main Banner Component ──────────────────────────────────────────────────────\n\ninterface DemoPdpBannerProps {\n apiUrl: string,\n edxSessionUrl: string,\n onEnroll: () => void,\n}\n\nconst DemoPdpBanner: React.FC<DemoPdpBannerProps> = ({ apiUrl, edxSessionUrl, onEnroll }) => {\n const { isLoading, isError, bannerData } = useBannerData(apiUrl, edxSessionUrl);\n\n const handleEarnCertificate = (e: React.MouseEvent) => {\n e.preventDefault();\n const url = buildCheckoutUrl();\n window.history.pushState({}, '', url);\n onEnroll();\n };\n\n if (isLoading) {\n return (\n <div className=\"demo-pdp\">\n <div className=\"demo-pdp__hero\">\n <div className=\"demo-pdp__hero-bg\" />\n <div className=\"demo-pdp__hero-content\" style={{ justifyContent: 'center', minHeight: 300 }}>\n <Spinner animation=\"border\" screenReaderText=\"Loading product details\\u2026\" />\n </div>\n </div>\n </div>\n );\n }\n\n if (isError || !bannerData) {\n return (\n <div className=\"demo-pdp\">\n <div className=\"demo-pdp__hero\">\n <div className=\"demo-pdp__hero-bg\" />\n <div className=\"demo-pdp__hero-content\">\n <div className=\"demo-pdp__main\">\n <h1 className=\"demo-pdp__title\">Earn a Certificate</h1>\n <p className=\"demo-pdp__meta\">Get started with your learning journey</p>\n <button\n type=\"button\"\n className=\"demo-pdp__cta-primary\"\n onClick={handleEarnCertificate}\n >\n Earn a certificate\n </button>\n </div>\n </div>\n </div>\n </div>\n );\n }\n\n return (\n <div className=\"demo-pdp\">\n {bannerData.kind === 'course' ? (\n <CourseBannerContent course={bannerData.data} onEarnCertificate={handleEarnCertificate} />\n ) : (\n <ProgramBannerContent program={bannerData.data} onEarnCertificate={handleEarnCertificate} />\n )}\n </div>\n );\n};\n\nexport default DemoPdpBanner;\n"]}
|
|
@@ -15,13 +15,16 @@ const PromoCode = ({ value, onChange, onApply, onRemove, appliedCode = null, dis
|
|
|
15
15
|
const canApply = !disabled && !isApplying && trimmed.length > 0;
|
|
16
16
|
const offerMatchesField = !trimmed
|
|
17
17
|
|| (!!offer && trimmed.toLowerCase() === offer.code.toLowerCase());
|
|
18
|
-
|
|
18
|
+
// A discount on the line without a coupon code means the backend omitted the
|
|
19
|
+
// code string but the coupon is still active — show the applied state.
|
|
20
|
+
const hasAppliedState = !!appliedCode || !!discount;
|
|
21
|
+
const showOffer = !hasAppliedState && !!offer && offerMatchesField;
|
|
19
22
|
const handleSubmit = (event) => {
|
|
20
23
|
event.preventDefault();
|
|
21
24
|
if (canApply)
|
|
22
25
|
onApply();
|
|
23
26
|
};
|
|
24
|
-
return (_jsxs("div", { className: "promo-code", children: [
|
|
27
|
+
return (_jsxs("div", { className: "promo-code", children: [hasAppliedState && discount ? (_jsx(PromoBanner, { testId: "promo-applied-banner", children: formatMessage(messages.discountAppliedBanner, { percent: discount.percent }) })) : null, appliedCode && !discount ? (_jsx(PromoBanner, { testId: "promo-applied-banner", children: formatMessage(messages.couponApplied, { code: appliedCode }) })) : null, showOffer && offer ? (_jsx(PromoBanner, { testId: "promo-offer-banner", children: formatMessage(messages.saveWithCode, { percent: offer.percent, code: offer.code }) })) : null, hasAppliedState ? (_jsxs("div", { className: "promo-code__applied", children: [appliedCode && discount ? (_jsxs("p", { className: "promo-code__celebration", children: [_jsx("span", { "aria-hidden": "true", children: "\uD83C\uDF89" }), formatMessage(messages.couponApplied, { code: appliedCode })] })) : null, discount ? (_jsxs(_Fragment, { children: [_jsx("hr", { className: "promo-code__divider" }), _jsxs("div", { className: "promo-code__discount-row", "data-testid": "promo-discount-row", children: [_jsxs("span", { className: "promo-code__discount-label", children: [formatMessage(messages.discountApplied, { percent: discount.percent }), _jsx(Icon, { src: Check, className: "promo-code__check", "aria-hidden": true })] }), onRemove ? (_jsx("button", { type: "button", className: "promo-code__remove", onClick: onRemove, disabled: disabled || isApplying, children: formatMessage(messages.remove) })) : null, _jsx("span", { className: "promo-code__amount", children: formatMessage(messages.discountAmount, {
|
|
25
28
|
amount: discount.amount.toFixed(2),
|
|
26
29
|
currency: discount.currency,
|
|
27
30
|
}) })] })] })) : onRemove && (_jsx("button", { type: "button", className: "promo-code__remove", onClick: onRemove, disabled: disabled || isApplying, children: formatMessage(messages.remove) }))] })) : (_jsxs("form", { className: "promo-code__form", onSubmit: handleSubmit, children: [_jsxs("div", { className: `promo-code__field${focused || value ? ' promo-code__field--active' : ''}${error ? ' promo-code__field--error' : ''}`, children: [_jsx("label", { htmlFor: "promoCode", className: "promo-code__label", children: formatMessage(messages.placeholder) }), _jsx("input", { id: "promoCode", type: "text", className: "promo-code__input", value: value, onChange: (e) => onChange(e.target.value), onFocus: () => setFocused(true), onBlur: () => setFocused(false), disabled: disabled || isApplying, "aria-invalid": !!error, "aria-describedby": error ? 'promoCode-error' : undefined })] }), _jsx("button", { type: "submit", disabled: !canApply, className: "promo-code__apply", children: isApplying
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PromoCode.js","sourceRoot":"","sources":["../../../src/components/PromoCode/PromoCode.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useState } from 'react';\nimport { Icon } from '@openedx/paragon';\nimport { Check, InfoOutline } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport messages from './Messages';\n\nexport interface PromoDiscount {\n /** Whole-number percent off, e.g. 15. */\n percent: number,\n amount: number,\n currency: string,\n}\n\n/** A known offer advertised before the learner applies it. */\nexport interface PromoOffer {\n code: string,\n percent: number,\n}\n\nexport interface PromoCodeProps {\n value: string,\n onChange: (value: string) => void,\n onApply: () => void,\n /** Clears an applied coupon (sends empty string to the API). */\n onRemove?: () => void,\n appliedCode?: string | null,\n /** Applied coupon's discount. Drives the applied banner and amount row. */\n discount?: PromoDiscount | null,\n /** Shown on the empty field when we already know a code and its percent. */\n offer?: PromoOffer | null,\n error?: string | null,\n isApplying?: boolean,\n disabled?: boolean,\n}\n\nconst PromoBanner = ({\n testId,\n children,\n}: {\n testId: string,\n children: React.ReactNode,\n}) => {\n const { formatMessage } = useIntl();\n return (\n <div className=\"promo-code__banner\" data-testid={testId}>\n <span className=\"promo-code__banner-text\">{children}</span>\n <Icon\n src={InfoOutline}\n className=\"promo-code__banner-icon\"\n screenReaderText={formatMessage(messages.bannerInfo)}\n />\n </div>\n );\n};\n\nconst PromoCode = ({\n value,\n onChange,\n onApply,\n onRemove,\n appliedCode = null,\n discount = null,\n offer = null,\n error = null,\n isApplying = false,\n disabled = false,\n}: PromoCodeProps) => {\n const { formatMessage } = useIntl();\n const [focused, setFocused] = useState(false);\n const trimmed = value.trim();\n const canApply = !disabled && !isApplying && trimmed.length > 0;\n const offerMatchesField = !trimmed\n || (!!offer && trimmed.toLowerCase() === offer.code.toLowerCase());\n const showOffer = !appliedCode && !!offer && offerMatchesField;\n\n const handleSubmit = (event: React.FormEvent) => {\n event.preventDefault();\n if (canApply) onApply();\n };\n\n return (\n <div className=\"promo-code\">\n {appliedCode && discount ? (\n <PromoBanner testId=\"promo-applied-banner\">\n {formatMessage(messages.discountAppliedBanner, { percent: discount.percent })}\n </PromoBanner>\n ) : null}\n {appliedCode && !discount ? (\n <PromoBanner testId=\"promo-applied-banner\">\n {formatMessage(messages.couponApplied, { code: appliedCode })}\n </PromoBanner>\n ) : null}\n {showOffer && offer ? (\n <PromoBanner testId=\"promo-offer-banner\">\n {formatMessage(messages.saveWithCode, { percent: offer.percent, code: offer.code })}\n </PromoBanner>\n ) : null}\n\n {appliedCode ? (\n <div className=\"promo-code__applied\">\n {discount ? (\n <p className=\"promo-code__celebration\">\n <span aria-hidden=\"true\">🎉</span>\n {formatMessage(messages.couponApplied, { code: appliedCode })}\n </p>\n ) : null}\n {discount ? (\n <>\n <hr className=\"promo-code__divider\" />\n <div className=\"promo-code__discount-row\" data-testid=\"promo-discount-row\">\n <span className=\"promo-code__discount-label\">\n {formatMessage(messages.discountApplied, { percent: discount.percent })}\n <Icon src={Check} className=\"promo-code__check\" aria-hidden />\n </span>\n {onRemove ? (\n <button\n type=\"button\"\n className=\"promo-code__remove\"\n onClick={onRemove}\n disabled={disabled || isApplying}\n >\n {formatMessage(messages.remove)}\n </button>\n ) : null}\n <span className=\"promo-code__amount\">\n {formatMessage(messages.discountAmount, {\n amount: discount.amount.toFixed(2),\n currency: discount.currency,\n })}\n </span>\n </div>\n </>\n ) : onRemove && (\n <button\n type=\"button\"\n className=\"promo-code__remove\"\n onClick={onRemove}\n disabled={disabled || isApplying}\n >\n {formatMessage(messages.remove)}\n </button>\n )}\n </div>\n ) : (\n <form className=\"promo-code__form\" onSubmit={handleSubmit}>\n <div className={`promo-code__field${focused || value ? ' promo-code__field--active' : ''}${error ? ' promo-code__field--error' : ''}`}>\n <label htmlFor=\"promoCode\" className=\"promo-code__label\">\n {formatMessage(messages.placeholder)}\n </label>\n <input\n id=\"promoCode\"\n type=\"text\"\n className=\"promo-code__input\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n disabled={disabled || isApplying}\n aria-invalid={!!error}\n aria-describedby={error ? 'promoCode-error' : undefined}\n />\n </div>\n <button\n type=\"submit\"\n disabled={!canApply}\n className=\"promo-code__apply\"\n >\n {isApplying\n ? formatMessage(messages.applying)\n : formatMessage(messages.apply)}\n </button>\n </form>\n )}\n {error && (\n <div id=\"promoCode-error\" className=\"promo-code__error\" role=\"alert\">\n <span className=\"promo-code__error-icon\" aria-hidden=\"true\" />\n {error}\n </div>\n )}\n </div>\n );\n};\n\nexport default PromoCode;\n"]}
|
|
1
|
+
{"version":3,"file":"PromoCode.js","sourceRoot":"","sources":["../../../src/components/PromoCode/PromoCode.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, { useState } from 'react';\nimport { Icon } from '@openedx/paragon';\nimport { Check, InfoOutline } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport messages from './Messages';\n\nexport interface PromoDiscount {\n /** Whole-number percent off, e.g. 15. */\n percent: number,\n amount: number,\n currency: string,\n}\n\n/** A known offer advertised before the learner applies it. */\nexport interface PromoOffer {\n code: string,\n percent: number,\n}\n\nexport interface PromoCodeProps {\n value: string,\n onChange: (value: string) => void,\n onApply: () => void,\n /** Clears an applied coupon (sends empty string to the API). */\n onRemove?: () => void,\n appliedCode?: string | null,\n /** Applied coupon's discount. Drives the applied banner and amount row. */\n discount?: PromoDiscount | null,\n /** Shown on the empty field when we already know a code and its percent. */\n offer?: PromoOffer | null,\n error?: string | null,\n isApplying?: boolean,\n disabled?: boolean,\n}\n\nconst PromoBanner = ({\n testId,\n children,\n}: {\n testId: string,\n children: React.ReactNode,\n}) => {\n const { formatMessage } = useIntl();\n return (\n <div className=\"promo-code__banner\" data-testid={testId}>\n <span className=\"promo-code__banner-text\">{children}</span>\n <Icon\n src={InfoOutline}\n className=\"promo-code__banner-icon\"\n screenReaderText={formatMessage(messages.bannerInfo)}\n />\n </div>\n );\n};\n\nconst PromoCode = ({\n value,\n onChange,\n onApply,\n onRemove,\n appliedCode = null,\n discount = null,\n offer = null,\n error = null,\n isApplying = false,\n disabled = false,\n}: PromoCodeProps) => {\n const { formatMessage } = useIntl();\n const [focused, setFocused] = useState(false);\n const trimmed = value.trim();\n const canApply = !disabled && !isApplying && trimmed.length > 0;\n const offerMatchesField = !trimmed\n || (!!offer && trimmed.toLowerCase() === offer.code.toLowerCase());\n // A discount on the line without a coupon code means the backend omitted the\n // code string but the coupon is still active — show the applied state.\n const hasAppliedState = !!appliedCode || !!discount;\n const showOffer = !hasAppliedState && !!offer && offerMatchesField;\n\n const handleSubmit = (event: React.FormEvent) => {\n event.preventDefault();\n if (canApply) onApply();\n };\n\n return (\n <div className=\"promo-code\">\n {hasAppliedState && discount ? (\n <PromoBanner testId=\"promo-applied-banner\">\n {formatMessage(messages.discountAppliedBanner, { percent: discount.percent })}\n </PromoBanner>\n ) : null}\n {appliedCode && !discount ? (\n <PromoBanner testId=\"promo-applied-banner\">\n {formatMessage(messages.couponApplied, { code: appliedCode })}\n </PromoBanner>\n ) : null}\n {showOffer && offer ? (\n <PromoBanner testId=\"promo-offer-banner\">\n {formatMessage(messages.saveWithCode, { percent: offer.percent, code: offer.code })}\n </PromoBanner>\n ) : null}\n\n {hasAppliedState ? (\n <div className=\"promo-code__applied\">\n {appliedCode && discount ? (\n <p className=\"promo-code__celebration\">\n <span aria-hidden=\"true\">🎉</span>\n {formatMessage(messages.couponApplied, { code: appliedCode })}\n </p>\n ) : null}\n {discount ? (\n <>\n <hr className=\"promo-code__divider\" />\n <div className=\"promo-code__discount-row\" data-testid=\"promo-discount-row\">\n <span className=\"promo-code__discount-label\">\n {formatMessage(messages.discountApplied, { percent: discount.percent })}\n <Icon src={Check} className=\"promo-code__check\" aria-hidden />\n </span>\n {onRemove ? (\n <button\n type=\"button\"\n className=\"promo-code__remove\"\n onClick={onRemove}\n disabled={disabled || isApplying}\n >\n {formatMessage(messages.remove)}\n </button>\n ) : null}\n <span className=\"promo-code__amount\">\n {formatMessage(messages.discountAmount, {\n amount: discount.amount.toFixed(2),\n currency: discount.currency,\n })}\n </span>\n </div>\n </>\n ) : onRemove && (\n <button\n type=\"button\"\n className=\"promo-code__remove\"\n onClick={onRemove}\n disabled={disabled || isApplying}\n >\n {formatMessage(messages.remove)}\n </button>\n )}\n </div>\n ) : (\n <form className=\"promo-code__form\" onSubmit={handleSubmit}>\n <div className={`promo-code__field${focused || value ? ' promo-code__field--active' : ''}${error ? ' promo-code__field--error' : ''}`}>\n <label htmlFor=\"promoCode\" className=\"promo-code__label\">\n {formatMessage(messages.placeholder)}\n </label>\n <input\n id=\"promoCode\"\n type=\"text\"\n className=\"promo-code__input\"\n value={value}\n onChange={(e) => onChange(e.target.value)}\n onFocus={() => setFocused(true)}\n onBlur={() => setFocused(false)}\n disabled={disabled || isApplying}\n aria-invalid={!!error}\n aria-describedby={error ? 'promoCode-error' : undefined}\n />\n </div>\n <button\n type=\"submit\"\n disabled={!canApply}\n className=\"promo-code__apply\"\n >\n {isApplying\n ? formatMessage(messages.applying)\n : formatMessage(messages.apply)}\n </button>\n </form>\n )}\n {error && (\n <div id=\"promoCode-error\" className=\"promo-code__error\" role=\"alert\">\n <span className=\"promo-code__error-icon\" aria-hidden=\"true\" />\n {error}\n </div>\n )}\n </div>\n );\n};\n\nexport default PromoCode;\n"]}
|
|
@@ -15,6 +15,7 @@ export declare const alacarteKeys: {
|
|
|
15
15
|
checkout: () => readonly ["alacarte", "checkout"];
|
|
16
16
|
ownedProduct: (productId: string) => readonly ["alacarte", "ownedProduct", string];
|
|
17
17
|
productDetail: (key: string) => readonly ["alacarte", "productDetail", string];
|
|
18
|
+
bannerDetail: (key: string) => readonly ["alacarte", "bannerDetail", string];
|
|
18
19
|
placedOrder: (biId: string) => readonly ["alacarte", "placedOrder", string];
|
|
19
20
|
orderDates: (biId: string) => readonly ["alacarte", "orderDates", string];
|
|
20
21
|
};
|
package/dist/data/hooks/keys.js
CHANGED
|
@@ -15,6 +15,7 @@ export const alacarteKeys = {
|
|
|
15
15
|
checkout: () => ['alacarte', 'checkout'],
|
|
16
16
|
ownedProduct: (productId) => ['alacarte', 'ownedProduct', productId],
|
|
17
17
|
productDetail: (key) => ['alacarte', 'productDetail', key],
|
|
18
|
+
bannerDetail: (key) => ['alacarte', 'bannerDetail', key],
|
|
18
19
|
placedOrder: (biId) => ['alacarte', 'placedOrder', biId],
|
|
19
20
|
orderDates: (biId) => ['alacarte', 'orderDates', biId],
|
|
20
21
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../src/data/hooks/keys.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,GAAG,EAAE,CAAC,UAAU,CAAU;IAC1B,MAAM,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAU;IACvE,YAAY,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,CAAU;IAC/F,cAAc,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,gBAAgB,EAAE,QAAQ,CAAU;IACvF,QAAQ,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,SAAS,CAAU;IAC7E,OAAO,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,IAAI,CAAU;IACjE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,UAAU,EAAE,aAAa,CAAU;IACvD,YAAY,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,SAAS,CAAU;CACtF,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,UAAU,EAAE,UAAU,CAAU;IACjD,YAAY,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,SAAS,CAAU;IACrF,aAAa,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,eAAe,EAAE,GAAG,CAAU;IAC3E,WAAW,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,aAAa,EAAE,IAAI,CAAU;IACzE,UAAU,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,YAAY,EAAE,IAAI,CAAU;CACxE,CAAC","sourcesContent":["/**\n * query/keys.ts — typed query-key factory for the checkout module.\n */\nexport const checkoutKeys = {\n all: ['checkout'] as const,\n status: (cartBiId: string) => ['checkout', 'status', cartBiId] as const,\n clientSecret: (subscriptionId: string) => ['checkout', 'clientSecret', subscriptionId] as const,\n subscriptionId: (cartBiId: string) => ['checkout', 'subscriptionId', cartBiId] as const,\n customer: (userEmail: string) => ['checkout', 'customer', userEmail] as const,\n salable: (maId: string) => ['checkout', 'salable', maId] as const,\n viaxSession: () => ['checkout', 'viaxSession'] as const,\n courseDetail: (courseKey: string) => ['checkout', 'courseDetail', courseKey] as const,\n};\n\nexport const alacarteKeys = {\n checkout: () => ['alacarte', 'checkout'] as const,\n ownedProduct: (productId: string) => ['alacarte', 'ownedProduct', productId] as const,\n productDetail: (key: string) => ['alacarte', 'productDetail', key] as const,\n placedOrder: (biId: string) => ['alacarte', 'placedOrder', biId] as const,\n orderDates: (biId: string) => ['alacarte', 'orderDates', biId] as const,\n};\n"]}
|
|
1
|
+
{"version":3,"file":"keys.js","sourceRoot":"","sources":["../../../src/data/hooks/keys.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,GAAG,EAAE,CAAC,UAAU,CAAU;IAC1B,MAAM,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,QAAQ,CAAU;IACvE,YAAY,EAAE,CAAC,cAAsB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,cAAc,CAAU;IAC/F,cAAc,EAAE,CAAC,QAAgB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,gBAAgB,EAAE,QAAQ,CAAU;IACvF,QAAQ,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,UAAU,EAAE,SAAS,CAAU;IAC7E,OAAO,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,SAAS,EAAE,IAAI,CAAU;IACjE,WAAW,EAAE,GAAG,EAAE,CAAC,CAAC,UAAU,EAAE,aAAa,CAAU;IACvD,YAAY,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,SAAS,CAAU;CACtF,CAAC;AAEF,MAAM,CAAC,MAAM,YAAY,GAAG;IAC1B,QAAQ,EAAE,GAAG,EAAE,CAAC,CAAC,UAAU,EAAE,UAAU,CAAU;IACjD,YAAY,EAAE,CAAC,SAAiB,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,SAAS,CAAU;IACrF,aAAa,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,eAAe,EAAE,GAAG,CAAU;IAC3E,YAAY,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,cAAc,EAAE,GAAG,CAAU;IACzE,WAAW,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,aAAa,EAAE,IAAI,CAAU;IACzE,UAAU,EAAE,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,UAAU,EAAE,YAAY,EAAE,IAAI,CAAU;CACxE,CAAC","sourcesContent":["/**\n * query/keys.ts — typed query-key factory for the checkout module.\n */\nexport const checkoutKeys = {\n all: ['checkout'] as const,\n status: (cartBiId: string) => ['checkout', 'status', cartBiId] as const,\n clientSecret: (subscriptionId: string) => ['checkout', 'clientSecret', subscriptionId] as const,\n subscriptionId: (cartBiId: string) => ['checkout', 'subscriptionId', cartBiId] as const,\n customer: (userEmail: string) => ['checkout', 'customer', userEmail] as const,\n salable: (maId: string) => ['checkout', 'salable', maId] as const,\n viaxSession: () => ['checkout', 'viaxSession'] as const,\n courseDetail: (courseKey: string) => ['checkout', 'courseDetail', courseKey] as const,\n};\n\nexport const alacarteKeys = {\n checkout: () => ['alacarte', 'checkout'] as const,\n ownedProduct: (productId: string) => ['alacarte', 'ownedProduct', productId] as const,\n productDetail: (key: string) => ['alacarte', 'productDetail', key] as const,\n bannerDetail: (key: string) => ['alacarte', 'bannerDetail', key] as const,\n placedOrder: (biId: string) => ['alacarte', 'placedOrder', biId] as const,\n orderDates: (biId: string) => ['alacarte', 'orderDates', biId] as const,\n};\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { BannerData } from '../../types/alacarte';
|
|
2
|
+
export interface UseBannerDataResult {
|
|
3
|
+
isLoading: boolean;
|
|
4
|
+
isError: boolean;
|
|
5
|
+
error: string | null;
|
|
6
|
+
bannerData: BannerData | null;
|
|
7
|
+
}
|
|
8
|
+
export declare function useBannerData(apiUrl: string, edxSessionUrl: string): UseBannerDataResult;
|