richie-education 2.28.2-dev58 → 2.28.2-dev61

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.
@@ -58,7 +58,6 @@ describe('AddressSelector', () => {
58
58
  billingAddress,
59
59
  setBillingAddress,
60
60
  setCreditCard: jest.fn(),
61
- onPaymentSuccess: jest.fn(),
62
61
  step: SaleTunnelStep.IDLE,
63
62
  registerSubmitCallback: jest.fn(),
64
63
  unregisterSubmitCallback: jest.fn(),
@@ -28,7 +28,6 @@ export interface SaleTunnelContextType {
28
28
  webAnalyticsEventKey: string;
29
29
 
30
30
  // internal
31
- onPaymentSuccess: () => void;
32
31
  step: SaleTunnelStep;
33
32
 
34
33
  // meta
@@ -75,16 +74,6 @@ export const GenericSaleTunnel = (props: GenericSaleTunnelProps) => {
75
74
  enrollmentId: props.enrollment?.id,
76
75
  productId: props.product.id,
77
76
  });
78
-
79
- const {
80
- methods: { refetch: refetchOmniscientOrders },
81
- } = useOmniscientOrders();
82
- const {
83
- methods: { invalidate: invalidateOrders },
84
- } = useOrders(undefined, { enabled: false });
85
- const {
86
- methods: { invalidate: invalidateEnrollments },
87
- } = useEnrollments(undefined, { enabled: false });
88
77
  const [billingAddress, setBillingAddress] = useState<Address>();
89
78
  const [creditCard, setCreditCard] = useState<CreditCard>();
90
79
  const [step, setStep] = useState<SaleTunnelStep>(SaleTunnelStep.IDLE);
@@ -127,19 +116,6 @@ export const GenericSaleTunnel = (props: GenericSaleTunnelProps) => {
127
116
  creditCard,
128
117
  setCreditCard,
129
118
  nextStep,
130
- onPaymentSuccess: () => {
131
- nextStep();
132
- WebAnalyticsAPIHandler()?.sendCourseProductEvent(
133
- CourseProductEvent.PAYMENT_SUCCEED,
134
- props.eventKey,
135
- );
136
- // Once the user has completed the purchase, we need to refetch the orders
137
- // to update the ordersQuery cache
138
- invalidateOrders();
139
- refetchOmniscientOrders();
140
- invalidateEnrollments();
141
- props.onFinish?.(order!);
142
- },
143
119
  step,
144
120
  registerSubmitCallback: (key, callback) => {
145
121
  setSubmitCallbacks((prev) => new Map(prev).set(key, callback));
@@ -230,6 +206,34 @@ export const GenericSaleTunnelInitialStep = (props: GenericSaleTunnelProps) => {
230
206
  };
231
207
 
232
208
  export const GenericSaleTunnelSuccessStep = (props: SaleTunnelProps) => {
209
+ const {
210
+ webAnalyticsEventKey,
211
+ props: { onFinish },
212
+ order,
213
+ } = useSaleTunnelContext();
214
+ const {
215
+ methods: { refetch: refetchOmniscientOrders },
216
+ } = useOmniscientOrders();
217
+ const {
218
+ methods: { invalidate: invalidateOrders },
219
+ } = useOrders(undefined, { enabled: false });
220
+ const {
221
+ methods: { invalidate: invalidateEnrollments },
222
+ } = useEnrollments(undefined, { enabled: false });
223
+
224
+ useEffect(() => {
225
+ WebAnalyticsAPIHandler()?.sendCourseProductEvent(
226
+ CourseProductEvent.PAYMENT_SUCCEED,
227
+ webAnalyticsEventKey,
228
+ );
229
+ // Once the user has completed the purchase, we need to refetch the orders
230
+ // to update the ordersQuery cache
231
+ invalidateOrders();
232
+ refetchOmniscientOrders();
233
+ invalidateEnrollments();
234
+ onFinish?.(order!);
235
+ }, []);
236
+
233
237
  return (
234
238
  <Modal {...props} size={ModalSize.MEDIUM}>
235
239
  <SaleTunnelSuccess closeModal={props.onClose} />
@@ -217,7 +217,6 @@ describe.each([
217
217
 
218
218
  // - Route to create order should have been called
219
219
  nbApiCalls += 1; // order create
220
- nbApiCalls += 1; // order get (invalidate queries)
221
220
  nbApiCalls += 1; // useProductOrder call (invalidate from create)
222
221
 
223
222
  await waitFor(() => expect(fetchMock.calls()).toHaveLength(nbApiCalls));
@@ -6,6 +6,7 @@ import {
6
6
  OrderEnrollment,
7
7
  OrderState,
8
8
  PaymentScheduleState,
9
+ PURCHASABLE_ORDER_STATES,
9
10
  } from 'types/Joanie';
10
11
 
11
12
  export enum OrderStatus {
@@ -96,4 +97,9 @@ export class OrderHelper {
96
97
  if (!order) return false;
97
98
  return ACTIVE_ORDER_STATES.includes(order.state);
98
99
  }
100
+
101
+ static isPurchasable(order?: Order | NestedCourseOrder | OrderEnrollment) {
102
+ if (!order) return true;
103
+ return PURCHASABLE_ORDER_STATES.includes(order.state);
104
+ }
99
105
  }
@@ -484,7 +484,6 @@ export const SaleTunnelContextFactory = factory(
484
484
  billingAddress: undefined,
485
485
  setBillingAddress: noop,
486
486
  setCreditCard: noop,
487
- onPaymentSuccess: noop,
488
487
  step: SaleTunnelStep.IDLE,
489
488
  registerSubmitCallback: noop,
490
489
  unregisterSubmitCallback: noop,
@@ -2,12 +2,7 @@ import { FormattedMessage, defineMessages } from 'react-intl';
2
2
  import { useState } from 'react';
3
3
  import PurchaseButton from 'components/PurchaseButton';
4
4
  import { Icon, IconTypeEnum } from 'components/Icon';
5
- import {
6
- CertificateProduct,
7
- Enrollment,
8
- ProductType,
9
- PURCHASABLE_ORDER_STATES,
10
- } from 'types/Joanie';
5
+ import { CertificateProduct, Enrollment, ProductType } from 'types/Joanie';
11
6
  import DownloadCertificateButton from 'components/DownloadCertificateButton';
12
7
  import { useCertificate } from 'hooks/useCertificates';
13
8
  import { isOpenedCourseRunCertificate } from 'utils/CourseRuns';
@@ -65,30 +60,27 @@ const ProductCertificateFooter = ({ product, enrollment }: ProductCertificateFoo
65
60
  <FormattedMessage {...messages.buyProductCertificateLabel} />
66
61
  )}
67
62
  </div>
68
- {OrderHelper.isActive(order) ? (
69
- order!.certificate_id && (
70
- <DownloadCertificateButton
71
- className="dashboard-item__button"
72
- certificateId={order!.certificate_id}
73
- />
74
- )
75
- ) : (
76
- <PurchaseButton
63
+ {OrderHelper.isActive(order) && order!.certificate_id && (
64
+ <DownloadCertificateButton
77
65
  className="dashboard-item__button"
78
- product={product}
79
- enrollment={enrollment}
80
- buttonProps={{ size: 'small' }}
81
- disabled={order && !PURCHASABLE_ORDER_STATES.includes(order.state)}
82
- onFinish={(o) => {
83
- /**
84
- * As we do not refetch enrollments in DashboardCourses after SaleTunnel cache invalidation (to avoid
85
- * scroll reset - and SaleTunnel modal unmounting too early caused by list reset) we need to manually
86
- * update the active order in the enrollment in order to hide the buy button and display the download button.
87
- */
88
- setOrder(o);
89
- }}
66
+ certificateId={order!.certificate_id}
90
67
  />
91
68
  )}
69
+ <PurchaseButton
70
+ className="dashboard-item__button"
71
+ product={product}
72
+ enrollment={enrollment}
73
+ buttonProps={{ size: 'small' }}
74
+ disabled={!OrderHelper.isPurchasable(order)}
75
+ onFinish={(o) => {
76
+ /**
77
+ * As we do not refetch enrollments in DashboardCourses after SaleTunnel cache invalidation (to avoid
78
+ * scroll reset - and SaleTunnel modal unmounting too early caused by list reset) we need to manually
79
+ * update the active order in the enrollment in order to hide the buy button and display the download button.
80
+ */
81
+ setOrder(o);
82
+ }}
83
+ />
92
84
  </div>
93
85
  );
94
86
  };
@@ -1,7 +1,7 @@
1
1
  import { Children, useEffect, useMemo } from 'react';
2
2
  import { defineMessages, FormattedMessage, FormattedNumber, useIntl } from 'react-intl';
3
3
  import c from 'classnames';
4
- import { ProductType, Product, CredentialOrder, PURCHASABLE_ORDER_STATES } from 'types/Joanie';
4
+ import { ProductType, Product, CredentialOrder } from 'types/Joanie';
5
5
  import { useCourseProduct } from 'hooks/useCourseProducts';
6
6
  import { Spinner } from 'components/Spinner';
7
7
  import { Icon, IconTypeEnum } from 'components/Icon';
@@ -156,7 +156,7 @@ const CourseProductItem = ({ productId, course, compact = false }: CourseProduct
156
156
  });
157
157
 
158
158
  const order = productOrder as CredentialOrder;
159
- const canPurchase = !order || PURCHASABLE_ORDER_STATES.includes(order.state);
159
+ const canPurchase = OrderHelper.isPurchasable(order);
160
160
  const hasPurchased = OrderHelper.isActive(order);
161
161
  const canEnroll = OrderHelper.allowEnrollment(order);
162
162
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "richie-education",
3
- "version": "2.28.2-dev58",
3
+ "version": "2.28.2-dev61",
4
4
  "description": "A CMS to build learning portals for Open Education",
5
5
  "main": "sandbox/manage.py",
6
6
  "scripts": {