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.
- package/js/components/SaleTunnel/AddressSelector/index.spec.tsx +0 -1
- package/js/components/SaleTunnel/GenericSaleTunnel.tsx +28 -24
- package/js/components/SaleTunnel/index.spec.tsx +0 -1
- package/js/utils/OrderHelper/index.ts +6 -0
- package/js/utils/test/factories/joanie.ts +0 -1
- package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.tsx +19 -27
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +2 -2
- package/package.json +1 -1
|
@@ -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,
|
package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.tsx
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
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 =
|
|
159
|
+
const canPurchase = OrderHelper.isPurchasable(order);
|
|
160
160
|
const hasPurchased = OrderHelper.isActive(order);
|
|
161
161
|
const canEnroll = OrderHelper.allowEnrollment(order);
|
|
162
162
|
|