richie-education 3.3.1-dev13 → 3.3.1-dev15
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/pages/DashboardBatchOrders/index.spec.tsx +103 -0
- package/js/widgets/Dashboard/components/DashboardItem/BatchOrder/BatchOrderPaymentModal/BatchOrderPaymentManager.tsx +15 -27
- package/js/widgets/cunningham-fr-FR-locale.json +80 -0
- package/js/widgets/index.tsx +6 -1
- package/package.json +1 -1
|
@@ -234,4 +234,107 @@ describe('<DashboardBatchOrders/>', () => {
|
|
|
234
234
|
|
|
235
235
|
await screen.findByText('Completed');
|
|
236
236
|
});
|
|
237
|
+
|
|
238
|
+
it('allows retrying payment after aborting the payment tunnel', async () => {
|
|
239
|
+
const batchOrder = BatchOrderReadFactory({
|
|
240
|
+
payment_method: PaymentMethod.CARD_PAYMENT,
|
|
241
|
+
state: BatchOrderState.PENDING,
|
|
242
|
+
total: 200,
|
|
243
|
+
currency: 'EUR',
|
|
244
|
+
}).one();
|
|
245
|
+
|
|
246
|
+
fetchMock.get(`https://joanie.endpoint/api/v1.0/batch-orders/?page=1&page_size=${perPage}`, {
|
|
247
|
+
results: [batchOrder],
|
|
248
|
+
count: 1,
|
|
249
|
+
next: null,
|
|
250
|
+
previous: null,
|
|
251
|
+
});
|
|
252
|
+
fetchMock.get(`https://joanie.endpoint/api/v1.0/batch-orders/`, [batchOrder]);
|
|
253
|
+
fetchMock.get(`https://joanie.endpoint/api/v1.0/batch-orders/${batchOrder.id}/`, batchOrder);
|
|
254
|
+
|
|
255
|
+
render(<DashboardTest initialRoute={LearnerDashboardPaths.BATCH_ORDERS} />, {
|
|
256
|
+
wrapper: BaseJoanieAppWrapper,
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
await expectNoSpinner('Loading batch orders...');
|
|
260
|
+
await screen.findByText('Payment required');
|
|
261
|
+
|
|
262
|
+
// Open modal and start payment
|
|
263
|
+
await userEvent.click(await screen.findByRole('button', { name: 'Pay €200.00' }));
|
|
264
|
+
|
|
265
|
+
fetchMock.post(
|
|
266
|
+
`https://joanie.endpoint/api/v1.0/batch-orders/${batchOrder.id}/submit-for-payment/`,
|
|
267
|
+
{ payment_id: 'payment_id', provider: 'payment_provider', url: 'payment_url' },
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
const firstModal = await screen.findByRole('dialog');
|
|
271
|
+
await userEvent.click(within(firstModal).getByRole('button', { name: 'Pay €200.00' }));
|
|
272
|
+
|
|
273
|
+
// Close payment modal
|
|
274
|
+
await screen.findByTestId('payment-abort');
|
|
275
|
+
fetchMock.get(
|
|
276
|
+
`https://joanie.endpoint/api/v1.0/batch-orders/?page=1&page_size=${perPage}`,
|
|
277
|
+
{
|
|
278
|
+
results: [{ ...batchOrder, state: BatchOrderState.PROCESS_PAYMENT }],
|
|
279
|
+
count: 1,
|
|
280
|
+
next: null,
|
|
281
|
+
previous: null,
|
|
282
|
+
},
|
|
283
|
+
{ overwriteRoutes: true },
|
|
284
|
+
);
|
|
285
|
+
fetchMock.get(
|
|
286
|
+
`https://joanie.endpoint/api/v1.0/batch-orders/${batchOrder.id}/`,
|
|
287
|
+
{ ...batchOrder, state: BatchOrderState.PROCESS_PAYMENT },
|
|
288
|
+
{ overwriteRoutes: true },
|
|
289
|
+
);
|
|
290
|
+
await userEvent.click(screen.getByTestId('payment-abort'));
|
|
291
|
+
|
|
292
|
+
const modalAfterAbort = await screen.findByRole('dialog');
|
|
293
|
+
await userEvent.click(within(modalAfterAbort).getByRole('button', { name: 'close' }));
|
|
294
|
+
await waitFor(() => expect(screen.queryByRole('dialog')).not.toBeInTheDocument());
|
|
295
|
+
|
|
296
|
+
await screen.findByText('Payment required');
|
|
297
|
+
await screen.findByRole('button', { name: 'Pay €200.00' });
|
|
298
|
+
|
|
299
|
+
// Retry payment successfully
|
|
300
|
+
await userEvent.click(await screen.findByRole('button', { name: 'Pay €200.00' }));
|
|
301
|
+
|
|
302
|
+
fetchMock.post(
|
|
303
|
+
`https://joanie.endpoint/api/v1.0/batch-orders/${batchOrder.id}/submit-for-payment/`,
|
|
304
|
+
{ payment_id: 'payment_id', provider: 'payment_provider', url: 'payment_url' },
|
|
305
|
+
{ overwriteRoutes: true },
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
const retryModal = await screen.findByRole('dialog');
|
|
309
|
+
await userEvent.click(within(retryModal).getByRole('button', { name: 'Pay €200.00' }));
|
|
310
|
+
|
|
311
|
+
await screen.findByTestId('payment-success');
|
|
312
|
+
|
|
313
|
+
fetchMock.get(
|
|
314
|
+
`https://joanie.endpoint/api/v1.0/batch-orders/?page=1&page_size=${perPage}`,
|
|
315
|
+
{
|
|
316
|
+
results: [{ ...batchOrder, state: BatchOrderState.COMPLETED }],
|
|
317
|
+
count: 1,
|
|
318
|
+
next: null,
|
|
319
|
+
previous: null,
|
|
320
|
+
},
|
|
321
|
+
{ overwriteRoutes: true },
|
|
322
|
+
);
|
|
323
|
+
fetchMock.get(
|
|
324
|
+
`https://joanie.endpoint/api/v1.0/batch-orders/${batchOrder.id}/`,
|
|
325
|
+
{ ...batchOrder, state: BatchOrderState.COMPLETED },
|
|
326
|
+
{ overwriteRoutes: true },
|
|
327
|
+
);
|
|
328
|
+
|
|
329
|
+
await userEvent.click(screen.getByTestId('payment-success'));
|
|
330
|
+
|
|
331
|
+
await waitFor(() => {
|
|
332
|
+
expect(mockMessageModal).toHaveBeenCalledWith(
|
|
333
|
+
expect.objectContaining({ title: 'Payment successful' }),
|
|
334
|
+
);
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
expect(screen.queryByRole('button', { name: /Pay/ })).not.toBeInTheDocument();
|
|
338
|
+
await screen.findByText('Completed');
|
|
339
|
+
});
|
|
237
340
|
});
|
|
@@ -2,7 +2,7 @@ import React, { useEffect } from 'react';
|
|
|
2
2
|
import { Button, useModal } from '@openfun/cunningham-react';
|
|
3
3
|
import { useIntl, FormattedMessage, defineMessages } from 'react-intl';
|
|
4
4
|
import { useBatchOrder } from 'hooks/useBatchOrder';
|
|
5
|
-
import { BatchOrderRead
|
|
5
|
+
import { BatchOrderRead } from 'types/Joanie';
|
|
6
6
|
import { DashboardSubItem } from 'widgets/Dashboard/components/DashboardItem/DashboardSubItem';
|
|
7
7
|
import { DashboardSubItemsList } from '../../DashboardSubItemsList';
|
|
8
8
|
import { BatchOrderPaymentModal } from '.';
|
|
@@ -13,11 +13,6 @@ const messages = defineMessages({
|
|
|
13
13
|
description: 'Label before the payment button',
|
|
14
14
|
defaultMessage: 'We are waiting for your payment to finalize the batch order.',
|
|
15
15
|
},
|
|
16
|
-
paymentProcessing: {
|
|
17
|
-
id: 'components.ProductCertificateFooter.paymentProcessing',
|
|
18
|
-
description: 'Button label for the payment is processing message',
|
|
19
|
-
defaultMessage: 'Payment processing...',
|
|
20
|
-
},
|
|
21
16
|
paymentNeededButton: {
|
|
22
17
|
id: 'components.ProductCertificateFooter.paymentNeededButton',
|
|
23
18
|
description: 'Button label for the payment needed message',
|
|
@@ -38,7 +33,6 @@ export const BatchOrderPaymentManager = ({ batchOrder }: BatchPaymentManagerProp
|
|
|
38
33
|
const intl = useIntl();
|
|
39
34
|
const retryModal = useModal();
|
|
40
35
|
const batchOrderQuery = useBatchOrder(batchOrder.id);
|
|
41
|
-
const processingPayment = batchOrder.state === BatchOrderState.PROCESS_PAYMENT;
|
|
42
36
|
|
|
43
37
|
useEffect(() => {
|
|
44
38
|
if (batchOrderQuery.item) {
|
|
@@ -54,30 +48,24 @@ export const BatchOrderPaymentManager = ({ batchOrder }: BatchPaymentManagerProp
|
|
|
54
48
|
footer={
|
|
55
49
|
<div className="content">
|
|
56
50
|
<FormattedMessage {...messages.batchOrderPayment} />
|
|
57
|
-
<Button
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
{...messages.paymentNeededButton}
|
|
68
|
-
values={{
|
|
69
|
-
amount: intl.formatNumber(batchOrder.total ?? 0, {
|
|
70
|
-
style: 'currency',
|
|
71
|
-
currency: batchOrder.currency ?? 'EUR',
|
|
72
|
-
}),
|
|
73
|
-
}}
|
|
74
|
-
/>
|
|
75
|
-
)}
|
|
51
|
+
<Button size="small" color="primary" onClick={retryModal.open}>
|
|
52
|
+
<FormattedMessage
|
|
53
|
+
{...messages.paymentNeededButton}
|
|
54
|
+
values={{
|
|
55
|
+
amount: intl.formatNumber(batchOrder.total ?? 0, {
|
|
56
|
+
style: 'currency',
|
|
57
|
+
currency: batchOrder.currency ?? 'EUR',
|
|
58
|
+
}),
|
|
59
|
+
}}
|
|
60
|
+
/>
|
|
76
61
|
</Button>
|
|
77
62
|
<BatchOrderPaymentModal
|
|
78
63
|
{...retryModal}
|
|
79
64
|
batchOrder={batchOrder}
|
|
80
|
-
onClose={
|
|
65
|
+
onClose={() => {
|
|
66
|
+
retryModal.onClose();
|
|
67
|
+
batchOrderQuery.methods.invalidate();
|
|
68
|
+
}}
|
|
81
69
|
/>
|
|
82
70
|
</div>
|
|
83
71
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"components": {
|
|
3
|
+
"alert": {
|
|
4
|
+
"close_aria_label": "Supprimer l'alerte",
|
|
5
|
+
"expand_aria_label": "Ouvrir l'alerte",
|
|
6
|
+
"shrink_aria_label": "Fermer l'alerte"
|
|
7
|
+
},
|
|
8
|
+
"pagination": {
|
|
9
|
+
"goto_label": "Aller à",
|
|
10
|
+
"goto_label_aria": "Aller à la page",
|
|
11
|
+
"next_aria": "Aller à la page suivante",
|
|
12
|
+
"previous_aria": "Aller à la page précédente",
|
|
13
|
+
"goto_page_aria": "Aller à la page {page}",
|
|
14
|
+
"current_page_aria": "Vous êtes actuellement sur la page {page}"
|
|
15
|
+
},
|
|
16
|
+
"datagrid": {
|
|
17
|
+
"empty": "Ce tableau est vide",
|
|
18
|
+
"empty_alt": "Illustration d'un tableau vide",
|
|
19
|
+
"loader_aria": "Chargement des données",
|
|
20
|
+
"rows_selection_aria": "Toutes les lignes sélectionnées",
|
|
21
|
+
"row_selection_aria": "Ligne sélectionnée"
|
|
22
|
+
},
|
|
23
|
+
"provider": {
|
|
24
|
+
"test": "Ceci est un test : {name}"
|
|
25
|
+
},
|
|
26
|
+
"forms": {
|
|
27
|
+
"input": {
|
|
28
|
+
"password": {
|
|
29
|
+
"show_password": "Afficher le mot de passe",
|
|
30
|
+
"hide_password": "Masquer le mot de passe"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"select": {
|
|
34
|
+
"toggle_button_aria_label": "Ouvrir le menu",
|
|
35
|
+
"clear_button_aria_label": "Effacer la sélection",
|
|
36
|
+
"clear_all_button_aria_label": "Effacer toutes les sélections",
|
|
37
|
+
"menu_empty_placeholder": "Aucun choix disponible"
|
|
38
|
+
},
|
|
39
|
+
"file_uploader": {
|
|
40
|
+
"delete_file_name": "Supprimer le fichier {name}",
|
|
41
|
+
"delete_file": "Supprimer le fichier",
|
|
42
|
+
"uploading": "Upload en cours ...",
|
|
43
|
+
"caption": "Glisser-déposer ou ",
|
|
44
|
+
"browse_files": "Parcourir"
|
|
45
|
+
},
|
|
46
|
+
"date_picker": {
|
|
47
|
+
"toggle_button_aria_label_open": "Ouvrir le calendrier",
|
|
48
|
+
"toggle_button_aria_label_close": "Fermer le calendrier",
|
|
49
|
+
"clear_button_aria_label": "Effacer la date",
|
|
50
|
+
"next_month_button_aria_label": "Mois suivant",
|
|
51
|
+
"next_year_button_aria_label": "Année suivante",
|
|
52
|
+
"previous_month_button_aria_label": "Mois précédent",
|
|
53
|
+
"previous_year_button_aria_label": "Année précédente",
|
|
54
|
+
"year_select_button_aria_label": "Sélectionner une année",
|
|
55
|
+
"month_select_button_aria_label": "Sélectionner un mois"
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
"modals": {
|
|
59
|
+
"helpers": {
|
|
60
|
+
"delete_confirmation": {
|
|
61
|
+
"title": "Êtes-vous sûr ?",
|
|
62
|
+
"children": "Êtes-vous sûr de vouloir supprimer cet élément ?",
|
|
63
|
+
"cancel": "Annuler",
|
|
64
|
+
"delete": "Supprimer"
|
|
65
|
+
},
|
|
66
|
+
"confirmation": {
|
|
67
|
+
"title": "Êtes-vous sûr ?",
|
|
68
|
+
"children": "Êtes-vous sûr de vouloir faire cela ?",
|
|
69
|
+
"cancel": "Annuler",
|
|
70
|
+
"yes": "Oui"
|
|
71
|
+
},
|
|
72
|
+
"disclaimer": {
|
|
73
|
+
"title": "Avertissement",
|
|
74
|
+
"children": "Ceci est un avertissement",
|
|
75
|
+
"ok": "Ok"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
package/js/widgets/index.tsx
CHANGED
|
@@ -4,6 +4,7 @@ import startCase from 'lodash-es/startCase';
|
|
|
4
4
|
import { lazy, Suspense } from 'react';
|
|
5
5
|
import ReactDOM from 'react-dom';
|
|
6
6
|
import { CunninghamProvider } from '@openfun/cunningham-react';
|
|
7
|
+
import cunninghamFrFRLocale from 'widgets/cunningham-fr-FR-locale.json';
|
|
7
8
|
import { HistoryProvider } from 'hooks/useHistory';
|
|
8
9
|
import { SessionProvider } from 'contexts/SessionContext';
|
|
9
10
|
import { Spinner } from 'components/Spinner';
|
|
@@ -105,7 +106,11 @@ export const Root = ({ richieReactSpots, locale = 'en-US' }: RootProps) => {
|
|
|
105
106
|
});
|
|
106
107
|
|
|
107
108
|
return (
|
|
108
|
-
<CunninghamProvider
|
|
109
|
+
<CunninghamProvider
|
|
110
|
+
currentLocale={locale}
|
|
111
|
+
// TODO: remove customLocales and the cunningham-fr-FR-locale.json file once Cunningham is upgraded to v4+
|
|
112
|
+
customLocales={{ 'fr-FR': cunninghamFrFRLocale }}
|
|
113
|
+
>
|
|
109
114
|
<SessionProvider>
|
|
110
115
|
<HistoryProvider>
|
|
111
116
|
<Suspense fallback={<Spinner />}>{portals}</Suspense>
|