richie-education 2.28.2-dev39 → 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 +12 -16
- 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/CreditCardSelector/index.spec.tsx +7 -7
- package/js/components/CreditCardSelector/index.tsx +2 -2
- 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/PurchaseButton/index.spec.tsx +69 -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 +75 -41
- package/js/components/SaleTunnel/SaleTunnelInformation/index.tsx +0 -30
- 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.tsx +5 -0
- package/js/components/SaleTunnel/SubscriptionButton/_styles.scss +7 -0
- package/js/components/SaleTunnel/SubscriptionButton/index.tsx +201 -0
- package/js/components/SaleTunnel/_styles.scss +10 -1
- package/js/components/SaleTunnel/hooks/useTerms.tsx +0 -77
- package/js/components/SaleTunnel/index.credential.spec.tsx +12 -21
- package/js/components/SaleTunnel/index.full-process.spec.tsx +110 -48
- package/js/components/SaleTunnel/index.spec.tsx +330 -779
- 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/useProductOrder/index.spec.tsx +77 -60
- package/js/hooks/useProductOrder/index.tsx +2 -2
- package/js/hooks/useResources/useResourcesRoot.ts +1 -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 +49 -34
- package/js/utils/OrderHelper/index.ts +38 -42
- package/js/utils/test/factories/joanie.ts +36 -51
- 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 +7 -6
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +9 -10
- 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/index.tsx +28 -8
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderPaymentRetryModal/index.tsx +2 -5
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateLearnerMessage/index.spec.tsx +18 -71
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateLearnerMessage/index.tsx +34 -35
- package/js/widgets/Dashboard/components/DashboardItem/Order/OrderStateMessage/index.tsx +27 -24
- 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 +1 -1
- package/scss/components/_index.scss +2 -1
- package/js/components/PaymentButton/_styles.scss +0 -27
- package/js/components/SaleTunnel/GenericPaymentButton/index.tsx +0 -333
- package/js/components/SaleTunnel/SaleTunnelNotValidated/index.tsx +0 -70
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/ProductSignatureHeader/index.tsx +0 -41
|
@@ -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,17 +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
269
|
PENDING_PAYMENT = 'pending_payment',
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
export const
|
|
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
|
+
];
|
|
273
298
|
|
|
274
299
|
export interface Order {
|
|
275
300
|
id: string;
|
|
@@ -290,6 +315,7 @@ export interface Order {
|
|
|
290
315
|
organization: Organization;
|
|
291
316
|
order_group_id?: OrderGroup['id'];
|
|
292
317
|
payment_schedule?: PaymentSchedule;
|
|
318
|
+
credit_card_id?: CreditCard['id'];
|
|
293
319
|
}
|
|
294
320
|
|
|
295
321
|
export interface CredentialOrder extends Order {
|
|
@@ -297,20 +323,12 @@ export interface CredentialOrder extends Order {
|
|
|
297
323
|
enrollment: null;
|
|
298
324
|
}
|
|
299
325
|
|
|
300
|
-
export interface CredentialOrderWithPaymentInfo extends CredentialOrder {
|
|
301
|
-
payment_info: Payment;
|
|
302
|
-
}
|
|
303
|
-
|
|
304
326
|
export interface CertificateOrder extends Order {
|
|
305
327
|
course: null;
|
|
306
328
|
enrollment: EnrollmentLight;
|
|
307
329
|
target_courses: never[];
|
|
308
330
|
}
|
|
309
331
|
|
|
310
|
-
export interface CertificateOrderWithPaymentInfo extends CertificateOrder {
|
|
311
|
-
payment_info: Payment;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
332
|
export type OrderLite = Pick<
|
|
315
333
|
Order,
|
|
316
334
|
| 'id'
|
|
@@ -329,9 +347,11 @@ export interface AbstractNestedOrder {
|
|
|
329
347
|
product_title: string;
|
|
330
348
|
owner_name: string;
|
|
331
349
|
state: OrderState;
|
|
350
|
+
course: Nullable<CourseLight>;
|
|
351
|
+
enrollment: Nullable<EnrollmentLight>;
|
|
332
352
|
}
|
|
333
353
|
export interface NestedCertificateOrder extends AbstractNestedOrder {
|
|
334
|
-
course:
|
|
354
|
+
course: null;
|
|
335
355
|
enrollment: EnrollmentLight;
|
|
336
356
|
}
|
|
337
357
|
export const isNestedCredentialOrder = (
|
|
@@ -342,7 +362,7 @@ export const isNestedCredentialOrder = (
|
|
|
342
362
|
|
|
343
363
|
export interface NestedCredentialOrder extends AbstractNestedOrder {
|
|
344
364
|
course: CourseLight;
|
|
345
|
-
enrollment:
|
|
365
|
+
enrollment: null;
|
|
346
366
|
}
|
|
347
367
|
|
|
348
368
|
export type OrderEnrollment = Pick<Order, 'id' | 'state' | 'product_id' | 'certificate_id'>;
|
|
@@ -376,10 +396,10 @@ export interface OrderGroup {
|
|
|
376
396
|
}
|
|
377
397
|
|
|
378
398
|
export enum CreditCardBrand {
|
|
379
|
-
MASTERCARD = '
|
|
380
|
-
MAESTRO = '
|
|
381
|
-
VISA = '
|
|
382
|
-
CB = '
|
|
399
|
+
MASTERCARD = 'mastercard',
|
|
400
|
+
MAESTRO = 'maestro',
|
|
401
|
+
VISA = 'visa',
|
|
402
|
+
CB = 'cb',
|
|
383
403
|
}
|
|
384
404
|
|
|
385
405
|
// Credit Card
|
|
@@ -444,7 +464,7 @@ export interface AddressCreationPayload extends Omit<Address, 'id' | 'is_main'>
|
|
|
444
464
|
interface AbstractOrderProductCreationPayload {
|
|
445
465
|
product_id: Product['id'];
|
|
446
466
|
order_group_id?: OrderGroup['id'];
|
|
447
|
-
|
|
467
|
+
billing_address: Omit<Address, 'id' | 'is_main'>;
|
|
448
468
|
}
|
|
449
469
|
|
|
450
470
|
interface OrderCertificateCreationPayload extends AbstractOrderProductCreationPayload {
|
|
@@ -460,15 +480,9 @@ export type OrderSubmitInstallmentPayment = {
|
|
|
460
480
|
credit_card_id?: string;
|
|
461
481
|
};
|
|
462
482
|
|
|
463
|
-
interface
|
|
464
|
-
id: Order['id'];
|
|
465
|
-
payment_id?: string;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
interface OrderSubmitPayload {
|
|
483
|
+
interface OrderSetPaymentMethodPayload {
|
|
469
484
|
id: Order['id'];
|
|
470
|
-
|
|
471
|
-
credit_card_id?: CreditCard['id'];
|
|
485
|
+
credit_card_id: CreditCard['id'];
|
|
472
486
|
}
|
|
473
487
|
|
|
474
488
|
export interface PaginatedResourceQuery extends ResourcesQuery {
|
|
@@ -563,14 +577,15 @@ interface APIUser {
|
|
|
563
577
|
update(payload: Address): Promise<Address>;
|
|
564
578
|
};
|
|
565
579
|
creditCards: {
|
|
566
|
-
create(payload: Omit<CreditCard, 'id'>): Promise<CreditCard>;
|
|
567
580
|
delete(id: CreditCard['id']): Promise<void>;
|
|
581
|
+
get(): Promise<PaginatedResponse<CreditCard>>;
|
|
568
582
|
get(filters?: ResourcesQuery): Promise<CreditCard>;
|
|
569
583
|
get(): Promise<CreditCard[]>;
|
|
570
584
|
update(payload: CreditCard): Promise<CreditCard>;
|
|
585
|
+
tokenize(): Promise<Payment>;
|
|
571
586
|
};
|
|
572
587
|
orders: {
|
|
573
|
-
|
|
588
|
+
cancel(id: Order['id']): Promise<void>;
|
|
574
589
|
create(payload: OrderCreationPayload): Promise<CredentialOrder | CertificateOrder>;
|
|
575
590
|
get<Filters extends OrderResourcesQuery = OrderResourcesQuery>(
|
|
576
591
|
filters?: Filters,
|
|
@@ -580,12 +595,12 @@ interface APIUser {
|
|
|
580
595
|
invoice: {
|
|
581
596
|
download(payload: { order_id: Order['id']; invoice_reference: string }): Promise<File>;
|
|
582
597
|
};
|
|
583
|
-
submit(payload: OrderSubmitPayload): Promise<OrderPaymentInfo>;
|
|
584
598
|
submit_for_signature(id: string): Promise<ContractInvitationLinkResponse>;
|
|
585
599
|
submit_installment_payment(
|
|
586
600
|
id: string,
|
|
587
601
|
payload?: OrderSubmitInstallmentPayment,
|
|
588
602
|
): Promise<Payment>;
|
|
603
|
+
set_payment_method(payload: OrderSetPaymentMethodPayload): Promise<void>;
|
|
589
604
|
};
|
|
590
605
|
certificates: {
|
|
591
606
|
download(id: string): Promise<File>;
|
|
@@ -1,56 +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
|
-
ContractDefinition,
|
|
7
|
-
NestedCourseOrder,
|
|
8
8
|
PaymentScheduleState,
|
|
9
9
|
} from 'types/Joanie';
|
|
10
10
|
|
|
11
11
|
export enum OrderStatus {
|
|
12
|
-
|
|
13
|
-
SUBMITTED = 'submitted',
|
|
14
|
-
PENDING = 'pending',
|
|
12
|
+
ASSIGNED = 'assigned',
|
|
15
13
|
CANCELED = 'canceled',
|
|
16
|
-
WAITING_SIGNATURE = 'waiting_signature',
|
|
17
|
-
WAITING_COUNTER_SIGNATURE = 'waiting_counter_signature',
|
|
18
14
|
COMPLETED = 'completed',
|
|
19
|
-
|
|
15
|
+
DRAFT = 'draft',
|
|
16
|
+
FAILED_PAYMENT = 'failed_payment',
|
|
20
17
|
NO_PAYMENT = 'no_payment',
|
|
18
|
+
PASSED = 'passed',
|
|
19
|
+
PENDING = 'pending',
|
|
21
20
|
PENDING_PAYMENT = 'pending_payment',
|
|
22
|
-
|
|
21
|
+
WAITING_COUNTER_SIGNATURE = 'waiting_counter_signature',
|
|
22
|
+
WAITING_PAYMENT_METHOD = 'waiting_payment_method',
|
|
23
|
+
WAITING_SIGNATURE = 'waiting_signature',
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Helper class for orders
|
|
27
28
|
*/
|
|
28
29
|
export class OrderHelper {
|
|
29
|
-
static getState(order: Order | NestedCourseOrder
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
if (OrderHelper.orderNeedsCounterSignature(order)) {
|
|
37
|
-
return OrderStatus.WAITING_COUNTER_SIGNATURE;
|
|
38
|
-
}
|
|
39
|
-
if (certificateId) {
|
|
40
|
-
return OrderStatus.COMPLETED;
|
|
41
|
-
} else {
|
|
42
|
-
return OrderStatus.ON_GOING;
|
|
43
|
-
}
|
|
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;
|
|
44
36
|
}
|
|
45
37
|
|
|
46
38
|
const orderStatusMap = {
|
|
47
|
-
[OrderState.
|
|
48
|
-
[OrderState.SUBMITTED]: OrderStatus.SUBMITTED,
|
|
49
|
-
[OrderState.PENDING]: OrderStatus.PENDING,
|
|
39
|
+
[OrderState.ASSIGNED]: OrderStatus.ASSIGNED,
|
|
50
40
|
[OrderState.CANCELED]: OrderStatus.CANCELED,
|
|
41
|
+
[OrderState.COMPLETED]: OrderStatus.COMPLETED,
|
|
42
|
+
[OrderState.DRAFT]: OrderStatus.DRAFT,
|
|
43
|
+
[OrderState.FAILED_PAYMENT]: OrderStatus.FAILED_PAYMENT,
|
|
51
44
|
[OrderState.NO_PAYMENT]: OrderStatus.NO_PAYMENT,
|
|
45
|
+
[OrderState.PENDING]: OrderStatus.PENDING,
|
|
52
46
|
[OrderState.PENDING_PAYMENT]: OrderStatus.PENDING_PAYMENT,
|
|
53
|
-
[OrderState.
|
|
47
|
+
[OrderState.SIGNING]: OrderStatus.WAITING_SIGNATURE,
|
|
48
|
+
[OrderState.TO_SAVE_PAYMENT_METHOD]: OrderStatus.WAITING_PAYMENT_METHOD,
|
|
49
|
+
[OrderState.TO_SIGN]: OrderStatus.WAITING_SIGNATURE,
|
|
54
50
|
};
|
|
55
51
|
|
|
56
52
|
if (order.state in orderStatusMap) {
|
|
@@ -69,18 +65,8 @@ export class OrderHelper {
|
|
|
69
65
|
return orders.find(filter);
|
|
70
66
|
}
|
|
71
67
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
*/
|
|
75
|
-
static orderNeedsSignature(
|
|
76
|
-
order: Order | NestedCourseOrder,
|
|
77
|
-
contractDefinition?: ContractDefinition,
|
|
78
|
-
) {
|
|
79
|
-
return (
|
|
80
|
-
order?.state === OrderState.VALIDATED &&
|
|
81
|
-
contractDefinition &&
|
|
82
|
-
!(order.contract && order.contract.student_signed_on)
|
|
83
|
-
);
|
|
68
|
+
static orderNeedsSignature(order: Order | NestedCourseOrder) {
|
|
69
|
+
return [OrderState.TO_SIGN, OrderState.SIGNING].includes(order.state);
|
|
84
70
|
}
|
|
85
71
|
|
|
86
72
|
/**
|
|
@@ -88,7 +74,7 @@ export class OrderHelper {
|
|
|
88
74
|
*/
|
|
89
75
|
static orderNeedsCounterSignature(order: Order | NestedCourseOrder) {
|
|
90
76
|
return (
|
|
91
|
-
order
|
|
77
|
+
ACTIVE_ORDER_STATES.includes(order.state) &&
|
|
92
78
|
order.contract &&
|
|
93
79
|
order.contract.student_signed_on &&
|
|
94
80
|
!order.contract.organization_signed_on
|
|
@@ -100,4 +86,14 @@ export class OrderHelper {
|
|
|
100
86
|
(installment) => installment.state === PaymentScheduleState.REFUSED,
|
|
101
87
|
);
|
|
102
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
|
+
}
|
|
103
99
|
}
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
Certificate,
|
|
6
6
|
CertificateDefinition,
|
|
7
7
|
CertificateOrder,
|
|
8
|
-
CertificateOrderWithPaymentInfo,
|
|
9
8
|
CertificateProduct,
|
|
10
9
|
Contract,
|
|
11
10
|
ContractDefinition,
|
|
@@ -16,7 +15,6 @@ import {
|
|
|
16
15
|
CourseProductRelation,
|
|
17
16
|
CourseRun,
|
|
18
17
|
CredentialOrder,
|
|
19
|
-
CredentialOrderWithPaymentInfo,
|
|
20
18
|
CredentialProduct,
|
|
21
19
|
CreditCard,
|
|
22
20
|
CreditCardBrand,
|
|
@@ -45,6 +43,9 @@ import { Payment, PaymentProviders } from 'components/PaymentInterfaces/types';
|
|
|
45
43
|
import { CourseStateFactory } from 'utils/test/factories/richie';
|
|
46
44
|
import { FactoryHelper } from 'utils/test/factories/helper';
|
|
47
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';
|
|
48
49
|
import { factory } from './factories';
|
|
49
50
|
|
|
50
51
|
export const UserLightFactory = factory((): UserLight => {
|
|
@@ -140,7 +141,7 @@ export const ContractFactory = factory((): Contract => {
|
|
|
140
141
|
organization_signatory: null,
|
|
141
142
|
created_on: faker.date.past().toISOString(),
|
|
142
143
|
definition: ContractDefinitionFactory().one(),
|
|
143
|
-
order: NestedCredentialOrderFactory().one(),
|
|
144
|
+
order: NestedCredentialOrderFactory({ state: OrderState.TO_SIGN }).one(),
|
|
144
145
|
};
|
|
145
146
|
});
|
|
146
147
|
|
|
@@ -314,7 +315,7 @@ export const NestedCourseOrderFactory = factory((): NestedCourseOrder => {
|
|
|
314
315
|
owner: UserLightFactory().one(),
|
|
315
316
|
course_id: faker.string.uuid(),
|
|
316
317
|
product_id: faker.string.uuid(),
|
|
317
|
-
state: OrderState.
|
|
318
|
+
state: OrderState.COMPLETED,
|
|
318
319
|
enrollment_id: faker.string.uuid(),
|
|
319
320
|
organization: OrganizationFactory().one(),
|
|
320
321
|
certificate_id: faker.string.uuid(),
|
|
@@ -363,7 +364,7 @@ export const OrderEnrollmentFactory = factory((): OrderEnrollment => {
|
|
|
363
364
|
return {
|
|
364
365
|
id: faker.string.uuid(),
|
|
365
366
|
product_id: faker.string.uuid(),
|
|
366
|
-
state: OrderState.
|
|
367
|
+
state: OrderState.COMPLETED,
|
|
367
368
|
};
|
|
368
369
|
});
|
|
369
370
|
|
|
@@ -375,19 +376,19 @@ export const OrderLiteFactory = factory((): OrderLite => {
|
|
|
375
376
|
main_invoice_reference: faker.string.uuid(),
|
|
376
377
|
total: faker.number.int(),
|
|
377
378
|
product_id: faker.string.uuid(),
|
|
378
|
-
state: OrderState.
|
|
379
|
+
state: OrderState.COMPLETED,
|
|
379
380
|
};
|
|
380
381
|
});
|
|
381
382
|
|
|
382
383
|
export const NestedCertificateOrderFactory = factory((): NestedCertificateOrder => {
|
|
383
384
|
return {
|
|
384
385
|
id: faker.string.uuid(),
|
|
385
|
-
course:
|
|
386
|
+
course: null,
|
|
386
387
|
enrollment: EnrollmentLightFactory().one(),
|
|
387
388
|
organization: OrganizationFactory().one(),
|
|
388
389
|
product_title: FactoryHelper.unique(faker.lorem.words, { args: [1] }),
|
|
389
390
|
owner_name: faker.internet.userName(),
|
|
390
|
-
state: OrderState.
|
|
391
|
+
state: OrderState.COMPLETED,
|
|
391
392
|
};
|
|
392
393
|
});
|
|
393
394
|
|
|
@@ -395,11 +396,11 @@ export const NestedCredentialOrderFactory = factory((): NestedCredentialOrder =>
|
|
|
395
396
|
return {
|
|
396
397
|
id: faker.string.uuid(),
|
|
397
398
|
course: CourseLightFactory().one(),
|
|
398
|
-
enrollment:
|
|
399
|
+
enrollment: null,
|
|
399
400
|
organization: OrganizationFactory().one(),
|
|
400
401
|
product_title: FactoryHelper.unique(faker.lorem.words, { args: [1] }),
|
|
401
402
|
owner_name: faker.internet.userName(),
|
|
402
|
-
state: OrderState.
|
|
403
|
+
state: OrderState.COMPLETED,
|
|
403
404
|
};
|
|
404
405
|
});
|
|
405
406
|
|
|
@@ -411,7 +412,7 @@ const AbstractOrderFactory = factory((): Order => {
|
|
|
411
412
|
total: faker.number.int(),
|
|
412
413
|
total_currency: faker.finance.currencyCode(),
|
|
413
414
|
main_invoice_reference: faker.string.uuid(),
|
|
414
|
-
state: OrderState.
|
|
415
|
+
state: OrderState.COMPLETED,
|
|
415
416
|
product_id: faker.string.uuid(),
|
|
416
417
|
target_courses: TargetCourseFactory().many(5),
|
|
417
418
|
target_enrollments: [],
|
|
@@ -422,56 +423,22 @@ const AbstractOrderFactory = factory((): Order => {
|
|
|
422
423
|
};
|
|
423
424
|
});
|
|
424
425
|
|
|
425
|
-
export const CredentialOrderFactory = factory(
|
|
426
|
-
|
|
426
|
+
export const CredentialOrderFactory = factory(
|
|
427
|
+
(): CredentialOrder => ({
|
|
427
428
|
...AbstractOrderFactory().one(),
|
|
428
429
|
course: CourseLightFactory().one(),
|
|
429
430
|
enrollment: null,
|
|
430
431
|
payment_schedule: PaymentInstallmentFactory().many(3),
|
|
431
|
-
}
|
|
432
|
-
return order;
|
|
433
|
-
});
|
|
434
|
-
|
|
435
|
-
export const CredentialOrderWithPaymentFactory = factory((): CredentialOrderWithPaymentInfo => {
|
|
436
|
-
return {
|
|
437
|
-
...CredentialOrderFactory().one(),
|
|
438
|
-
payment_info: PaymentFactory().one(),
|
|
439
|
-
};
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
export const CredentialOrderWithOneClickPaymentFactory = factory(
|
|
443
|
-
(): CredentialOrderWithPaymentInfo => {
|
|
444
|
-
return {
|
|
445
|
-
...CredentialOrderFactory().one(),
|
|
446
|
-
payment_info: PaymentFactory().one(),
|
|
447
|
-
};
|
|
448
|
-
},
|
|
432
|
+
}),
|
|
449
433
|
);
|
|
450
434
|
|
|
451
|
-
export const CertificateOrderFactory = factory(
|
|
452
|
-
|
|
435
|
+
export const CertificateOrderFactory = factory(
|
|
436
|
+
(): CertificateOrder => ({
|
|
453
437
|
...AbstractOrderFactory().one(),
|
|
454
438
|
course: null,
|
|
455
439
|
target_courses: [],
|
|
456
440
|
enrollment: EnrollmentLightFactory().one(),
|
|
457
|
-
}
|
|
458
|
-
return order;
|
|
459
|
-
});
|
|
460
|
-
|
|
461
|
-
export const CertificateOrderWithPaymentFactory = factory((): CertificateOrderWithPaymentInfo => {
|
|
462
|
-
return {
|
|
463
|
-
...CertificateOrderFactory().one(),
|
|
464
|
-
payment_info: PaymentFactory().one(),
|
|
465
|
-
};
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
export const CertificateOrderWithOneClickPaymentFactory = factory(
|
|
469
|
-
(): CertificateOrderWithPaymentInfo => {
|
|
470
|
-
return {
|
|
471
|
-
...CertificateOrderFactory().one(),
|
|
472
|
-
payment_info: PaymentFactory().one(),
|
|
473
|
-
};
|
|
474
|
-
},
|
|
441
|
+
}),
|
|
475
442
|
);
|
|
476
443
|
|
|
477
444
|
export const AddressFactory = factory((): Address => {
|
|
@@ -507,3 +474,21 @@ export const PaymentFactory = factory((): Payment => {
|
|
|
507
474
|
url: faker.internet.url(),
|
|
508
475
|
};
|
|
509
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
|
+
);
|
|
@@ -9,7 +9,6 @@ import {
|
|
|
9
9
|
CourseRun,
|
|
10
10
|
CredentialOrder,
|
|
11
11
|
Enrollment,
|
|
12
|
-
Product,
|
|
13
12
|
} from 'types/Joanie';
|
|
14
13
|
import { Spinner } from 'components/Spinner';
|
|
15
14
|
import Banner, { BannerType } from 'components/Banner';
|
|
@@ -68,10 +67,11 @@ const messages = defineMessages({
|
|
|
68
67
|
description: 'Text displayed when course runs list is loading',
|
|
69
68
|
id: 'components.DashboardItemCourseEnrollingRun.courseRunsLoading',
|
|
70
69
|
},
|
|
71
|
-
|
|
72
|
-
id: 'components.DashboardItemCourseEnrollingRun.
|
|
73
|
-
description:
|
|
74
|
-
|
|
70
|
+
cannotEnroll: {
|
|
71
|
+
id: 'components.DashboardItemCourseEnrollingRun.cannotEnroll',
|
|
72
|
+
description:
|
|
73
|
+
'Message displayed as disabled button title when the order state does not allow enrollment.',
|
|
74
|
+
defaultMessage: 'You cannot enroll yet to this training.',
|
|
75
75
|
},
|
|
76
76
|
});
|
|
77
77
|
|
|
@@ -81,7 +81,6 @@ interface DashboardItemCourseEnrollingProps {
|
|
|
81
81
|
course: AbstractCourse;
|
|
82
82
|
activeEnrollment?: Enrollment;
|
|
83
83
|
order?: CredentialOrder;
|
|
84
|
-
product?: Product;
|
|
85
84
|
writable: boolean;
|
|
86
85
|
hideEnrollButtons?: boolean;
|
|
87
86
|
icon?: boolean;
|
|
@@ -93,7 +92,6 @@ export const DashboardItemCourseEnrolling = ({
|
|
|
93
92
|
activeEnrollment,
|
|
94
93
|
writable,
|
|
95
94
|
order,
|
|
96
|
-
product,
|
|
97
95
|
icon = false,
|
|
98
96
|
notEnrolledUrl = '#',
|
|
99
97
|
hideEnrollButtons,
|
|
@@ -121,7 +119,6 @@ export const DashboardItemCourseEnrolling = ({
|
|
|
121
119
|
course={course}
|
|
122
120
|
enrollments={CoursesHelper.findCourseEnrollmentsInOrder(course, order)}
|
|
123
121
|
order={order}
|
|
124
|
-
product={product}
|
|
125
122
|
/>
|
|
126
123
|
)}
|
|
127
124
|
</div>
|
|
@@ -132,14 +129,12 @@ interface DashboardItemCourseEnrollingRunsProps {
|
|
|
132
129
|
course: AbstractCourse;
|
|
133
130
|
enrollments: Enrollment[];
|
|
134
131
|
order?: CredentialOrder;
|
|
135
|
-
product?: Product;
|
|
136
132
|
}
|
|
137
133
|
|
|
138
134
|
const DashboardItemCourseEnrollingRuns = ({
|
|
139
135
|
course,
|
|
140
136
|
enrollments,
|
|
141
137
|
order,
|
|
142
|
-
product,
|
|
143
138
|
}: DashboardItemCourseEnrollingRunsProps) => {
|
|
144
139
|
const { enroll, isLoading, error } = useEnroll(enrollments, order);
|
|
145
140
|
|
|
@@ -176,7 +171,6 @@ const DashboardItemCourseEnrollingRuns = ({
|
|
|
176
171
|
selected={data.selected}
|
|
177
172
|
enroll={() => enroll(data.courseRun)}
|
|
178
173
|
order={order}
|
|
179
|
-
product={product}
|
|
180
174
|
/>
|
|
181
175
|
))}
|
|
182
176
|
{isLoading && (
|
|
@@ -200,7 +194,6 @@ interface DashboardItemCourseEnrollingRunProps {
|
|
|
200
194
|
selected: boolean;
|
|
201
195
|
enroll: () => void;
|
|
202
196
|
order?: CredentialOrder | CertificateOrder;
|
|
203
|
-
product?: Product;
|
|
204
197
|
}
|
|
205
198
|
|
|
206
199
|
export const DashboardItemCourseEnrollingRun = ({
|
|
@@ -208,14 +201,11 @@ export const DashboardItemCourseEnrollingRun = ({
|
|
|
208
201
|
selected,
|
|
209
202
|
enroll,
|
|
210
203
|
order,
|
|
211
|
-
product,
|
|
212
204
|
}: DashboardItemCourseEnrollingRunProps) => {
|
|
213
205
|
const intl = useIntl();
|
|
214
206
|
const formatDate = useDateFormat();
|
|
215
207
|
const courseRunPeriodMessage = useCourseRunPeriodMessage(courseRun, selected);
|
|
216
|
-
const
|
|
217
|
-
? OrderHelper.orderNeedsSignature(order, product?.contract_definition)
|
|
218
|
-
: false;
|
|
208
|
+
const canEnroll = OrderHelper.allowEnrollment(order);
|
|
219
209
|
const isOpenedForEnrollment = useMemo(
|
|
220
210
|
() => courseRun.state.priority < Priority.FUTURE_NOT_YET_OPEN,
|
|
221
211
|
[courseRun],
|
|
@@ -266,11 +256,11 @@ export const DashboardItemCourseEnrollingRun = ({
|
|
|
266
256
|
) : (
|
|
267
257
|
<div>
|
|
268
258
|
<Button
|
|
269
|
-
disabled={!isOpenedForEnrollment ||
|
|
259
|
+
disabled={!isOpenedForEnrollment || !canEnroll}
|
|
270
260
|
color="tertiary"
|
|
271
261
|
size="small"
|
|
272
262
|
onClick={enroll}
|
|
273
|
-
title={
|
|
263
|
+
title={!canEnroll ? intl.formatMessage(messages.cannotEnroll) : ''}
|
|
274
264
|
>
|
|
275
265
|
<FormattedMessage {...messages.enrollRun} />
|
|
276
266
|
</Button>
|