richie-education 2.28.2-dev26 → 2.28.2-dev53
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/api/joanie.ts +42 -17
- package/js/api/lms/dummy.ts +1 -12
- package/js/components/ContractFrame/AbstractContractFrame.spec.tsx +16 -9
- package/js/components/ContractFrame/AbstractContractFrame.tsx +28 -23
- package/js/components/ContractFrame/LearnerContractFrame.tsx +2 -2
- package/js/components/ContractFrame/_styles.scss +6 -14
- package/js/components/{SaleTunnel/CreditCardSelector → CreditCardSelector}/index.spec.tsx +15 -45
- package/js/components/{SaleTunnel/CreditCardSelector → CreditCardSelector}/index.tsx +17 -24
- package/js/components/DownloadContractButton/index.spec.tsx +1 -1
- package/js/components/OpenEdxFullNameForm/index.spec.tsx +229 -0
- package/js/components/OpenEdxFullNameForm/index.tsx +7 -7
- package/js/components/PaymentInterfaces/LyraPopIn.tsx +2 -2
- package/js/components/PaymentInterfaces/PayplugLightbox.tsx +1 -1
- package/js/components/PaymentInterfaces/__mocks__/index.tsx +1 -4
- package/js/components/PaymentInterfaces/types.ts +5 -2
- package/js/components/PaymentScheduleGrid/_styles.scss +13 -0
- package/js/components/PaymentScheduleGrid/index.tsx +50 -70
- package/js/components/PurchaseButton/index.spec.tsx +84 -37
- package/js/components/SaleTunnel/AddressSelector/index.spec.tsx +2 -1
- package/js/components/SaleTunnel/CertificateSaleTunnel/index.tsx +2 -2
- package/js/components/SaleTunnel/CredentialSaleTunnel/index.tsx +6 -10
- package/js/components/SaleTunnel/GenericSaleTunnel.tsx +80 -27
- package/js/components/SaleTunnel/SaleTunnelInformation/index.tsx +16 -20
- package/js/components/SaleTunnel/SaleTunnelSavePaymentMethod/_styles.scss +12 -0
- package/js/components/SaleTunnel/SaleTunnelSavePaymentMethod/index.tsx +160 -0
- package/js/components/SaleTunnel/SaleTunnelSuccess/index.tsx +15 -29
- package/js/components/SaleTunnel/Sponsors/SaleTunnelSponsors.scss +4 -5
- package/js/components/SaleTunnel/Sponsors/SaleTunnelSponsors.tsx +39 -11
- package/js/components/SaleTunnel/SubscriptionButton/_styles.scss +7 -0
- package/js/components/SaleTunnel/SubscriptionButton/index.tsx +201 -0
- package/js/components/SaleTunnel/_styles.scss +16 -5
- package/js/components/SaleTunnel/hooks/useTerms.tsx +0 -77
- package/js/components/SaleTunnel/index.credential.spec.tsx +14 -25
- package/js/components/SaleTunnel/index.full-process.spec.tsx +116 -48
- package/js/components/SaleTunnel/index.spec.tsx +334 -717
- package/js/components/SignContractButton/index.omniscientOrders.spec.tsx +16 -11
- package/js/components/SignContractButton/index.spec.tsx +16 -20
- package/js/components/SignContractButton/index.tsx +3 -1
- package/js/hooks/useCreditCards/index.spec.tsx +70 -6
- package/js/hooks/useCreditCards/index.ts +49 -11
- package/js/hooks/useOrders/index.spec.tsx +322 -0
- package/js/hooks/{useOrders.ts → useOrders/index.ts} +40 -14
- package/js/hooks/usePaymentSchedule.tsx +23 -0
- package/js/hooks/useProductOrder/index.spec.tsx +77 -60
- package/js/hooks/useProductOrder/index.tsx +2 -2
- package/js/hooks/useResources/useResourcesRoot.ts +4 -3
- package/js/index.tsx +2 -0
- package/js/pages/DashboardCreditCardsManagement/CreditCardBrandLogo.spec.tsx +1 -1
- package/js/pages/DashboardCreditCardsManagement/CreditCardBrandLogo.tsx +4 -2
- package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.spec.tsx +8 -5
- package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.spec.tsx +8 -9
- package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.spec.tsx +1 -1
- package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.tsx +1 -6
- package/js/settings/settings.test.ts +11 -2
- package/js/types/Joanie.ts +77 -31
- package/js/utils/OrderHelper/index.ts +47 -38
- package/js/utils/test/factories/joanie.ts +66 -68
- package/js/widgets/Dashboard/components/DashboardItem/CourseEnrolling/index.tsx +8 -18
- package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.spec.tsx +26 -32
- package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.tsx +11 -6
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.spec.tsx +114 -5
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +99 -12
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.spec.tsx +3 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.useUnionResource.cache.spec.tsx +6 -7
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentDetailsModal/_styles.scss +7 -0
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentDetailsModal/index.tsx +126 -0
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentRetryModal/index.tsx +209 -0
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateLearnerMessage/index.spec.tsx +18 -71
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateLearnerMessage/index.tsx +40 -25
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateMessage/index.tsx +28 -22
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateTeacherMessage/index.spec.tsx +18 -73
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateTeacherMessage/index.tsx +32 -16
- package/js/widgets/Dashboard/components/DashboardOrderLoader/index.tsx +3 -11
- package/js/widgets/Dashboard/components/Signature/SignatureDummy.tsx +25 -3
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/EnrollableCourseRunList.tsx +2 -6
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/index.spec.tsx +7 -14
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseRunItem/index.spec.tsx +7 -5
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseRunItem/index.tsx +5 -7
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +242 -332
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +12 -13
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +10 -21
- package/js/widgets/SyllabusCourseRunsList/components/CourseRunEnrollment/index.joanie.spec.tsx +2 -2
- package/package.json +2 -1
- package/scss/components/_index.scss +4 -2
- package/js/components/PaymentButton/_styles.scss +0 -27
- package/js/components/SaleTunnel/GenericPaymentButton/index.tsx +0 -338
- package/js/components/SaleTunnel/SaleTunnelNotValidated/index.tsx +0 -70
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/ProductSignatureHeader/index.tsx +0 -41
- /package/js/components/{SaleTunnel/CreditCardSelector → CreditCardSelector}/_styles.scss +0 -0
package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.spec.tsx
CHANGED
|
@@ -3,6 +3,7 @@ import { render, screen } from '@testing-library/react';
|
|
|
3
3
|
import { IntlProvider } from 'react-intl';
|
|
4
4
|
import { PropsWithChildren } from 'react';
|
|
5
5
|
import { QueryClientProvider } from '@tanstack/react-query';
|
|
6
|
+
import { CunninghamProvider } from '@openfun/cunningham-react';
|
|
6
7
|
import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
|
|
7
8
|
import JoanieApiProvider from 'contexts/JoanieApiContext';
|
|
8
9
|
|
|
@@ -50,11 +51,13 @@ jest.mock('pages/TeacherDashboardContractsLayout/hooks/useHasContractToDownload/
|
|
|
50
51
|
describe('TeacherDashboardContractsLayout/ContractActionsBar', () => {
|
|
51
52
|
const Wrapper = ({ children }: PropsWithChildren) => {
|
|
52
53
|
return (
|
|
53
|
-
<
|
|
54
|
-
<
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
54
|
+
<CunninghamProvider>
|
|
55
|
+
<IntlProvider locale="en">
|
|
56
|
+
<QueryClientProvider client={createTestQueryClient({ user: true })}>
|
|
57
|
+
<JoanieApiProvider>{children}</JoanieApiProvider>
|
|
58
|
+
</QueryClientProvider>
|
|
59
|
+
</IntlProvider>
|
|
60
|
+
</CunninghamProvider>
|
|
58
61
|
);
|
|
59
62
|
};
|
|
60
63
|
|
|
@@ -5,6 +5,7 @@ import { IntlProvider } from 'react-intl';
|
|
|
5
5
|
import { QueryClientProvider } from '@tanstack/react-query';
|
|
6
6
|
import fetchMock from 'fetch-mock';
|
|
7
7
|
import userEvent from '@testing-library/user-event';
|
|
8
|
+
import { CunninghamProvider } from '@openfun/cunningham-react';
|
|
8
9
|
import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
|
|
9
10
|
import JoanieApiProvider from 'contexts/JoanieApiContext';
|
|
10
11
|
import { createTestQueryClient } from 'utils/test/createTestQueryClient';
|
|
@@ -21,11 +22,13 @@ jest.mock('utils/context', () => ({
|
|
|
21
22
|
describe('TeacherDashboardContractsLayout/SignOrganizationContractButton', () => {
|
|
22
23
|
const Wrapper = ({ children }: PropsWithChildren) => {
|
|
23
24
|
return (
|
|
24
|
-
<
|
|
25
|
-
<
|
|
26
|
-
<
|
|
27
|
-
|
|
28
|
-
|
|
25
|
+
<CunninghamProvider>
|
|
26
|
+
<IntlProvider locale="en">
|
|
27
|
+
<QueryClientProvider client={createTestQueryClient({ user: true })}>
|
|
28
|
+
<JoanieApiProvider>{children}</JoanieApiProvider>
|
|
29
|
+
</QueryClientProvider>
|
|
30
|
+
</IntlProvider>
|
|
31
|
+
</CunninghamProvider>
|
|
29
32
|
);
|
|
30
33
|
};
|
|
31
34
|
|
|
@@ -46,8 +49,6 @@ describe('TeacherDashboardContractsLayout/SignOrganizationContractButton', () =>
|
|
|
46
49
|
expect(
|
|
47
50
|
screen.getByRole('button', { name: 'Sign all pending contracts (12)' }),
|
|
48
51
|
).toBeInTheDocument();
|
|
49
|
-
const DashboardContractFramePortal = document.getElementsByClassName('ReactModalPortal');
|
|
50
|
-
expect(DashboardContractFramePortal).toHaveLength(1);
|
|
51
52
|
});
|
|
52
53
|
|
|
53
54
|
it("shouldn't display sign button user don't have some contract to sign", () => {
|
|
@@ -63,8 +64,6 @@ describe('TeacherDashboardContractsLayout/SignOrganizationContractButton', () =>
|
|
|
63
64
|
expect(
|
|
64
65
|
screen.queryByRole('button', { name: /Sign all pending contracts/ }),
|
|
65
66
|
).not.toBeInTheDocument();
|
|
66
|
-
const DashboardContractFramePortal = document.getElementsByClassName('ReactModalPortal');
|
|
67
|
-
expect(DashboardContractFramePortal).toHaveLength(1);
|
|
68
67
|
});
|
|
69
68
|
|
|
70
69
|
it.each([
|
|
@@ -14,7 +14,7 @@ import CourseLearnerDataGrid from '.';
|
|
|
14
14
|
describe('pages/CourseLearnerDataGrid', () => {
|
|
15
15
|
it('should render a list of user', async () => {
|
|
16
16
|
const courseOrderList = NestedCourseOrderFactory({
|
|
17
|
-
state: OrderState.
|
|
17
|
+
state: OrderState.COMPLETED,
|
|
18
18
|
certificate_id: faker.string.uuid(),
|
|
19
19
|
}).many(3);
|
|
20
20
|
render(
|
package/js/pages/TeacherDashboardCourseLearnersLayout/components/CourseLearnerDataGrid/index.tsx
CHANGED
|
@@ -74,12 +74,7 @@ const CourseLearnerDataGrid = ({
|
|
|
74
74
|
headerName: intl.formatMessage(messages.columnState),
|
|
75
75
|
enableSorting: false,
|
|
76
76
|
renderCell: (params: { row: Row }) => {
|
|
77
|
-
return
|
|
78
|
-
<OrderStateTeacherMessage
|
|
79
|
-
order={params.row.courseOrder}
|
|
80
|
-
contractDefinition={params.row.courseOrder.product.contract_definition_id}
|
|
81
|
-
/>
|
|
82
|
-
);
|
|
77
|
+
return <OrderStateTeacherMessage order={params.row.courseOrder} />;
|
|
83
78
|
},
|
|
84
79
|
},
|
|
85
80
|
{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// This
|
|
2
|
-
// Mostly
|
|
1
|
+
// This configuration file is used for testing.
|
|
2
|
+
// Mostly useful to test our test tools.
|
|
3
3
|
import { DevDemoUser } from 'api/lms/dummy';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -14,3 +14,12 @@ import { DevDemoUser } from 'api/lms/dummy';
|
|
|
14
14
|
* * student_user
|
|
15
15
|
*/
|
|
16
16
|
export const CURRENT_JOANIE_DEV_DEMO_USER: DevDemoUser = 'admin';
|
|
17
|
+
|
|
18
|
+
export const CONTRACT_SETTINGS = {
|
|
19
|
+
// Interval in ms to poll the related order when a signature has succeeded.
|
|
20
|
+
pollInterval: 150,
|
|
21
|
+
// Number of retries
|
|
22
|
+
pollLimit: 45,
|
|
23
|
+
// Simulated sign request delay
|
|
24
|
+
dummySignatureSignTimeout: 100,
|
|
25
|
+
};
|
package/js/types/Joanie.ts
CHANGED
|
@@ -259,14 +259,42 @@ export interface EnrollmentLight {
|
|
|
259
259
|
|
|
260
260
|
// Order
|
|
261
261
|
export enum OrderState {
|
|
262
|
-
|
|
263
|
-
SUBMITTED = 'submitted',
|
|
262
|
+
ASSIGNED = 'assigned',
|
|
264
263
|
CANCELED = 'canceled',
|
|
264
|
+
COMPLETED = 'completed',
|
|
265
|
+
DRAFT = 'draft',
|
|
266
|
+
FAILED_PAYMENT = 'failed_payment',
|
|
267
|
+
NO_PAYMENT = 'no_payment',
|
|
265
268
|
PENDING = 'pending',
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
269
|
+
PENDING_PAYMENT = 'pending_payment',
|
|
270
|
+
SIGNING = 'signing',
|
|
271
|
+
TO_SAVE_PAYMENT_METHOD = 'to_save_payment_method',
|
|
272
|
+
TO_SIGN = 'to_sign',
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export const PURCHASABLE_ORDER_STATES = [
|
|
276
|
+
OrderState.DRAFT,
|
|
277
|
+
OrderState.ASSIGNED,
|
|
278
|
+
OrderState.TO_SIGN,
|
|
279
|
+
OrderState.SIGNING,
|
|
280
|
+
OrderState.TO_SAVE_PAYMENT_METHOD,
|
|
281
|
+
];
|
|
282
|
+
|
|
283
|
+
export const ACTIVE_ORDER_STATES = [
|
|
284
|
+
OrderState.PENDING,
|
|
285
|
+
OrderState.PENDING_PAYMENT,
|
|
286
|
+
OrderState.NO_PAYMENT,
|
|
287
|
+
OrderState.FAILED_PAYMENT,
|
|
288
|
+
OrderState.COMPLETED,
|
|
289
|
+
];
|
|
290
|
+
|
|
291
|
+
export const NOT_CANCELED_ORDER_STATES = [...ACTIVE_ORDER_STATES, ...PURCHASABLE_ORDER_STATES];
|
|
292
|
+
|
|
293
|
+
export const ENROLLABLE_ORDER_STATES = [
|
|
294
|
+
OrderState.COMPLETED,
|
|
295
|
+
OrderState.PENDING_PAYMENT,
|
|
296
|
+
OrderState.FAILED_PAYMENT,
|
|
297
|
+
];
|
|
270
298
|
|
|
271
299
|
export interface Order {
|
|
272
300
|
id: string;
|
|
@@ -286,6 +314,8 @@ export interface Order {
|
|
|
286
314
|
organization_id: Organization['id'];
|
|
287
315
|
organization: Organization;
|
|
288
316
|
order_group_id?: OrderGroup['id'];
|
|
317
|
+
payment_schedule?: PaymentSchedule;
|
|
318
|
+
credit_card_id?: CreditCard['id'];
|
|
289
319
|
}
|
|
290
320
|
|
|
291
321
|
export interface CredentialOrder extends Order {
|
|
@@ -293,20 +323,12 @@ export interface CredentialOrder extends Order {
|
|
|
293
323
|
enrollment: null;
|
|
294
324
|
}
|
|
295
325
|
|
|
296
|
-
export interface CredentialOrderWithPaymentInfo extends CredentialOrder {
|
|
297
|
-
payment_info: Payment;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
326
|
export interface CertificateOrder extends Order {
|
|
301
327
|
course: null;
|
|
302
328
|
enrollment: EnrollmentLight;
|
|
303
329
|
target_courses: never[];
|
|
304
330
|
}
|
|
305
331
|
|
|
306
|
-
export interface CertificateOrderWithPaymentInfo extends CertificateOrder {
|
|
307
|
-
payment_info: Payment;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
332
|
export type OrderLite = Pick<
|
|
311
333
|
Order,
|
|
312
334
|
| 'id'
|
|
@@ -325,9 +347,11 @@ export interface AbstractNestedOrder {
|
|
|
325
347
|
product_title: string;
|
|
326
348
|
owner_name: string;
|
|
327
349
|
state: OrderState;
|
|
350
|
+
course: Nullable<CourseLight>;
|
|
351
|
+
enrollment: Nullable<EnrollmentLight>;
|
|
328
352
|
}
|
|
329
353
|
export interface NestedCertificateOrder extends AbstractNestedOrder {
|
|
330
|
-
course:
|
|
354
|
+
course: null;
|
|
331
355
|
enrollment: EnrollmentLight;
|
|
332
356
|
}
|
|
333
357
|
export const isNestedCredentialOrder = (
|
|
@@ -338,7 +362,7 @@ export const isNestedCredentialOrder = (
|
|
|
338
362
|
|
|
339
363
|
export interface NestedCredentialOrder extends AbstractNestedOrder {
|
|
340
364
|
course: CourseLight;
|
|
341
|
-
enrollment:
|
|
365
|
+
enrollment: null;
|
|
342
366
|
}
|
|
343
367
|
|
|
344
368
|
export type OrderEnrollment = Pick<Order, 'id' | 'state' | 'product_id' | 'certificate_id'>;
|
|
@@ -372,10 +396,10 @@ export interface OrderGroup {
|
|
|
372
396
|
}
|
|
373
397
|
|
|
374
398
|
export enum CreditCardBrand {
|
|
375
|
-
MASTERCARD = '
|
|
376
|
-
MAESTRO = '
|
|
377
|
-
VISA = '
|
|
378
|
-
CB = '
|
|
399
|
+
MASTERCARD = 'mastercard',
|
|
400
|
+
MAESTRO = 'maestro',
|
|
401
|
+
VISA = 'visa',
|
|
402
|
+
CB = 'cb',
|
|
379
403
|
}
|
|
380
404
|
|
|
381
405
|
// Credit Card
|
|
@@ -416,6 +440,22 @@ export interface OrderPaymentInfo {
|
|
|
416
440
|
payment_info: Payment;
|
|
417
441
|
}
|
|
418
442
|
|
|
443
|
+
export enum PaymentScheduleState {
|
|
444
|
+
PENDING = 'pending',
|
|
445
|
+
PAID = 'paid',
|
|
446
|
+
REFUSED = 'refused',
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export interface PaymentInstallment {
|
|
450
|
+
id: string;
|
|
451
|
+
amount: number;
|
|
452
|
+
currency: string;
|
|
453
|
+
due_date: string;
|
|
454
|
+
state: PaymentScheduleState;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export type PaymentSchedule = readonly PaymentInstallment[];
|
|
458
|
+
|
|
419
459
|
// - API
|
|
420
460
|
export interface AddressCreationPayload extends Omit<Address, 'id' | 'is_main'> {
|
|
421
461
|
is_main?: boolean;
|
|
@@ -424,7 +464,7 @@ export interface AddressCreationPayload extends Omit<Address, 'id' | 'is_main'>
|
|
|
424
464
|
interface AbstractOrderProductCreationPayload {
|
|
425
465
|
product_id: Product['id'];
|
|
426
466
|
order_group_id?: OrderGroup['id'];
|
|
427
|
-
|
|
467
|
+
billing_address: Omit<Address, 'id' | 'is_main'>;
|
|
428
468
|
}
|
|
429
469
|
|
|
430
470
|
interface OrderCertificateCreationPayload extends AbstractOrderProductCreationPayload {
|
|
@@ -436,15 +476,13 @@ export interface OrderCredentialCreationPayload extends AbstractOrderProductCrea
|
|
|
436
476
|
|
|
437
477
|
export type OrderCreationPayload = OrderCertificateCreationPayload | OrderCredentialCreationPayload;
|
|
438
478
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
}
|
|
479
|
+
export type OrderSubmitInstallmentPayment = {
|
|
480
|
+
credit_card_id?: string;
|
|
481
|
+
};
|
|
443
482
|
|
|
444
|
-
interface
|
|
483
|
+
interface OrderSetPaymentMethodPayload {
|
|
445
484
|
id: Order['id'];
|
|
446
|
-
|
|
447
|
-
credit_card_id?: CreditCard['id'];
|
|
485
|
+
credit_card_id: CreditCard['id'];
|
|
448
486
|
}
|
|
449
487
|
|
|
450
488
|
export interface PaginatedResourceQuery extends ResourcesQuery {
|
|
@@ -539,14 +577,15 @@ interface APIUser {
|
|
|
539
577
|
update(payload: Address): Promise<Address>;
|
|
540
578
|
};
|
|
541
579
|
creditCards: {
|
|
542
|
-
create(payload: Omit<CreditCard, 'id'>): Promise<CreditCard>;
|
|
543
580
|
delete(id: CreditCard['id']): Promise<void>;
|
|
581
|
+
get(): Promise<PaginatedResponse<CreditCard>>;
|
|
544
582
|
get(filters?: ResourcesQuery): Promise<CreditCard>;
|
|
545
583
|
get(): Promise<CreditCard[]>;
|
|
546
584
|
update(payload: CreditCard): Promise<CreditCard>;
|
|
585
|
+
tokenize(): Promise<Payment>;
|
|
547
586
|
};
|
|
548
587
|
orders: {
|
|
549
|
-
|
|
588
|
+
cancel(id: Order['id']): Promise<void>;
|
|
550
589
|
create(payload: OrderCreationPayload): Promise<CredentialOrder | CertificateOrder>;
|
|
551
590
|
get<Filters extends OrderResourcesQuery = OrderResourcesQuery>(
|
|
552
591
|
filters?: Filters,
|
|
@@ -556,8 +595,12 @@ interface APIUser {
|
|
|
556
595
|
invoice: {
|
|
557
596
|
download(payload: { order_id: Order['id']; invoice_reference: string }): Promise<File>;
|
|
558
597
|
};
|
|
559
|
-
submit(payload: OrderSubmitPayload): Promise<OrderPaymentInfo>;
|
|
560
598
|
submit_for_signature(id: string): Promise<ContractInvitationLinkResponse>;
|
|
599
|
+
submit_installment_payment(
|
|
600
|
+
id: string,
|
|
601
|
+
payload?: OrderSubmitInstallmentPayment,
|
|
602
|
+
): Promise<Payment>;
|
|
603
|
+
set_payment_method(payload: OrderSetPaymentMethodPayload): Promise<void>;
|
|
561
604
|
};
|
|
562
605
|
certificates: {
|
|
563
606
|
download(id: string): Promise<File>;
|
|
@@ -614,6 +657,9 @@ export interface API {
|
|
|
614
657
|
: Promise<PaginatedResponse<CourseListItem>>;
|
|
615
658
|
products: {
|
|
616
659
|
get(filters?: CourseProductQueryFilters): Promise<Nullable<CourseProductRelation>>;
|
|
660
|
+
paymentSchedule: {
|
|
661
|
+
get(filters?: CourseProductQueryFilters): Promise<Nullable<PaymentSchedule>>;
|
|
662
|
+
};
|
|
617
663
|
};
|
|
618
664
|
orders: {
|
|
619
665
|
get(
|
|
@@ -1,49 +1,52 @@
|
|
|
1
1
|
import {
|
|
2
|
-
OrderEnrollment,
|
|
3
2
|
ACTIVE_ORDER_STATES,
|
|
3
|
+
ENROLLABLE_ORDER_STATES,
|
|
4
|
+
NestedCourseOrder,
|
|
4
5
|
Order,
|
|
6
|
+
OrderEnrollment,
|
|
5
7
|
OrderState,
|
|
6
|
-
|
|
7
|
-
NestedCourseOrder,
|
|
8
|
+
PaymentScheduleState,
|
|
8
9
|
} from 'types/Joanie';
|
|
9
10
|
|
|
10
11
|
export enum OrderStatus {
|
|
12
|
+
ASSIGNED = 'assigned',
|
|
13
|
+
CANCELED = 'canceled',
|
|
14
|
+
COMPLETED = 'completed',
|
|
11
15
|
DRAFT = 'draft',
|
|
12
|
-
|
|
16
|
+
FAILED_PAYMENT = 'failed_payment',
|
|
17
|
+
NO_PAYMENT = 'no_payment',
|
|
18
|
+
PASSED = 'passed',
|
|
13
19
|
PENDING = 'pending',
|
|
14
|
-
|
|
15
|
-
WAITING_SIGNATURE = 'waiting_signature',
|
|
20
|
+
PENDING_PAYMENT = 'pending_payment',
|
|
16
21
|
WAITING_COUNTER_SIGNATURE = 'waiting_counter_signature',
|
|
17
|
-
|
|
18
|
-
|
|
22
|
+
WAITING_PAYMENT_METHOD = 'waiting_payment_method',
|
|
23
|
+
WAITING_SIGNATURE = 'waiting_signature',
|
|
19
24
|
}
|
|
20
25
|
|
|
21
26
|
/**
|
|
22
27
|
* Helper class for orders
|
|
23
28
|
*/
|
|
24
29
|
export class OrderHelper {
|
|
25
|
-
static getState(order: Order | NestedCourseOrder
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
if (OrderHelper.orderNeedsCounterSignature(order)) {
|
|
33
|
-
return OrderStatus.WAITING_COUNTER_SIGNATURE;
|
|
34
|
-
}
|
|
35
|
-
if (certificateId) {
|
|
36
|
-
return OrderStatus.COMPLETED;
|
|
37
|
-
} else {
|
|
38
|
-
return OrderStatus.ON_GOING;
|
|
39
|
-
}
|
|
30
|
+
static getState(order: Order | NestedCourseOrder) {
|
|
31
|
+
if (OrderHelper.allowEnrollment(order) && OrderHelper.orderNeedsCounterSignature(order)) {
|
|
32
|
+
return OrderStatus.WAITING_COUNTER_SIGNATURE;
|
|
33
|
+
}
|
|
34
|
+
if (order.state === OrderState.COMPLETED && order.certificate_id) {
|
|
35
|
+
return OrderStatus.PASSED;
|
|
40
36
|
}
|
|
41
37
|
|
|
42
38
|
const orderStatusMap = {
|
|
39
|
+
[OrderState.ASSIGNED]: OrderStatus.ASSIGNED,
|
|
40
|
+
[OrderState.CANCELED]: OrderStatus.CANCELED,
|
|
41
|
+
[OrderState.COMPLETED]: OrderStatus.COMPLETED,
|
|
43
42
|
[OrderState.DRAFT]: OrderStatus.DRAFT,
|
|
44
|
-
[OrderState.
|
|
43
|
+
[OrderState.FAILED_PAYMENT]: OrderStatus.FAILED_PAYMENT,
|
|
44
|
+
[OrderState.NO_PAYMENT]: OrderStatus.NO_PAYMENT,
|
|
45
45
|
[OrderState.PENDING]: OrderStatus.PENDING,
|
|
46
|
-
[OrderState.
|
|
46
|
+
[OrderState.PENDING_PAYMENT]: OrderStatus.PENDING_PAYMENT,
|
|
47
|
+
[OrderState.SIGNING]: OrderStatus.WAITING_SIGNATURE,
|
|
48
|
+
[OrderState.TO_SAVE_PAYMENT_METHOD]: OrderStatus.WAITING_PAYMENT_METHOD,
|
|
49
|
+
[OrderState.TO_SIGN]: OrderStatus.WAITING_SIGNATURE,
|
|
47
50
|
};
|
|
48
51
|
|
|
49
52
|
if (order.state in orderStatusMap) {
|
|
@@ -62,18 +65,8 @@ export class OrderHelper {
|
|
|
62
65
|
return orders.find(filter);
|
|
63
66
|
}
|
|
64
67
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
*/
|
|
68
|
-
static orderNeedsSignature(
|
|
69
|
-
order: Order | NestedCourseOrder,
|
|
70
|
-
contractDefinition?: ContractDefinition,
|
|
71
|
-
) {
|
|
72
|
-
return (
|
|
73
|
-
order?.state === OrderState.VALIDATED &&
|
|
74
|
-
contractDefinition &&
|
|
75
|
-
!(order.contract && order.contract.student_signed_on)
|
|
76
|
-
);
|
|
68
|
+
static orderNeedsSignature(order: Order | NestedCourseOrder) {
|
|
69
|
+
return [OrderState.TO_SIGN, OrderState.SIGNING].includes(order.state);
|
|
77
70
|
}
|
|
78
71
|
|
|
79
72
|
/**
|
|
@@ -81,10 +74,26 @@ export class OrderHelper {
|
|
|
81
74
|
*/
|
|
82
75
|
static orderNeedsCounterSignature(order: Order | NestedCourseOrder) {
|
|
83
76
|
return (
|
|
84
|
-
order
|
|
77
|
+
ACTIVE_ORDER_STATES.includes(order.state) &&
|
|
85
78
|
order.contract &&
|
|
86
79
|
order.contract.student_signed_on &&
|
|
87
80
|
!order.contract.organization_signed_on
|
|
88
81
|
);
|
|
89
82
|
}
|
|
83
|
+
|
|
84
|
+
static getFailedInstallment(order: Order) {
|
|
85
|
+
return order.payment_schedule?.find(
|
|
86
|
+
(installment) => installment.state === PaymentScheduleState.REFUSED,
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
static allowEnrollment(order?: Order | NestedCourseOrder | OrderEnrollment) {
|
|
91
|
+
if (!order) return false;
|
|
92
|
+
return ENROLLABLE_ORDER_STATES.includes(order.state);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
static isActive(order?: Order | NestedCourseOrder | OrderEnrollment) {
|
|
96
|
+
if (!order) return false;
|
|
97
|
+
return ACTIVE_ORDER_STATES.includes(order.state);
|
|
98
|
+
}
|
|
90
99
|
}
|
|
@@ -4,45 +4,48 @@ import {
|
|
|
4
4
|
Address,
|
|
5
5
|
Certificate,
|
|
6
6
|
CertificateDefinition,
|
|
7
|
-
|
|
7
|
+
CertificateOrder,
|
|
8
|
+
CertificateProduct,
|
|
9
|
+
Contract,
|
|
10
|
+
ContractDefinition,
|
|
11
|
+
ContractLight,
|
|
8
12
|
CourseLight,
|
|
13
|
+
CourseListItem,
|
|
9
14
|
CourseProduct,
|
|
10
15
|
CourseProductRelation,
|
|
11
16
|
CourseRun,
|
|
17
|
+
CredentialOrder,
|
|
18
|
+
CredentialProduct,
|
|
12
19
|
CreditCard,
|
|
13
20
|
CreditCardBrand,
|
|
21
|
+
DefinitionResourcesProduct,
|
|
14
22
|
Enrollment,
|
|
23
|
+
EnrollmentLight,
|
|
15
24
|
EnrollmentState,
|
|
25
|
+
JoanieFile,
|
|
26
|
+
NestedCertificateOrder,
|
|
27
|
+
NestedCourseOrder,
|
|
28
|
+
NestedCredentialOrder,
|
|
29
|
+
Order,
|
|
30
|
+
OrderEnrollment,
|
|
31
|
+
OrderGroup,
|
|
32
|
+
PaymentInstallment,
|
|
16
33
|
OrderLite,
|
|
17
34
|
OrderState,
|
|
18
35
|
Organization,
|
|
19
36
|
OrganizationLight,
|
|
37
|
+
PaymentScheduleState,
|
|
20
38
|
ProductType,
|
|
21
39
|
TargetCourse,
|
|
22
|
-
JoanieFile,
|
|
23
|
-
Contract,
|
|
24
|
-
OrderEnrollment,
|
|
25
|
-
ContractDefinition,
|
|
26
|
-
NestedCredentialOrder,
|
|
27
|
-
NestedCertificateOrder,
|
|
28
|
-
Order,
|
|
29
|
-
CertificateOrder,
|
|
30
|
-
CredentialOrder,
|
|
31
|
-
CertificateOrderWithPaymentInfo,
|
|
32
|
-
CredentialOrderWithPaymentInfo,
|
|
33
|
-
EnrollmentLight,
|
|
34
|
-
OrderGroup,
|
|
35
|
-
CertificateProduct,
|
|
36
|
-
CredentialProduct,
|
|
37
|
-
NestedCourseOrder,
|
|
38
40
|
UserLight,
|
|
39
|
-
ContractLight,
|
|
40
|
-
DefinitionResourcesProduct,
|
|
41
41
|
} from 'types/Joanie';
|
|
42
42
|
import { Payment, PaymentProviders } from 'components/PaymentInterfaces/types';
|
|
43
43
|
import { CourseStateFactory } from 'utils/test/factories/richie';
|
|
44
44
|
import { FactoryHelper } from 'utils/test/factories/helper';
|
|
45
45
|
import { JoanieUserApiAbilityActions, JoanieUserProfile } from 'types/User';
|
|
46
|
+
import { SaleTunnelContextType, SaleTunnelStep } from 'components/SaleTunnel/GenericSaleTunnel';
|
|
47
|
+
import { SaleTunnelProps } from 'components/SaleTunnel';
|
|
48
|
+
import { noop } from 'utils/index';
|
|
46
49
|
import { factory } from './factories';
|
|
47
50
|
|
|
48
51
|
export const UserLightFactory = factory((): UserLight => {
|
|
@@ -138,7 +141,7 @@ export const ContractFactory = factory((): Contract => {
|
|
|
138
141
|
organization_signatory: null,
|
|
139
142
|
created_on: faker.date.past().toISOString(),
|
|
140
143
|
definition: ContractDefinitionFactory().one(),
|
|
141
|
-
order: NestedCredentialOrderFactory().one(),
|
|
144
|
+
order: NestedCredentialOrderFactory({ state: OrderState.TO_SIGN }).one(),
|
|
142
145
|
};
|
|
143
146
|
});
|
|
144
147
|
|
|
@@ -312,7 +315,7 @@ export const NestedCourseOrderFactory = factory((): NestedCourseOrder => {
|
|
|
312
315
|
owner: UserLightFactory().one(),
|
|
313
316
|
course_id: faker.string.uuid(),
|
|
314
317
|
product_id: faker.string.uuid(),
|
|
315
|
-
state: OrderState.
|
|
318
|
+
state: OrderState.COMPLETED,
|
|
316
319
|
enrollment_id: faker.string.uuid(),
|
|
317
320
|
organization: OrganizationFactory().one(),
|
|
318
321
|
certificate_id: faker.string.uuid(),
|
|
@@ -347,11 +350,21 @@ export const CourseListItemFactory = factory((): CourseListItem => {
|
|
|
347
350
|
};
|
|
348
351
|
});
|
|
349
352
|
|
|
353
|
+
export const PaymentInstallmentFactory = factory((): PaymentInstallment => {
|
|
354
|
+
return {
|
|
355
|
+
id: faker.string.uuid(),
|
|
356
|
+
currency: faker.finance.currencyCode(),
|
|
357
|
+
amount: faker.number.int(),
|
|
358
|
+
due_date: faker.date.future().toISOString(),
|
|
359
|
+
state: PaymentScheduleState.PAID,
|
|
360
|
+
};
|
|
361
|
+
});
|
|
362
|
+
|
|
350
363
|
export const OrderEnrollmentFactory = factory((): OrderEnrollment => {
|
|
351
364
|
return {
|
|
352
365
|
id: faker.string.uuid(),
|
|
353
366
|
product_id: faker.string.uuid(),
|
|
354
|
-
state: OrderState.
|
|
367
|
+
state: OrderState.COMPLETED,
|
|
355
368
|
};
|
|
356
369
|
});
|
|
357
370
|
|
|
@@ -363,19 +376,19 @@ export const OrderLiteFactory = factory((): OrderLite => {
|
|
|
363
376
|
main_invoice_reference: faker.string.uuid(),
|
|
364
377
|
total: faker.number.int(),
|
|
365
378
|
product_id: faker.string.uuid(),
|
|
366
|
-
state: OrderState.
|
|
379
|
+
state: OrderState.COMPLETED,
|
|
367
380
|
};
|
|
368
381
|
});
|
|
369
382
|
|
|
370
383
|
export const NestedCertificateOrderFactory = factory((): NestedCertificateOrder => {
|
|
371
384
|
return {
|
|
372
385
|
id: faker.string.uuid(),
|
|
373
|
-
course:
|
|
386
|
+
course: null,
|
|
374
387
|
enrollment: EnrollmentLightFactory().one(),
|
|
375
388
|
organization: OrganizationFactory().one(),
|
|
376
389
|
product_title: FactoryHelper.unique(faker.lorem.words, { args: [1] }),
|
|
377
390
|
owner_name: faker.internet.userName(),
|
|
378
|
-
state: OrderState.
|
|
391
|
+
state: OrderState.COMPLETED,
|
|
379
392
|
};
|
|
380
393
|
});
|
|
381
394
|
|
|
@@ -383,11 +396,11 @@ export const NestedCredentialOrderFactory = factory((): NestedCredentialOrder =>
|
|
|
383
396
|
return {
|
|
384
397
|
id: faker.string.uuid(),
|
|
385
398
|
course: CourseLightFactory().one(),
|
|
386
|
-
enrollment:
|
|
399
|
+
enrollment: null,
|
|
387
400
|
organization: OrganizationFactory().one(),
|
|
388
401
|
product_title: FactoryHelper.unique(faker.lorem.words, { args: [1] }),
|
|
389
402
|
owner_name: faker.internet.userName(),
|
|
390
|
-
state: OrderState.
|
|
403
|
+
state: OrderState.COMPLETED,
|
|
391
404
|
};
|
|
392
405
|
});
|
|
393
406
|
|
|
@@ -399,7 +412,7 @@ const AbstractOrderFactory = factory((): Order => {
|
|
|
399
412
|
total: faker.number.int(),
|
|
400
413
|
total_currency: faker.finance.currencyCode(),
|
|
401
414
|
main_invoice_reference: faker.string.uuid(),
|
|
402
|
-
state: OrderState.
|
|
415
|
+
state: OrderState.COMPLETED,
|
|
403
416
|
product_id: faker.string.uuid(),
|
|
404
417
|
target_courses: TargetCourseFactory().many(5),
|
|
405
418
|
target_enrollments: [],
|
|
@@ -410,55 +423,22 @@ const AbstractOrderFactory = factory((): Order => {
|
|
|
410
423
|
};
|
|
411
424
|
});
|
|
412
425
|
|
|
413
|
-
export const CredentialOrderFactory = factory(
|
|
414
|
-
|
|
426
|
+
export const CredentialOrderFactory = factory(
|
|
427
|
+
(): CredentialOrder => ({
|
|
415
428
|
...AbstractOrderFactory().one(),
|
|
416
429
|
course: CourseLightFactory().one(),
|
|
417
430
|
enrollment: null,
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
});
|
|
421
|
-
|
|
422
|
-
export const CredentialOrderWithPaymentFactory = factory((): CredentialOrderWithPaymentInfo => {
|
|
423
|
-
return {
|
|
424
|
-
...CredentialOrderFactory().one(),
|
|
425
|
-
payment_info: PaymentFactory().one(),
|
|
426
|
-
};
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
export const CredentialOrderWithOneClickPaymentFactory = factory(
|
|
430
|
-
(): CredentialOrderWithPaymentInfo => {
|
|
431
|
-
return {
|
|
432
|
-
...CredentialOrderFactory().one(),
|
|
433
|
-
payment_info: PaymentFactory().one(),
|
|
434
|
-
};
|
|
435
|
-
},
|
|
431
|
+
payment_schedule: PaymentInstallmentFactory().many(3),
|
|
432
|
+
}),
|
|
436
433
|
);
|
|
437
434
|
|
|
438
|
-
export const CertificateOrderFactory = factory(
|
|
439
|
-
|
|
435
|
+
export const CertificateOrderFactory = factory(
|
|
436
|
+
(): CertificateOrder => ({
|
|
440
437
|
...AbstractOrderFactory().one(),
|
|
441
438
|
course: null,
|
|
442
439
|
target_courses: [],
|
|
443
440
|
enrollment: EnrollmentLightFactory().one(),
|
|
444
|
-
}
|
|
445
|
-
return order;
|
|
446
|
-
});
|
|
447
|
-
|
|
448
|
-
export const CertificateOrderWithPaymentFactory = factory((): CertificateOrderWithPaymentInfo => {
|
|
449
|
-
return {
|
|
450
|
-
...CertificateOrderFactory().one(),
|
|
451
|
-
payment_info: PaymentFactory().one(),
|
|
452
|
-
};
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
export const CertificateOrderWithOneClickPaymentFactory = factory(
|
|
456
|
-
(): CertificateOrderWithPaymentInfo => {
|
|
457
|
-
return {
|
|
458
|
-
...CertificateOrderFactory().one(),
|
|
459
|
-
payment_info: PaymentFactory().one(),
|
|
460
|
-
};
|
|
461
|
-
},
|
|
441
|
+
}),
|
|
462
442
|
);
|
|
463
443
|
|
|
464
444
|
export const AddressFactory = factory((): Address => {
|
|
@@ -494,3 +474,21 @@ export const PaymentFactory = factory((): Payment => {
|
|
|
494
474
|
url: faker.internet.url(),
|
|
495
475
|
};
|
|
496
476
|
});
|
|
477
|
+
|
|
478
|
+
export const SaleTunnelContextFactory = factory(
|
|
479
|
+
(): SaleTunnelContextType => ({
|
|
480
|
+
webAnalyticsEventKey: 'eventKey',
|
|
481
|
+
order: CredentialOrderFactory().one(),
|
|
482
|
+
product: ProductFactory().one(),
|
|
483
|
+
props: {} as SaleTunnelProps,
|
|
484
|
+
billingAddress: undefined,
|
|
485
|
+
setBillingAddress: noop,
|
|
486
|
+
setCreditCard: noop,
|
|
487
|
+
onPaymentSuccess: noop,
|
|
488
|
+
step: SaleTunnelStep.IDLE,
|
|
489
|
+
registerSubmitCallback: noop,
|
|
490
|
+
unregisterSubmitCallback: noop,
|
|
491
|
+
runSubmitCallbacks: () => new Promise((resolve) => resolve()),
|
|
492
|
+
nextStep: noop,
|
|
493
|
+
}),
|
|
494
|
+
);
|