@edx/frontend-app-subscription-manage 1.18.1 → 1.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (107) hide show
  1. package/dist/Main.js +8 -2
  2. package/dist/Main.js.map +1 -1
  3. package/dist/components/ActiveSubscription/ActiveSubscription.js +5 -5
  4. package/dist/components/ActiveSubscription/ActiveSubscription.js.map +1 -1
  5. package/dist/components/BecomeSubscriber/BecomeSubscriber.js +3 -3
  6. package/dist/components/BecomeSubscriber/BecomeSubscriber.js.map +1 -1
  7. package/dist/components/CancelConfirmation/CancelConfirmation.js +6 -6
  8. package/dist/components/CancelConfirmation/CancelConfirmation.js.map +1 -1
  9. package/dist/components/CancelSuccess/CancelSuccess.js +3 -3
  10. package/dist/components/CancelSuccess/CancelSuccess.js.map +1 -1
  11. package/dist/components/CanceledSubscription/CanceledSubscription.js +8 -9
  12. package/dist/components/CanceledSubscription/CanceledSubscription.js.map +1 -1
  13. package/dist/components/EnrolledCoursesList/EnrolledCoursesList.js +2 -2
  14. package/dist/components/EnrolledCoursesList/EnrolledCoursesList.js.map +1 -1
  15. package/dist/components/HelpFooter/HelpFooter.js +1 -1
  16. package/dist/components/HelpFooter/HelpFooter.js.map +1 -1
  17. package/dist/components/OrderHistory/OrderHistoryCard.d.ts +11 -0
  18. package/dist/components/OrderHistory/OrderHistoryCard.js +6 -0
  19. package/dist/components/OrderHistory/OrderHistoryCard.js.map +1 -0
  20. package/dist/components/OrderHistory/OrderHistoryDetails.d.ts +2 -0
  21. package/dist/components/OrderHistory/OrderHistoryDetails.js +120 -0
  22. package/dist/components/OrderHistory/OrderHistoryDetails.js.map +1 -0
  23. package/dist/components/OrderHistory/OrderList.d.ts +3 -0
  24. package/dist/components/OrderHistory/OrderList.js +13 -0
  25. package/dist/components/OrderHistory/OrderList.js.map +1 -0
  26. package/dist/components/OrderHistory/OrderPurchases.d.ts +4 -0
  27. package/dist/components/OrderHistory/OrderPurchases.js +44 -0
  28. package/dist/components/OrderHistory/OrderPurchases.js.map +1 -0
  29. package/dist/components/OrderHistory/ProgramItemsTable.d.ts +7 -0
  30. package/dist/components/OrderHistory/ProgramItemsTable.js +49 -0
  31. package/dist/components/OrderHistory/ProgramItemsTable.js.map +1 -0
  32. package/dist/components/OrderHistory/data/receiptRows.d.ts +17 -0
  33. package/dist/components/OrderHistory/data/receiptRows.js +16 -0
  34. package/dist/components/OrderHistory/data/receiptRows.js.map +1 -0
  35. package/dist/components/OrderHistory/messages.d.ts +118 -0
  36. package/dist/components/OrderHistory/messages.js +120 -0
  37. package/dist/components/OrderHistory/messages.js.map +1 -0
  38. package/dist/components/OrderHistoryHeader/OrderHistoryHeader.js +1 -1
  39. package/dist/components/OrderHistoryHeader/OrderHistoryHeader.js.map +1 -1
  40. package/dist/components/PaymentHistory/PaymentHistory.js +5 -5
  41. package/dist/components/PaymentHistory/PaymentHistory.js.map +1 -1
  42. package/dist/components/RefundNotice/RefundNotice.d.ts +3 -0
  43. package/dist/components/RefundNotice/RefundNotice.js +15 -0
  44. package/dist/components/RefundNotice/RefundNotice.js.map +1 -0
  45. package/dist/components/RefundNotice/messages.d.ts +18 -0
  46. package/dist/components/RefundNotice/messages.js +20 -0
  47. package/dist/components/RefundNotice/messages.js.map +1 -0
  48. package/dist/components/RenewSubscriptionModal/RenewSubscriptionModal.js +12 -12
  49. package/dist/components/RenewSubscriptionModal/RenewSubscriptionModal.js.map +1 -1
  50. package/dist/components/ResubscribeCard/ResubscribeCard.js +5 -5
  51. package/dist/components/ResubscribeCard/ResubscribeCard.js.map +1 -1
  52. package/dist/components/UpdatePaymentMethodModal/UpdatePaymentMethodModal.js +12 -12
  53. package/dist/components/UpdatePaymentMethodModal/UpdatePaymentMethodModal.js.map +1 -1
  54. package/dist/components/messages.d.ts +89 -99
  55. package/dist/components/messages.js +180 -190
  56. package/dist/components/messages.js.map +1 -1
  57. package/dist/constants.d.ts +7 -1
  58. package/dist/constants.js +7 -1
  59. package/dist/constants.js.map +1 -1
  60. package/dist/data/hooks/keys.d.ts +5 -0
  61. package/dist/data/hooks/keys.js +5 -0
  62. package/dist/data/hooks/keys.js.map +1 -1
  63. package/dist/data/hooks/useOrderHistoryQueries.d.ts +11 -0
  64. package/dist/data/hooks/useOrderHistoryQueries.js +30 -0
  65. package/dist/data/hooks/useOrderHistoryQueries.js.map +1 -0
  66. package/dist/data/services/orderHistory/orderHistory.d.ts +7 -0
  67. package/dist/data/services/orderHistory/orderHistory.js +40 -0
  68. package/dist/data/services/orderHistory/orderHistory.js.map +1 -0
  69. package/dist/data/services/segment/AnalyticsPageWrap.d.ts +9 -0
  70. package/dist/data/services/segment/AnalyticsPageWrap.js +14 -0
  71. package/dist/data/services/segment/AnalyticsPageWrap.js.map +1 -0
  72. package/dist/data/services/segment/index.js +4 -1
  73. package/dist/data/services/segment/index.js.map +1 -1
  74. package/dist/{manageSubscriptionPage → orderHistory}/CancelSubscriptionPage.js +4 -4
  75. package/dist/orderHistory/CancelSubscriptionPage.js.map +1 -0
  76. package/dist/{manageSubscriptionPage → orderHistory}/CancelSuccessPage.js +2 -2
  77. package/dist/orderHistory/CancelSuccessPage.js.map +1 -0
  78. package/dist/orderHistory/OrderHistoryPage.d.ts +3 -0
  79. package/dist/orderHistory/OrderHistoryPage.js +127 -0
  80. package/dist/orderHistory/OrderHistoryPage.js.map +1 -0
  81. package/dist/routes.d.ts +23 -2
  82. package/dist/routes.js +44 -19
  83. package/dist/routes.js.map +1 -1
  84. package/dist/style.scss +1 -1
  85. package/dist/styles/components/{manageSubscription.scss → orderHistory.scss} +60 -24
  86. package/dist/tracking/trackingContext.d.ts +1 -1
  87. package/dist/tracking/trackingContext.js +1 -1
  88. package/dist/tracking/trackingContext.js.map +1 -1
  89. package/dist/types.d.ts +55 -0
  90. package/dist/types.js.map +1 -1
  91. package/dist/utils/currency.d.ts +6 -0
  92. package/dist/utils/currency.js +6 -0
  93. package/dist/utils/currency.js.map +1 -0
  94. package/dist/utils/queries.d.ts +6 -0
  95. package/dist/utils/queries.js +12 -0
  96. package/dist/utils/queries.js.map +1 -1
  97. package/package.json +1 -1
  98. package/dist/components/SubscriptionTabs/SubscriptionTabs.d.ts +0 -8
  99. package/dist/components/SubscriptionTabs/SubscriptionTabs.js +0 -15
  100. package/dist/components/SubscriptionTabs/SubscriptionTabs.js.map +0 -1
  101. package/dist/manageSubscriptionPage/CancelSubscriptionPage.js.map +0 -1
  102. package/dist/manageSubscriptionPage/CancelSuccessPage.js.map +0 -1
  103. package/dist/manageSubscriptionPage/ManageSubscriptionPage.d.ts +0 -3
  104. package/dist/manageSubscriptionPage/ManageSubscriptionPage.js +0 -88
  105. package/dist/manageSubscriptionPage/ManageSubscriptionPage.js.map +0 -1
  106. /package/dist/{manageSubscriptionPage → orderHistory}/CancelSubscriptionPage.d.ts +0 -0
  107. /package/dist/{manageSubscriptionPage → orderHistory}/CancelSuccessPage.d.ts +0 -0
@@ -0,0 +1,127 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useState, useCallback, } from 'react';
3
+ import { Alert, Container, Spinner, Tabs, Tab, } from '@openedx/paragon';
4
+ import { useIntl } from '@openedx/frontend-base';
5
+ import { useSearchParams } from 'react-router-dom';
6
+ import ActiveSubscription from '../components/ActiveSubscription/ActiveSubscription';
7
+ import BecomeSubscriber from '../components/BecomeSubscriber/BecomeSubscriber';
8
+ import CanceledSubscription, { RenewBanner, } from '../components/CanceledSubscription/CanceledSubscription';
9
+ import HelpFooter from '../components/HelpFooter/HelpFooter';
10
+ import OrderHistoryHeader from '../components/OrderHistoryHeader/OrderHistoryHeader';
11
+ import OrderPurchases from '../components/OrderHistory/OrderPurchases';
12
+ import PaymentHistory from '../components/PaymentHistory/PaymentHistory';
13
+ import RenewSubscriptionModal from '../components/RenewSubscriptionModal/RenewSubscriptionModal';
14
+ import ResubscribeCard from '../components/ResubscribeCard/ResubscribeCard';
15
+ import messages from '../components/messages';
16
+ import { useBillingPlansQuery, useIsSubscriberQuery, usePaymentHistoryQuery, useReactivateSubscriptionMutation, useViaxClient, useViaxSession, } from '../data/hooks/useManageQueries';
17
+ import { getPaymentHistorySubscriptionIds } from '../data/services/subscription/getPaymentHistory';
18
+ import { getPlanPrice } from '../utils/billingDetails';
19
+ import { formatDisplayDate } from '../utils/formatDate';
20
+ import { resolvePlanLabel, resolveSubscriptionView, } from '../utils/subscriptionView';
21
+ const OrderHistoryPage = () => {
22
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
23
+ const { formatMessage } = useIntl();
24
+ const [searchParams, setSearchParams] = useSearchParams();
25
+ const [isRenewModalOpen, setIsRenewModalOpen] = useState(false);
26
+ // Initialize active tab from URL.
27
+ const [activeTab, setActiveTab] = useState(searchParams.get('tab') === 'payment-history'
28
+ ? 'payment-history'
29
+ : 'subscription');
30
+ const client = useViaxClient();
31
+ const { sessionReady, isSessionLoading, isSessionError, } = useViaxSession(client);
32
+ const subscriberQuery = useIsSubscriberQuery(client, sessionReady);
33
+ const billingPlansQuery = useBillingPlansQuery(client, sessionReady);
34
+ const reactivateMutation = useReactivateSubscriptionMutation(client);
35
+ const subscriptionBiId = (_b = (_a = subscriberQuery.data) === null || _a === void 0 ? void 0 : _a.subscriptionBiId) !== null && _b !== void 0 ? _b : null;
36
+ const paymentHistoryQuery = usePaymentHistoryQuery(client, sessionReady, getPaymentHistorySubscriptionIds(subscriberQuery.data));
37
+ const subscriber = (_c = subscriberQuery.data) !== null && _c !== void 0 ? _c : null;
38
+ const planCycle = resolvePlanLabel(subscriber);
39
+ const planPrice = getPlanPrice((_e = (_d = billingPlansQuery.data) === null || _d === void 0 ? void 0 : _d.plans) !== null && _e !== void 0 ? _e : [], planCycle);
40
+ const price = (_f = planPrice === null || planPrice === void 0 ? void 0 : planPrice.amount) !== null && _f !== void 0 ? _f : null;
41
+ const currency = (_g = planPrice === null || planPrice === void 0 ? void 0 : planPrice.currency) !== null && _g !== void 0 ? _g : null;
42
+ const renewalDate = formatDisplayDate((_h = subscriber === null || subscriber === void 0 ? void 0 : subscriber.currentPeriodEnd) !== null && _h !== void 0 ? _h : subscriber === null || subscriber === void 0 ? void 0 : subscriber.subscriptionEndDate);
43
+ const handleOpenRenewModal = useCallback(() => {
44
+ if (!subscriptionBiId) {
45
+ return;
46
+ }
47
+ reactivateMutation.reset();
48
+ setIsRenewModalOpen(true);
49
+ }, [subscriptionBiId, reactivateMutation]);
50
+ const handleConfirmRenew = useCallback(() => {
51
+ if (!subscriptionBiId || reactivateMutation.isPending) {
52
+ return;
53
+ }
54
+ reactivateMutation.mutate(subscriptionBiId);
55
+ }, [subscriptionBiId, reactivateMutation]);
56
+ useEffect(() => {
57
+ if (reactivateMutation.isSuccess) {
58
+ setIsRenewModalOpen(false);
59
+ }
60
+ }, [reactivateMutation.isSuccess]);
61
+ // Synchronize active tab with URL changes.
62
+ useEffect(() => {
63
+ const tab = searchParams.get('tab');
64
+ setActiveTab(tab === 'payment-history' ? 'payment-history' : 'subscription');
65
+ }, [searchParams]);
66
+ const handleTabSelect = useCallback((tabKey) => {
67
+ if (tabKey === 'subscription' || tabKey === 'payment-history') {
68
+ setActiveTab(tabKey);
69
+ setSearchParams(tabKey === 'subscription' ? {} : { tab: tabKey }, { replace: true });
70
+ }
71
+ }, [setSearchParams]);
72
+ const isLoading = isSessionLoading || (sessionReady && subscriberQuery.isPending);
73
+ const isError = isSessionError || subscriberQuery.isError;
74
+ const resolvedView = resolveSubscriptionView(subscriber);
75
+ const showRenewBanner = resolvedView === 'canceled_access';
76
+ // Resolve subscription panel.
77
+ const subscriptionPanel = useMemo(() => {
78
+ switch (resolvedView) {
79
+ case 'active':
80
+ return _jsx(ActiveSubscription, { subscriber: subscriber });
81
+ case 'canceled_access':
82
+ return (_jsx(CanceledSubscription, { subscriber: subscriber, onRenew: handleOpenRenewModal }));
83
+ case 'canceled_expired':
84
+ return (_jsx(ResubscribeCard, { subscriber: subscriber, price: price, currency: currency }));
85
+ case 'none':
86
+ default:
87
+ return (_jsx(BecomeSubscriber, { price: price, currency: currency }));
88
+ }
89
+ }, [
90
+ resolvedView,
91
+ subscriber,
92
+ price,
93
+ currency,
94
+ handleOpenRenewModal,
95
+ ]);
96
+ // Define tabs dynamically.
97
+ const orderHistoryTabs = useMemo(() => {
98
+ var _a;
99
+ return [
100
+ {
101
+ key: 'subscription',
102
+ title: 'Manage subscription',
103
+ panel: (_jsxs(_Fragment, { children: [subscriptionPanel, _jsx(HelpFooter, {})] })),
104
+ },
105
+ {
106
+ key: 'payment-history',
107
+ title: 'Payment history',
108
+ panel: (_jsxs(_Fragment, { children: [_jsx(PaymentHistory, { subscriber: subscriber, invoices: (_a = paymentHistoryQuery.data) !== null && _a !== void 0 ? _a : [], isLoading: paymentHistoryQuery.isPending, isError: paymentHistoryQuery.isError }), _jsx(OrderPurchases, {}), _jsx(HelpFooter, {})] })),
109
+ },
110
+ ];
111
+ }, [
112
+ subscriptionPanel,
113
+ subscriber,
114
+ paymentHistoryQuery.data,
115
+ paymentHistoryQuery.isPending,
116
+ paymentHistoryQuery.isError,
117
+ ]);
118
+ if (isLoading) {
119
+ return (_jsx("div", { className: "manage-page manage-page--loading d-flex justify-content-center align-items-center", children: _jsx(Spinner, { animation: "border", screenReaderText: formatMessage(messages['order-history.loading']) }) }));
120
+ }
121
+ if (isError) {
122
+ return (_jsx("div", { className: "manage-page", children: _jsx(Container, { size: "lg", className: "manage-page__inner px-3 px-md-4 py-5", children: _jsx(Alert, { variant: "danger", children: formatMessage(messages['order-history.error']) }) }) }));
123
+ }
124
+ return (_jsxs("div", { className: "manage-page", children: [showRenewBanner && (_jsx("div", { className: "manage-renew-banner py-5 mb-2", children: _jsx(Container, { size: "lg", className: "px-3 px-md-4", children: _jsx("div", { className: "manage-renew-banner__inner d-flex flex-column align-items-start", children: _jsx(RenewBanner, { expireDate: renewalDate, onRenew: handleOpenRenewModal }) }) }) })), _jsxs(Container, { size: "lg", className: "manage-page__inner px-3 px-md-4 py-5", children: [_jsx(OrderHistoryHeader, {}), _jsx(Tabs, { id: "order-history-tabs", defaultActiveKey: (_j = orderHistoryTabs[0]) === null || _j === void 0 ? void 0 : _j.key, activeKey: activeTab, onSelect: handleTabSelect, className: "mb-4", mountOnEnter: true, children: orderHistoryTabs.map(tab => (_jsx(Tab, { eventKey: tab.key, title: tab.title, children: tab.panel }, tab.key))) })] }), _jsx(RenewSubscriptionModal, { isOpen: isRenewModalOpen, onClose: () => setIsRenewModalOpen(false), onConfirm: handleConfirmRenew, isConfirming: reactivateMutation.isPending, confirmError: reactivateMutation.isError, subscriptionBiId: subscriptionBiId, renewalDate: renewalDate, price: price, currency: currency, cycle: planCycle })] }));
125
+ };
126
+ export default OrderHistoryPage;
127
+ //# sourceMappingURL=OrderHistoryPage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OrderHistoryPage.js","sourceRoot":"","sources":["../../src/orderHistory/OrderHistoryPage.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {\n useEffect,\n useMemo,\n useState,\n useCallback,\n} from 'react';\nimport {\n Alert,\n Container,\n Spinner,\n Tabs,\n Tab,\n} from '@openedx/paragon';\nimport { useIntl } from '@openedx/frontend-base';\nimport { useSearchParams } from 'react-router-dom';\n\nimport ActiveSubscription from '../components/ActiveSubscription/ActiveSubscription';\nimport BecomeSubscriber from '../components/BecomeSubscriber/BecomeSubscriber';\nimport CanceledSubscription, {\n RenewBanner,\n} from '../components/CanceledSubscription/CanceledSubscription';\nimport HelpFooter from '../components/HelpFooter/HelpFooter';\nimport OrderHistoryHeader from '../components/OrderHistoryHeader/OrderHistoryHeader';\nimport OrderPurchases from '../components/OrderHistory/OrderPurchases';\nimport PaymentHistory from '../components/PaymentHistory/PaymentHistory';\nimport RenewSubscriptionModal from '../components/RenewSubscriptionModal/RenewSubscriptionModal';\nimport ResubscribeCard from '../components/ResubscribeCard/ResubscribeCard';\nimport messages from '../components/messages';\nimport {\n useBillingPlansQuery,\n useIsSubscriberQuery,\n usePaymentHistoryQuery,\n useReactivateSubscriptionMutation,\n useViaxClient,\n useViaxSession,\n} from '../data/hooks/useManageQueries';\nimport { getPaymentHistorySubscriptionIds } from '../data/services/subscription/getPaymentHistory';\nimport { getPlanPrice } from '../utils/billingDetails';\nimport { formatDisplayDate } from '../utils/formatDate';\nimport {\n resolvePlanLabel,\n resolveSubscriptionView,\n} from '../utils/subscriptionView';\n\nconst OrderHistoryPage: React.FC = () => {\n const { formatMessage } = useIntl();\n const [searchParams, setSearchParams] = useSearchParams();\n\n const [isRenewModalOpen, setIsRenewModalOpen] = useState(false);\n\n // Initialize active tab from URL.\n const [activeTab, setActiveTab] = useState(\n searchParams.get('tab') === 'payment-history'\n ? 'payment-history'\n : 'subscription',\n );\n\n const client = useViaxClient();\n\n const {\n sessionReady,\n isSessionLoading,\n isSessionError,\n } = useViaxSession(client);\n\n const subscriberQuery = useIsSubscriberQuery(client, sessionReady);\n const billingPlansQuery = useBillingPlansQuery(client, sessionReady);\n const reactivateMutation = useReactivateSubscriptionMutation(client);\n\n const subscriptionBiId = subscriberQuery.data?.subscriptionBiId ?? null;\n\n const paymentHistoryQuery = usePaymentHistoryQuery(\n client,\n sessionReady,\n getPaymentHistorySubscriptionIds(subscriberQuery.data),\n );\n\n const subscriber = subscriberQuery.data ?? null;\n\n const planCycle = resolvePlanLabel(subscriber);\n\n const planPrice = getPlanPrice(\n billingPlansQuery.data?.plans ?? [],\n planCycle,\n );\n\n const price = planPrice?.amount ?? null;\n const currency = planPrice?.currency ?? null;\n\n const renewalDate = formatDisplayDate(\n subscriber?.currentPeriodEnd ?? subscriber?.subscriptionEndDate,\n );\n\n const handleOpenRenewModal = useCallback(() => {\n if (!subscriptionBiId) {\n return;\n }\n\n reactivateMutation.reset();\n setIsRenewModalOpen(true);\n }, [subscriptionBiId, reactivateMutation]);\n\n const handleConfirmRenew = useCallback(() => {\n if (!subscriptionBiId || reactivateMutation.isPending) {\n return;\n }\n\n reactivateMutation.mutate(subscriptionBiId);\n }, [subscriptionBiId, reactivateMutation]);\n\n useEffect(() => {\n if (reactivateMutation.isSuccess) {\n setIsRenewModalOpen(false);\n }\n }, [reactivateMutation.isSuccess]);\n\n // Synchronize active tab with URL changes.\n useEffect(() => {\n const tab = searchParams.get('tab');\n\n setActiveTab(\n tab === 'payment-history' ? 'payment-history' : 'subscription',\n );\n }, [searchParams]);\n\n const handleTabSelect = useCallback((tabKey: string | null) => {\n if (tabKey === 'subscription' || tabKey === 'payment-history') {\n setActiveTab(tabKey);\n\n setSearchParams(\n tabKey === 'subscription' ? {} : { tab: tabKey },\n { replace: true },\n );\n }\n }, [setSearchParams]);\n\n const isLoading = isSessionLoading || (sessionReady && subscriberQuery.isPending);\n const isError = isSessionError || subscriberQuery.isError;\n\n const resolvedView = resolveSubscriptionView(subscriber);\n const showRenewBanner = resolvedView === 'canceled_access';\n\n // Resolve subscription panel.\n const subscriptionPanel = useMemo(() => {\n switch (resolvedView) {\n case 'active':\n return <ActiveSubscription subscriber={subscriber} />;\n\n case 'canceled_access':\n return (\n <CanceledSubscription\n subscriber={subscriber}\n onRenew={handleOpenRenewModal}\n />\n );\n\n case 'canceled_expired':\n return (\n <ResubscribeCard\n subscriber={subscriber}\n price={price}\n currency={currency}\n />\n );\n\n case 'none':\n default:\n return (\n <BecomeSubscriber\n price={price}\n currency={currency}\n />\n );\n }\n }, [\n resolvedView,\n subscriber,\n price,\n currency,\n handleOpenRenewModal,\n ]);\n\n // Define tabs dynamically.\n const orderHistoryTabs = useMemo(() => [\n {\n key: 'subscription',\n title: 'Manage subscription',\n panel: (\n <>\n {subscriptionPanel}\n <HelpFooter />\n </>\n ),\n },\n {\n key: 'payment-history',\n title: 'Payment history',\n panel: (\n <>\n <PaymentHistory\n subscriber={subscriber}\n invoices={paymentHistoryQuery.data ?? []}\n isLoading={paymentHistoryQuery.isPending}\n isError={paymentHistoryQuery.isError}\n />\n <OrderPurchases />\n <HelpFooter />\n </>\n ),\n },\n ], [\n subscriptionPanel,\n subscriber,\n paymentHistoryQuery.data,\n paymentHistoryQuery.isPending,\n paymentHistoryQuery.isError,\n ]);\n\n if (isLoading) {\n return (\n <div className=\"manage-page manage-page--loading d-flex justify-content-center align-items-center\">\n <Spinner\n animation=\"border\"\n screenReaderText={formatMessage(messages['order-history.loading'])}\n />\n </div>\n );\n }\n\n if (isError) {\n return (\n <div className=\"manage-page\">\n <Container size=\"lg\" className=\"manage-page__inner px-3 px-md-4 py-5\">\n <Alert variant=\"danger\">\n {formatMessage(messages['order-history.error'])}\n </Alert>\n </Container>\n </div>\n );\n }\n\n return (\n <div className=\"manage-page\">\n {showRenewBanner && (\n <div className=\"manage-renew-banner py-5 mb-2\">\n <Container size=\"lg\" className=\"px-3 px-md-4\">\n <div className=\"manage-renew-banner__inner d-flex flex-column align-items-start\">\n <RenewBanner\n expireDate={renewalDate}\n onRenew={handleOpenRenewModal}\n />\n </div>\n </Container>\n </div>\n )}\n\n <Container size=\"lg\" className=\"manage-page__inner px-3 px-md-4 py-5\">\n <OrderHistoryHeader />\n\n <Tabs\n id=\"order-history-tabs\"\n defaultActiveKey={orderHistoryTabs[0]?.key}\n activeKey={activeTab}\n onSelect={handleTabSelect}\n className=\"mb-4\"\n mountOnEnter\n >\n {orderHistoryTabs.map(tab => (\n <Tab\n eventKey={tab.key}\n title={tab.title}\n key={tab.key}\n >\n {tab.panel}\n </Tab>\n ))}\n </Tabs>\n </Container>\n\n <RenewSubscriptionModal\n isOpen={isRenewModalOpen}\n onClose={() => setIsRenewModalOpen(false)}\n onConfirm={handleConfirmRenew}\n isConfirming={reactivateMutation.isPending}\n confirmError={reactivateMutation.isError}\n subscriptionBiId={subscriptionBiId}\n renewalDate={renewalDate}\n price={price}\n currency={currency}\n cycle={planCycle}\n />\n </div>\n );\n};\n\nexport default OrderHistoryPage;\n"]}
package/dist/routes.d.ts CHANGED
@@ -1,7 +1,11 @@
1
- declare const routes: {
1
+ declare const routes: ({
2
2
  id: string;
3
3
  path: string;
4
4
  loader: (args: any) => Promise<null>;
5
+ shouldRevalidate: ({ currentUrl, nextUrl }: {
6
+ currentUrl: any;
7
+ nextUrl: any;
8
+ }) => boolean;
5
9
  handle: {
6
10
  roles: string[];
7
11
  };
@@ -21,5 +25,22 @@ declare const routes: {
21
25
  }>;
22
26
  index?: undefined;
23
27
  })[];
24
- }[];
28
+ } | {
29
+ id: string;
30
+ path: string;
31
+ loader: (args: any) => Promise<null>;
32
+ handle: {
33
+ roles: string[];
34
+ };
35
+ lazy(): Promise<{
36
+ Component: () => import("react/jsx-runtime").JSX.Element;
37
+ }>;
38
+ children: {
39
+ index: boolean;
40
+ lazy(): Promise<{
41
+ Component: () => import("react/jsx-runtime").JSX.Element;
42
+ }>;
43
+ }[];
44
+ shouldRevalidate?: undefined;
45
+ })[];
25
46
  export default routes;
package/dist/routes.js CHANGED
@@ -8,39 +8,37 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { authenticatedLoader, getAuthenticatedHttpClient, } from '@openedx/frontend-base';
11
- import { redirect } from 'react-router-dom';
12
11
  import { getSubsInitApiUrl } from './data/services/subs/urls';
13
12
  import { subscriptionManageRole } from './constants';
14
13
  import { setCommonTrackingProps, mapSubsInitToTrackingProps } from './tracking/trackingContext';
15
- // loader to check if the user has subscription access
16
- const subscriptionAccessLoader = () => __awaiter(void 0, void 0, void 0, function* () {
14
+ // loader to set common tracking props from subscription init data
15
+ let trackingInitialized = false;
16
+ const subscriptionTrackingLoader = () => __awaiter(void 0, void 0, void 0, function* () {
17
+ if (trackingInitialized) {
18
+ return null;
19
+ }
17
20
  try {
18
21
  const { data } = yield getAuthenticatedHttpClient().get(getSubsInitApiUrl());
19
- if (!(data === null || data === void 0 ? void 0 : data.userSubscription) || !data.userSubscription.isSubscriber) {
20
- throw redirect('/learner-dashboard'); // redirect to learner dashboard if user does not have subscription access
21
- }
22
22
  setCommonTrackingProps(mapSubsInitToTrackingProps(data));
23
- return null;
23
+ trackingInitialized = true;
24
24
  }
25
- catch (error) {
26
- // Let redirect responses bubble up
27
- if (error instanceof Response) {
28
- throw error;
29
- }
30
- throw redirect('/learner-dashboard'); // redirect to learner dashboard if an error occurs
25
+ catch (_a) {
26
+ // Tracking failures must not block page load
31
27
  }
28
+ return null;
32
29
  });
33
- // composed loader that checks for authentication and subscription access
30
+ // composed loader that checks for authentication and sets tracking props
34
31
  const protectedLoader = (args) => __awaiter(void 0, void 0, void 0, function* () {
35
32
  yield authenticatedLoader(args);
36
- yield subscriptionAccessLoader();
33
+ yield subscriptionTrackingLoader();
37
34
  return null;
38
35
  });
39
36
  const routes = [
40
37
  {
41
38
  id: 'org.openedx.frontend.route.subscription.manage.main',
42
- path: 'manage-subscription',
39
+ path: 'order-history',
43
40
  loader: protectedLoader,
41
+ shouldRevalidate: ({ currentUrl, nextUrl }) => currentUrl.pathname !== nextUrl.pathname,
44
42
  handle: {
45
43
  roles: [subscriptionManageRole],
46
44
  },
@@ -57,7 +55,7 @@ const routes = [
57
55
  lazy() {
58
56
  return __awaiter(this, void 0, void 0, function* () {
59
57
  const module = yield import(
60
- /* webpackChunkName: "ManageSubscriptionPage" */ './manageSubscriptionPage/ManageSubscriptionPage');
58
+ /* webpackChunkName: "OrderHistory" */ './orderHistory/OrderHistoryPage');
61
59
  return { Component: module.default };
62
60
  });
63
61
  },
@@ -67,7 +65,7 @@ const routes = [
67
65
  lazy() {
68
66
  return __awaiter(this, void 0, void 0, function* () {
69
67
  const module = yield import(
70
- /* webpackChunkName: "CancelSubscriptionPage" */ './manageSubscriptionPage/CancelSubscriptionPage');
68
+ /* webpackChunkName: "CancelSubscriptionPage" */ './orderHistory/CancelSubscriptionPage');
71
69
  return { Component: module.default };
72
70
  });
73
71
  },
@@ -77,7 +75,34 @@ const routes = [
77
75
  lazy() {
78
76
  return __awaiter(this, void 0, void 0, function* () {
79
77
  const module = yield import(
80
- /* webpackChunkName: "CancelSuccessPage" */ './manageSubscriptionPage/CancelSuccessPage');
78
+ /* webpackChunkName: "CancelSuccessPage" */ './orderHistory/CancelSuccessPage');
79
+ return { Component: module.default };
80
+ });
81
+ },
82
+ },
83
+ ],
84
+ },
85
+ {
86
+ id: 'org.openedx.frontend.route.subscription.manage.order-details',
87
+ path: 'order-details/:orderNumber',
88
+ loader: protectedLoader,
89
+ handle: {
90
+ roles: [subscriptionManageRole],
91
+ },
92
+ lazy() {
93
+ return __awaiter(this, void 0, void 0, function* () {
94
+ const module = yield import(
95
+ /* webpackChunkName: "subscription-manage-main" */ './Main');
96
+ return { Component: module.default };
97
+ });
98
+ },
99
+ children: [
100
+ {
101
+ index: true,
102
+ lazy() {
103
+ return __awaiter(this, void 0, void 0, function* () {
104
+ const module = yield import(
105
+ /* webpackChunkName: "OrderHistoryDetails" */ './components/OrderHistory/OrderHistoryDetails');
81
106
  return { Component: module.default };
82
107
  });
83
108
  },
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.tsx"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EACL,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,sBAAsB,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAEhG,sDAAsD;AACtD,MAAM,wBAAwB,GAAG,GAAS,EAAE;IAC1C,IAAI,CAAC;QACH,MAAM,EAAE,IAAI,EAAE,GACV,MAAM,0BAA0B,EAAE,CAAC,GAAG,CAAC,iBAAiB,EAAE,CAAC,CAAC;QAEhE,IAAI,CAAC,CAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,CAAA,IAAI,CAAC,IAAI,CAAC,gBAAgB,CAAC,YAAY,EAAE,CAAC;YACnE,MAAM,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAC,0EAA0E;QAClH,CAAC;QAED,sBAAsB,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,mCAAmC;QACnC,IAAI,KAAK,YAAY,QAAQ,EAAE,CAAC;YAC9B,MAAM,KAAK,CAAC;QACd,CAAC;QAED,MAAM,QAAQ,CAAC,oBAAoB,CAAC,CAAC,CAAC,mDAAmD;IAC3F,CAAC;AACH,CAAC,CAAA,CAAC;AAEF,yEAAyE;AACzE,MAAM,eAAe,GAAG,CAAO,IAAI,EAAE,EAAE;IACrC,MAAM,mBAAmB,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,wBAAwB,EAAE,CAAC;IAEjC,OAAO,IAAI,CAAC;AACd,CAAC,CAAA,CAAC;AAEF,MAAM,MAAM,GAAG;IACb;QACE,EAAE,EAAE,qDAAqD;QACzD,IAAI,EAAE,qBAAqB;QAC3B,MAAM,EAAE,eAAe;QACvB,MAAM,EAAE;YACN,KAAK,EAAE,CAAC,sBAAsB,CAAC;SAChC;QACK,IAAI;;gBACR,MAAM,MAAM,GAAG,MAAM,MAAM;gBACzB,kDAAkD,CAAC,QAAQ,CAC5D,CAAC;gBACF,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;YACvC,CAAC;SAAA;QACD,QAAQ,EAAE;YACR;gBACE,KAAK,EAAE,IAAI;gBACL,IAAI;;wBACR,MAAM,MAAM,GAAG,MAAM,MAAM;wBACzB,gDAAgD,CAAC,iDAAiD,CACnG,CAAC;wBACF,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;oBACvC,CAAC;iBAAA;aACF;YACD;gBACE,IAAI,EAAE,QAAQ;gBACR,IAAI;;wBACR,MAAM,MAAM,GAAG,MAAM,MAAM;wBACzB,gDAAgD,CAAC,iDAAiD,CACnG,CAAC;wBACF,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;oBACvC,CAAC;iBAAA;aACF;YACD;gBACE,IAAI,EAAE,gBAAgB;gBAChB,IAAI;;wBACR,MAAM,MAAM,GAAG,MAAM,MAAM;wBACzB,2CAA2C,CAAC,4CAA4C,CACzF,CAAC;wBACF,OAAO,EAAE,SAAS,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC;oBACvC,CAAC;iBAAA;aACF;SACF;KACF;CACF,CAAC;AAEF,eAAe,MAAM,CAAC","sourcesContent":["import {\n authenticatedLoader,\n getAuthenticatedHttpClient,\n} from '@openedx/frontend-base';\nimport { redirect } from 'react-router-dom';\n\nimport { getSubsInitApiUrl } from './data/services/subs/urls';\nimport { subscriptionManageRole } from './constants';\nimport { setCommonTrackingProps, mapSubsInitToTrackingProps } from './tracking/trackingContext';\n\n// loader to check if the user has subscription access\nconst subscriptionAccessLoader = async () => {\n try {\n const { data }\n = await getAuthenticatedHttpClient().get(getSubsInitApiUrl());\n\n if (!data?.userSubscription || !data.userSubscription.isSubscriber) {\n throw redirect('/learner-dashboard'); // redirect to learner dashboard if user does not have subscription access\n }\n\n setCommonTrackingProps(mapSubsInitToTrackingProps(data));\n return null;\n } catch (error) {\n // Let redirect responses bubble up\n if (error instanceof Response) {\n throw error;\n }\n\n throw redirect('/learner-dashboard'); // redirect to learner dashboard if an error occurs\n }\n};\n\n// composed loader that checks for authentication and subscription access\nconst protectedLoader = async (args) => {\n await authenticatedLoader(args);\n await subscriptionAccessLoader();\n\n return null;\n};\n\nconst routes = [\n {\n id: 'org.openedx.frontend.route.subscription.manage.main',\n path: 'manage-subscription',\n loader: protectedLoader,\n handle: {\n roles: [subscriptionManageRole],\n },\n async lazy() {\n const module = await import(\n /* webpackChunkName: \"subscription-manage-main\" */ './Main'\n );\n return { Component: module.default };\n },\n children: [\n {\n index: true,\n async lazy() {\n const module = await import(\n /* webpackChunkName: \"ManageSubscriptionPage\" */ './manageSubscriptionPage/ManageSubscriptionPage'\n );\n return { Component: module.default };\n },\n },\n {\n path: 'cancel',\n async lazy() {\n const module = await import(\n /* webpackChunkName: \"CancelSubscriptionPage\" */ './manageSubscriptionPage/CancelSubscriptionPage'\n );\n return { Component: module.default };\n },\n },\n {\n path: 'cancel/success',\n async lazy() {\n const module = await import(\n /* webpackChunkName: \"CancelSuccessPage\" */ './manageSubscriptionPage/CancelSuccessPage'\n );\n return { Component: module.default };\n },\n },\n ],\n },\n];\n\nexport default routes;\n"]}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n authenticatedLoader,\n getAuthenticatedHttpClient,\n} from '@openedx/frontend-base';\n\nimport { getSubsInitApiUrl } from './data/services/subs/urls';\nimport { subscriptionManageRole } from './constants';\nimport { setCommonTrackingProps, mapSubsInitToTrackingProps } from './tracking/trackingContext';\n\n// loader to set common tracking props from subscription init data\nlet trackingInitialized = false;\nconst subscriptionTrackingLoader = async () => {\n if (trackingInitialized) {\n return null;\n }\n try {\n const { data } = await getAuthenticatedHttpClient().get(getSubsInitApiUrl());\n setCommonTrackingProps(mapSubsInitToTrackingProps(data));\n trackingInitialized = true;\n } catch {\n // Tracking failures must not block page load\n }\n\n return null;\n};\n\n// composed loader that checks for authentication and sets tracking props\nconst protectedLoader = async (args) => {\n await authenticatedLoader(args);\n await subscriptionTrackingLoader();\n\n return null;\n};\n\nconst routes = [\n {\n id: 'org.openedx.frontend.route.subscription.manage.main',\n path: 'order-history',\n loader: protectedLoader,\n shouldRevalidate: ({ currentUrl, nextUrl }) => currentUrl.pathname !== nextUrl.pathname,\n handle: {\n roles: [subscriptionManageRole],\n },\n async lazy() {\n const module = await import(\n /* webpackChunkName: \"subscription-manage-main\" */ './Main'\n );\n return { Component: module.default };\n },\n children: [\n {\n index: true,\n async lazy() {\n const module = await import(\n /* webpackChunkName: \"OrderHistory\" */ './orderHistory/OrderHistoryPage'\n );\n return { Component: module.default };\n },\n },\n {\n path: 'cancel',\n async lazy() {\n const module = await import(\n /* webpackChunkName: \"CancelSubscriptionPage\" */ './orderHistory/CancelSubscriptionPage'\n );\n return { Component: module.default };\n },\n },\n {\n path: 'cancel/success',\n async lazy() {\n const module = await import(\n /* webpackChunkName: \"CancelSuccessPage\" */ './orderHistory/CancelSuccessPage'\n );\n return { Component: module.default };\n },\n },\n ],\n },\n {\n id: 'org.openedx.frontend.route.subscription.manage.order-details',\n path: 'order-details/:orderNumber',\n loader: protectedLoader,\n handle: {\n roles: [subscriptionManageRole],\n },\n async lazy() {\n const module = await import(\n /* webpackChunkName: \"subscription-manage-main\" */ './Main'\n );\n return { Component: module.default };\n },\n children: [\n {\n index: true,\n async lazy() {\n const module = await import(\n /* webpackChunkName: \"OrderHistoryDetails\" */ './components/OrderHistory/OrderHistoryDetails'\n );\n return { Component: module.default };\n },\n },\n ],\n },\n];\n\nexport default routes;\n"]}
package/dist/style.scss CHANGED
@@ -1,4 +1,4 @@
1
- @use "./styles/components/manageSubscription.scss";
1
+ @use "./styles/components/orderHistory.scss";
2
2
 
3
3
  button.btn.btn-outline:has(svg) {
4
4
  display: none !important;
@@ -19,30 +19,6 @@
19
19
  min-height: 50vh;
20
20
  }
21
21
 
22
- /* Tabs — custom 3px underline active state */
23
- .manage-tabs {
24
- gap: 1.5rem;
25
- }
26
-
27
- .manage-tabs__tab {
28
- appearance: none;
29
- background: none;
30
- border: 0;
31
- border-bottom: 3px solid transparent;
32
- color: var(--manage-muted);
33
- cursor: pointer;
34
- font-size: 1rem;
35
- font-weight: 500;
36
- margin-bottom: -1px;
37
- padding: 0.75rem 0;
38
- }
39
-
40
- .manage-tabs__tab--active {
41
- color: var(--manage-title);
42
- font-weight: 700;
43
- border-bottom-color: var(--manage-title);
44
- }
45
-
46
22
  /* Cards — gap only (no gap utility) */
47
23
  .manage-card,
48
24
  .manage-card__actions,
@@ -452,3 +428,63 @@
452
428
  height: 1.125rem;
453
429
  flex-shrink: 0;
454
430
  }
431
+
432
+ .orderHistory {
433
+ padding-inline: 7.5rem;
434
+
435
+ @media (max-width: 1024px) {
436
+ padding-inline: 3rem;
437
+ }
438
+
439
+ @media (max-width: 767px) {
440
+ padding-inline: 1.5rem;
441
+ }
442
+ }
443
+
444
+ .orderHistoryTitle {
445
+ font-weight: 800;
446
+ }
447
+
448
+ .refund-notice {
449
+ background-color: #fff;
450
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.15), 0 1px 2px rgba(0, 0, 0, 0.15);
451
+ border-radius: 4px;
452
+ }
453
+
454
+ .refundTitle {
455
+ font-size: 22px;
456
+ }
457
+
458
+ .refundBody {
459
+ font-size: 18px;
460
+ }
461
+
462
+ .orderHistoryCard {
463
+
464
+ &__details {
465
+ font-weight: 700;
466
+ }
467
+
468
+ &__link {
469
+ color: #002B2B;
470
+ }
471
+ }
472
+
473
+ .programItemsTable {
474
+ .pgn__data-table thead th {
475
+ background-color: #F2F0EF;
476
+ color: #000000;
477
+ }
478
+
479
+ .pgn__data-table tbody tr:nth-child(even) {
480
+ background-color: #FBFAF9;
481
+ }
482
+
483
+ .pgn__data-table tbody tr {
484
+ color: #000000;
485
+ }
486
+ }
487
+
488
+ .divider {
489
+ border-top: 1px solid #002B2B;
490
+ }
@@ -5,7 +5,7 @@ export interface CommonTrackingProps {
5
5
  subscription_start_date?: string | null;
6
6
  subscription_end_date?: string | null;
7
7
  current_period_end?: string | null;
8
- paymentMethod?: string | null;
8
+ payment_method?: string | null;
9
9
  user_email?: string | null;
10
10
  session_id?: string | null;
11
11
  experimentation?: string | null;
@@ -14,7 +14,7 @@ export const mapSubsInitToTrackingProps = (data) => {
14
14
  subscription_start_date: (_e = (_d = data === null || data === void 0 ? void 0 : data.userSubscription) === null || _d === void 0 ? void 0 : _d.subscriptionStartDate) !== null && _e !== void 0 ? _e : null,
15
15
  subscription_end_date: (_g = (_f = data === null || data === void 0 ? void 0 : data.userSubscription) === null || _f === void 0 ? void 0 : _f.subscriptionEndDate) !== null && _g !== void 0 ? _g : null,
16
16
  current_period_end: (_j = (_h = data === null || data === void 0 ? void 0 : data.userSubscription) === null || _h === void 0 ? void 0 : _h.currentPeriodEnd) !== null && _j !== void 0 ? _j : null,
17
- paymentMethod: (_l = (_k = data === null || data === void 0 ? void 0 : data.userSubscription) === null || _k === void 0 ? void 0 : _k.paymentMethod) !== null && _l !== void 0 ? _l : null,
17
+ payment_method: (_l = (_k = data === null || data === void 0 ? void 0 : data.userSubscription) === null || _k === void 0 ? void 0 : _k.paymentMethod) !== null && _l !== void 0 ? _l : null,
18
18
  user_email: (_o = (_m = getAuthenticatedUser()) === null || _m === void 0 ? void 0 : _m.email) !== null && _o !== void 0 ? _o : null,
19
19
  session_id: '',
20
20
  experimentation: '',
@@ -1 +1 @@
1
- {"version":3,"file":"trackingContext.js","sourceRoot":"","sources":["../../src/tracking/trackingContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAkB9D,IAAI,WAAW,GAAwB,EAAE,CAAC;AAE1C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,KAA0B,EAAE,EAAE;IACnE,WAAW,mCAAQ,WAAW,GAAK,KAAK,CAAE,CAAC;AAC7C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAwB,EAAE,CAAC,WAAW,CAAC;AAE7E,sEAAsE;AACtE,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,IAAS,EAAuB,EAAE;;IAAC,OAAA,CAAC;QAC7E,iBAAiB,EAAE,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,WAAW;QACtD,mBAAmB,EAAE,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,MAAM;QACnD,iBAAiB,EAAE,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,gBAAgB;QAC3D,uBAAuB,EAAE,MAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,qBAAqB,mCAAI,IAAI;QAC9E,qBAAqB,EAAE,MAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,mBAAmB,mCAAI,IAAI;QAC1E,kBAAkB,EAAE,MAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,gBAAgB,mCAAI,IAAI;QACpE,aAAa,EAAE,MAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,aAAa,mCAAI,IAAI;QAC5D,UAAU,EAAE,MAAA,MAAA,oBAAoB,EAAE,0CAAE,KAAK,mCAAI,IAAI;QACjD,UAAU,EAAE,EAAE;QACd,eAAe,EAAE,EAAE;QACnB,WAAW,EAAE,EAAE;QACf,MAAM,EAAE,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,MAAM,mCAAI,IAAI;QAC5B,eAAe,EAAE,MAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,gBAAgB,mCAAI,IAAI;KAClE,CAAC,CAAA;CAAA,CAAC","sourcesContent":["import { getAuthenticatedUser } from '@openedx/frontend-base';\n\nexport interface CommonTrackingProps {\n free_trial_status?: string,\n subscription_status?: string,\n subscription_tier?: string,\n subscription_start_date?: string | null,\n subscription_end_date?: string | null,\n current_period_end?: string | null,\n paymentMethod?: string | null,\n user_email?: string | null,\n session_id?: string | null,\n experimentation?: string | null,\n device_type?: string | null,\n opt_in?: boolean | null,\n isUserSuspended?: boolean | null | undefined,\n}\n\nlet commonProps: CommonTrackingProps = {};\n\nexport const setCommonTrackingProps = (props: CommonTrackingProps) => {\n commonProps = { ...commonProps, ...props };\n};\n\nexport const getCommonTrackingProps = (): CommonTrackingProps => commonProps;\n\n// Single place that maps the subs/init API response -> tracking props\nexport const mapSubsInitToTrackingProps = (data: any): CommonTrackingProps => ({\n free_trial_status: data?.userSubscription?.trialActive,\n subscription_status: data?.userSubscription?.status,\n subscription_tier: data?.userSubscription?.billingFrequency,\n subscription_start_date: data?.userSubscription?.subscriptionStartDate ?? null,\n subscription_end_date: data?.userSubscription?.subscriptionEndDate ?? null,\n current_period_end: data?.userSubscription?.currentPeriodEnd ?? null,\n paymentMethod: data?.userSubscription?.paymentMethod ?? null,\n user_email: getAuthenticatedUser()?.email ?? null,\n session_id: '',\n experimentation: '',\n device_type: '',\n opt_in: data?.opt_in ?? null,\n isUserSuspended: data?.userSubscription?.tIsUserSuspended ?? null,\n});\n"]}
1
+ {"version":3,"file":"trackingContext.js","sourceRoot":"","sources":["../../src/tracking/trackingContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAkB9D,IAAI,WAAW,GAAwB,EAAE,CAAC;AAE1C,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,KAA0B,EAAE,EAAE;IACnE,WAAW,mCAAQ,WAAW,GAAK,KAAK,CAAE,CAAC;AAC7C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,sBAAsB,GAAG,GAAwB,EAAE,CAAC,WAAW,CAAC;AAE7E,sEAAsE;AACtE,MAAM,CAAC,MAAM,0BAA0B,GAAG,CAAC,IAAS,EAAuB,EAAE;;IAAC,OAAA,CAAC;QAC7E,iBAAiB,EAAE,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,WAAW;QACtD,mBAAmB,EAAE,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,MAAM;QACnD,iBAAiB,EAAE,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,gBAAgB;QAC3D,uBAAuB,EAAE,MAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,qBAAqB,mCAAI,IAAI;QAC9E,qBAAqB,EAAE,MAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,mBAAmB,mCAAI,IAAI;QAC1E,kBAAkB,EAAE,MAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,gBAAgB,mCAAI,IAAI;QACpE,cAAc,EAAE,MAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,aAAa,mCAAI,IAAI;QAC7D,UAAU,EAAE,MAAA,MAAA,oBAAoB,EAAE,0CAAE,KAAK,mCAAI,IAAI;QACjD,UAAU,EAAE,EAAE;QACd,eAAe,EAAE,EAAE;QACnB,WAAW,EAAE,EAAE;QACf,MAAM,EAAE,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,MAAM,mCAAI,IAAI;QAC5B,eAAe,EAAE,MAAA,MAAA,IAAI,aAAJ,IAAI,uBAAJ,IAAI,CAAE,gBAAgB,0CAAE,gBAAgB,mCAAI,IAAI;KAClE,CAAC,CAAA;CAAA,CAAC","sourcesContent":["import { getAuthenticatedUser } from '@openedx/frontend-base';\n\nexport interface CommonTrackingProps {\n free_trial_status?: string,\n subscription_status?: string,\n subscription_tier?: string,\n subscription_start_date?: string | null,\n subscription_end_date?: string | null,\n current_period_end?: string | null,\n payment_method?: string | null,\n user_email?: string | null,\n session_id?: string | null,\n experimentation?: string | null,\n device_type?: string | null,\n opt_in?: boolean | null,\n isUserSuspended?: boolean | null | undefined,\n}\n\nlet commonProps: CommonTrackingProps = {};\n\nexport const setCommonTrackingProps = (props: CommonTrackingProps) => {\n commonProps = { ...commonProps, ...props };\n};\n\nexport const getCommonTrackingProps = (): CommonTrackingProps => commonProps;\n\n// Single place that maps the subs/init API response -> tracking props\nexport const mapSubsInitToTrackingProps = (data: any): CommonTrackingProps => ({\n free_trial_status: data?.userSubscription?.trialActive,\n subscription_status: data?.userSubscription?.status,\n subscription_tier: data?.userSubscription?.billingFrequency,\n subscription_start_date: data?.userSubscription?.subscriptionStartDate ?? null,\n subscription_end_date: data?.userSubscription?.subscriptionEndDate ?? null,\n current_period_end: data?.userSubscription?.currentPeriodEnd ?? null,\n payment_method: data?.userSubscription?.paymentMethod ?? null,\n user_email: getAuthenticatedUser()?.email ?? null,\n session_id: '',\n experimentation: '',\n device_type: '',\n opt_in: data?.opt_in ?? null,\n isUserSuspended: data?.userSubscription?.tIsUserSuspended ?? null,\n});\n"]}
package/dist/types.d.ts CHANGED
@@ -102,3 +102,58 @@ export interface BillingPlan {
102
102
  export interface BillingPlansResult {
103
103
  plans: BillingPlan[];
104
104
  }
105
+ /** One course line on a program purchase receipt. */
106
+ export interface PurchaseHistoryCourse {
107
+ courseId: string;
108
+ name: string;
109
+ price: number;
110
+ discountAmount: number;
111
+ }
112
+ /** One-off course or program purchase from `tOrderHistory.orders`. */
113
+ export interface PurchaseHistoryItem {
114
+ orderId: string;
115
+ orderNumber: string;
116
+ purchasedAt: string;
117
+ paymentIntentId?: string;
118
+ paymentMethodType?: string;
119
+ name: string;
120
+ total: {
121
+ amount: number;
122
+ currency: string;
123
+ };
124
+ /** Set when the payload reports a program purchase. */
125
+ itemType?: 'course' | 'program';
126
+ courses?: PurchaseHistoryCourse[];
127
+ }
128
+ export interface OrdersListProps {
129
+ orders: PurchaseHistoryItem[];
130
+ onViewReceipt?: (order: PurchaseHistoryItem) => void;
131
+ }
132
+ export interface MoneyAmount {
133
+ amount: number;
134
+ currency: string;
135
+ }
136
+ /** One line on a program order details payload. */
137
+ export interface OrderProgramItem {
138
+ productId?: string;
139
+ courseRunKey?: string;
140
+ name: string;
141
+ price?: MoneyAmount;
142
+ discount?: MoneyAmount;
143
+ total?: MoneyAmount;
144
+ }
145
+ /** Normalized `tOrderDetails` payload for the order details page. */
146
+ export interface OrderDetailsData {
147
+ orderId: string;
148
+ orderNumber: string;
149
+ purchasedAt: string;
150
+ paymentIntentId: string;
151
+ name: string;
152
+ subtotalAmount?: MoneyAmount;
153
+ itemPriceAmount?: MoneyAmount;
154
+ discountCode?: string | null;
155
+ total: MoneyAmount;
156
+ discountAmount?: MoneyAmount;
157
+ itemType: 'course' | 'program';
158
+ items?: OrderProgramItem[];
159
+ }
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * Manage Subscription — domain types for Viax subscriber + enrollment data.\n */\n\n/** GraphQL runner used by VIAX fetchers. */\nexport type GqlRunner = (\n query: string,\n variables?: Record<string, unknown>,\n) => Promise<any>;\n\n/** Result of `tIsCurrentUserSubscriber` (JSON scalar). */\nexport interface CurrentUserSubscriberResult {\n /** Has/expects access — drive manage vs upsell messaging. */\n isSubscriber: boolean,\n /** Enrollable right now (ACTIVE / TRIAL_ACTIVE). */\n canEnroll: boolean,\n subscriptionBiId: string | null,\n /**\n * Every subscription the learner has held (all statuses, newest first),\n * e.g. a cancelled Stripe one plus a newer PayPal one. Payment history\n * lists invoices across all of them. Empty until the API returns it.\n */\n subscriptionBiIds?: string[],\n status: string | null,\n currentPeriodEnd: string | null,\n subscriptionStartDate: string | null,\n subscriptionEndDate: string | null,\n /**\n * Billing cycle / plan label used by UI (from `billingFrequency`,\n * `renewalType`, or `planName` on the API payload).\n */\n renewalType?: string | null,\n /** Optional cancel timestamp when present. */\n canceledAt?: string | null,\n /** Optional price display amount when present. */\n amount?: string | null,\n currency?: string | null,\n /**\n * Billing-plan fallback fields used when `renewalType` is missing\n * (e.g. PayPal payloads). See `resolvePlanLabel`.\n */\n billingPlanUid?: string | null,\n interval?: string | null,\n billingInterval?: string | null,\n bipOwnershipModel?: { uid?: string, omName?: string } | null,\n}\n\n/** Normalized enrollment for the cancel confirmation course list. */\nexport interface SubscriptionEnrollment {\n biId: string,\n courseMaId: string | null,\n courseName: string | null,\n courseSlug: string | null,\n /** Course card / thumbnail image. */\n imageUrl: string | null,\n orgName: string | null,\n orgLogoUrl: string | null,\n courseRunKey: string | null,\n /** TwouEnrollment bpStatus.code (ACTIVE, COMPLETED, …). */\n status: string | null,\n /** True when the enrollment has a certificate id and/or URL. */\n hasCertificate: boolean,\n /** Absolute certificate link (`COURSES_BASE_URL` + `tCertificateUrl`). */\n certificateUrl: string | null,\n}\n\nexport type PaymentProcessor = 'stripe' | 'paypal';\n\n/** Normalized invoice row for the Payment history tab. */\nexport interface PaymentHistoryItem {\n biId: string,\n /** Display order number, e.g. EDX-BI3X25N5SY30OSIK */\n orderNumber: string,\n stripeInvoiceId: string | null,\n /** PayPal sale id, or `trial:<PayPal subscription id>` for a $0 trial row. */\n paypalInvoiceId: string | null,\n /** Which processor billed this invoice; null when neither id is set. */\n paymentProcessor: PaymentProcessor | null,\n status: string | null,\n billDate: string | null,\n periodStart: string | null,\n periodEnd: string | null,\n /** Charged amount in major currency units (e.g. 59.00). */\n amount: number,\n currency: string,\n isTrial: boolean,\n}\n\n/** UI view derived from subscriber status. */\nexport type SubscriptionView =\n | 'none'\n | 'active'\n | 'canceled_access'\n | 'canceled_expired';\n\nexport type ManageTab = 'subscription' | 'payment-history';\n\nexport type BillingCycle = 'monthly' | 'annual';\n\nexport interface BillingPlanOwnershipModel {\n uid: string,\n omName: string,\n}\n\n/** Normalized row from `filterCustomerBillingPlan`. */\nexport interface BillingPlan {\n uid: string,\n stripePriceId: string,\n /** Price amount e.g. \"39.99\" */\n amount: string,\n /** Currency code e.g. \"USD\" */\n currency: string,\n isTemplate: boolean,\n ownershipModel: BillingPlanOwnershipModel,\n}\n\nexport interface BillingPlansResult {\n plans: BillingPlan[],\n}\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG","sourcesContent":["/**\n * Manage Subscription — domain types for Viax subscriber + enrollment data.\n */\n\n/** GraphQL runner used by VIAX fetchers. */\nexport type GqlRunner = (\n query: string,\n variables?: Record<string, unknown>,\n) => Promise<any>;\n\n/** Result of `tIsCurrentUserSubscriber` (JSON scalar). */\nexport interface CurrentUserSubscriberResult {\n /** Has/expects access — drive manage vs upsell messaging. */\n isSubscriber: boolean,\n /** Enrollable right now (ACTIVE / TRIAL_ACTIVE). */\n canEnroll: boolean,\n subscriptionBiId: string | null,\n /**\n * Every subscription the learner has held (all statuses, newest first),\n * e.g. a cancelled Stripe one plus a newer PayPal one. Payment history\n * lists invoices across all of them. Empty until the API returns it.\n */\n subscriptionBiIds?: string[],\n status: string | null,\n currentPeriodEnd: string | null,\n subscriptionStartDate: string | null,\n subscriptionEndDate: string | null,\n /**\n * Billing cycle / plan label used by UI (from `billingFrequency`,\n * `renewalType`, or `planName` on the API payload).\n */\n renewalType?: string | null,\n /** Optional cancel timestamp when present. */\n canceledAt?: string | null,\n /** Optional price display amount when present. */\n amount?: string | null,\n currency?: string | null,\n /**\n * Billing-plan fallback fields used when `renewalType` is missing\n * (e.g. PayPal payloads). See `resolvePlanLabel`.\n */\n billingPlanUid?: string | null,\n interval?: string | null,\n billingInterval?: string | null,\n bipOwnershipModel?: { uid?: string, omName?: string } | null,\n}\n\n/** Normalized enrollment for the cancel confirmation course list. */\nexport interface SubscriptionEnrollment {\n biId: string,\n courseMaId: string | null,\n courseName: string | null,\n courseSlug: string | null,\n /** Course card / thumbnail image. */\n imageUrl: string | null,\n orgName: string | null,\n orgLogoUrl: string | null,\n courseRunKey: string | null,\n /** TwouEnrollment bpStatus.code (ACTIVE, COMPLETED, …). */\n status: string | null,\n /** True when the enrollment has a certificate id and/or URL. */\n hasCertificate: boolean,\n /** Absolute certificate link (`COURSES_BASE_URL` + `tCertificateUrl`). */\n certificateUrl: string | null,\n}\n\nexport type PaymentProcessor = 'stripe' | 'paypal';\n\n/** Normalized invoice row for the Payment history tab. */\nexport interface PaymentHistoryItem {\n biId: string,\n /** Display order number, e.g. EDX-BI3X25N5SY30OSIK */\n orderNumber: string,\n stripeInvoiceId: string | null,\n /** PayPal sale id, or `trial:<PayPal subscription id>` for a $0 trial row. */\n paypalInvoiceId: string | null,\n /** Which processor billed this invoice; null when neither id is set. */\n paymentProcessor: PaymentProcessor | null,\n status: string | null,\n billDate: string | null,\n periodStart: string | null,\n periodEnd: string | null,\n /** Charged amount in major currency units (e.g. 59.00). */\n amount: number,\n currency: string,\n isTrial: boolean,\n}\n\n/** UI view derived from subscriber status. */\nexport type SubscriptionView =\n | 'none'\n | 'active'\n | 'canceled_access'\n | 'canceled_expired';\n\nexport type ManageTab = 'subscription' | 'payment-history';\n\nexport type BillingCycle = 'monthly' | 'annual';\n\nexport interface BillingPlanOwnershipModel {\n uid: string,\n omName: string,\n}\n\n/** Normalized row from `filterCustomerBillingPlan`. */\nexport interface BillingPlan {\n uid: string,\n stripePriceId: string,\n /** Price amount e.g. \"39.99\" */\n amount: string,\n /** Currency code e.g. \"USD\" */\n currency: string,\n isTemplate: boolean,\n ownershipModel: BillingPlanOwnershipModel,\n}\n\nexport interface BillingPlansResult {\n plans: BillingPlan[],\n}\n\n/** One course line on a program purchase receipt. */\nexport interface PurchaseHistoryCourse {\n courseId: string,\n name: string,\n price: number,\n discountAmount: number,\n}\n\n/** One-off course or program purchase from `tOrderHistory.orders`. */\nexport interface PurchaseHistoryItem {\n orderId: string,\n orderNumber: string,\n purchasedAt: string,\n paymentIntentId?: string,\n paymentMethodType?: string,\n name: string,\n total: {\n amount: number,\n currency: string,\n },\n /** Set when the payload reports a program purchase. */\n itemType?: 'course' | 'program',\n courses?: PurchaseHistoryCourse[],\n}\n\nexport interface OrdersListProps {\n orders: PurchaseHistoryItem[],\n onViewReceipt?: (order: PurchaseHistoryItem) => void,\n}\n\nexport interface MoneyAmount {\n amount: number,\n currency: string,\n}\n\n/** One line on a program order details payload. */\nexport interface OrderProgramItem {\n productId?: string,\n courseRunKey?: string,\n name: string,\n price?: MoneyAmount,\n discount?: MoneyAmount,\n total?: MoneyAmount,\n}\n\n/** Normalized `tOrderDetails` payload for the order details page. */\nexport interface OrderDetailsData {\n orderId: string,\n orderNumber: string,\n purchasedAt: string,\n paymentIntentId: string,\n name: string,\n subtotalAmount?: MoneyAmount,\n itemPriceAmount?: MoneyAmount,\n discountCode?: string | null,\n total: MoneyAmount,\n discountAmount?: MoneyAmount,\n itemType: 'course' | 'program',\n items?: OrderProgramItem[],\n}\n"]}
@@ -0,0 +1,6 @@
1
+ interface OrderTotal {
2
+ amount: number;
3
+ currency: string;
4
+ }
5
+ declare const getFormattedCurrency: ({ amount, currency }: OrderTotal) => string;
6
+ export default getFormattedCurrency;
@@ -0,0 +1,6 @@
1
+ const getFormattedCurrency = ({ amount, currency }) => new Intl.NumberFormat('en-US', {
2
+ style: 'currency',
3
+ currency,
4
+ }).format(amount);
5
+ export default getFormattedCurrency;
6
+ //# sourceMappingURL=currency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"currency.js","sourceRoot":"","sources":["../../src/utils/currency.ts"],"names":[],"mappings":"AAKA,MAAM,oBAAoB,GAAG,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAc,EAAU,EAAE,CACxE,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE;IAC7B,KAAK,EAAE,UAAU;IACjB,QAAQ;CACT,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AAEpB,eAAe,oBAAoB,CAAC","sourcesContent":["interface OrderTotal {\n amount: number,\n currency: string,\n}\n\nconst getFormattedCurrency = ({ amount, currency }: OrderTotal): string =>\n new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency,\n }).format(amount);\n\nexport default getFormattedCurrency;\n"]}
@@ -44,3 +44,9 @@ export declare const VALIDATE_CART_PAYMENT_METHOD = "mutation ValidateCartPaymen
44
44
  * Attach a new Stripe payment method (pm_…) as the subscription default.
45
45
  */
46
46
  export declare const UPDATE_SUBSCRIPTION_PAYMENT_METHOD = "mutation UpdateSubscriptionPaymentMethod(\n $biId: String!\n $tStripePaymentMethodId: String!\n) {\n tUpdateSubscriptionPaymentMethod(\n biId: $biId\n tStripePaymentMethodId: $tStripePaymentMethodId\n )\n}";
47
+ /** One-off course and program purchases for the order history page. */
48
+ export declare const GET_PURCHASE_HISTORY = "query PurchaseHistory {\n tOrderHistory\n}";
49
+ /** Detailed payload for one order, looked up by its public order number. */
50
+ export declare const GET_ORDER_DETAILS = "query OrderDetails($orderNumber: String) {\n tOrderDetails(orderNumber: $orderNumber)\n}";
51
+ /** Stripe receipt URL for one order (biId). */
52
+ export declare const GET_STRIPE_PAYMENT_RECEIPT = "query StripePaymentReceipt($biId: String) {\n tPaymentReceipt(biId: $biId)\n}";
@@ -142,4 +142,16 @@ export const UPDATE_SUBSCRIPTION_PAYMENT_METHOD = `mutation UpdateSubscriptionPa
142
142
  tStripePaymentMethodId: $tStripePaymentMethodId
143
143
  )
144
144
  }`;
145
+ /** One-off course and program purchases for the order history page. */
146
+ export const GET_PURCHASE_HISTORY = `query PurchaseHistory {
147
+ tOrderHistory
148
+ }`;
149
+ /** Detailed payload for one order, looked up by its public order number. */
150
+ export const GET_ORDER_DETAILS = `query OrderDetails($orderNumber: String) {
151
+ tOrderDetails(orderNumber: $orderNumber)
152
+ }`;
153
+ /** Stripe receipt URL for one order (biId). */
154
+ export const GET_STRIPE_PAYMENT_RECEIPT = `query StripePaymentReceipt($biId: String) {
155
+ tPaymentReceipt(biId: $biId)
156
+ }`;
145
157
  //# sourceMappingURL=queries.js.map