richie-education 2.28.1 → 2.28.2-dev23
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/AddressesManagement/AddressForm/validationSchema.spec.ts +2 -2
- package/js/components/PaymentInterfaces/LyraPopIn.tsx +10 -3
- package/js/components/PaymentInterfaces/types.ts +1 -1
- package/js/components/PurchaseButton/index.spec.tsx +9 -9
- package/js/components/PurchaseButton/index.tsx +2 -1
- package/js/components/SaleTunnel/GenericPaymentButton/index.tsx +13 -3
- package/js/components/SaleTunnel/hooks/useTerms.tsx +2 -2
- package/js/components/SaleTunnel/index.credential.spec.tsx +2 -2
- package/js/components/SaleTunnel/index.full-process.spec.tsx +11 -4
- package/js/components/SaleTunnel/index.spec.tsx +2 -2
- package/js/components/SaleTunnel/index.stories.tsx +3 -2
- package/js/components/SaleTunnel/index.tsx +2 -2
- package/js/types/commonDataProps.ts +0 -1
- package/js/types/index.ts +6 -0
- package/js/utils/CourseRunHelper/index.spec.ts +35 -0
- package/js/utils/CourseRunHelper/index.ts +13 -0
- package/js/utils/react-query/useSessionQuery/index.ts +1 -1
- package/js/utils/test/factories/richie.ts +16 -2
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/CourseProductItemFooter/index.tsx +3 -2
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +32 -30
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +5 -6
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +3 -2
- package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.login.spec.tsx +5 -3
- package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.logout.spec.tsx +5 -3
- package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.tsx +2 -2
- package/js/widgets/SyllabusCourseRunsList/components/SyllabusAsideList/index.tsx +11 -4
- package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRun/index.tsx +20 -9
- package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRunCompacted/index.tsx +111 -0
- package/js/widgets/SyllabusCourseRunsList/index.spec.tsx +311 -15
- package/js/widgets/SyllabusCourseRunsList/index.tsx +24 -8
- package/package.json +44 -44
- package/tsconfig.json +1 -1
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { getByText, screen, waitFor } from '@testing-library/react';
|
|
2
2
|
import fetchMock from 'fetch-mock';
|
|
3
3
|
import queryString from 'query-string';
|
|
4
|
-
import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
|
|
5
4
|
import {
|
|
6
|
-
|
|
5
|
+
RichieContextFactory as mockRichieContextFactory,
|
|
6
|
+
PacedCourseFactory,
|
|
7
|
+
} from 'utils/test/factories/richie';
|
|
8
|
+
import {
|
|
7
9
|
CourseProductRelationFactory,
|
|
8
10
|
EnrollmentFactory,
|
|
9
11
|
CredentialOrderFactory,
|
|
@@ -81,7 +83,7 @@ describe('CourseProductItem', () => {
|
|
|
81
83
|
|
|
82
84
|
render(
|
|
83
85
|
<CourseProductItem
|
|
84
|
-
course={
|
|
86
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
85
87
|
productId={product.id}
|
|
86
88
|
/>,
|
|
87
89
|
{ queryOptions: { client: createTestQueryClient({ user: null }) } },
|
|
@@ -103,7 +105,7 @@ describe('CourseProductItem', () => {
|
|
|
103
105
|
|
|
104
106
|
render(
|
|
105
107
|
<CourseProductItem
|
|
106
|
-
course={
|
|
108
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
107
109
|
productId={product.id}
|
|
108
110
|
/>,
|
|
109
111
|
{ queryOptions: { client: createTestQueryClient({ user: null }) } },
|
|
@@ -162,7 +164,7 @@ describe('CourseProductItem', () => {
|
|
|
162
164
|
render(
|
|
163
165
|
<CourseProductItem
|
|
164
166
|
productId={product.id}
|
|
165
|
-
course={
|
|
167
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
166
168
|
/>,
|
|
167
169
|
{ queryOptions: { client: createTestQueryClient({ user: null }) } },
|
|
168
170
|
);
|
|
@@ -183,7 +185,7 @@ describe('CourseProductItem', () => {
|
|
|
183
185
|
|
|
184
186
|
const { container } = render(
|
|
185
187
|
<CourseProductItem
|
|
186
|
-
course={
|
|
188
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
187
189
|
productId={relation.product.id}
|
|
188
190
|
compact
|
|
189
191
|
/>,
|
|
@@ -234,7 +236,7 @@ describe('CourseProductItem', () => {
|
|
|
234
236
|
const { product } = relation;
|
|
235
237
|
const order = CredentialOrderFactory({
|
|
236
238
|
product_id: product.id,
|
|
237
|
-
course:
|
|
239
|
+
course: PacedCourseFactory({ code: '00000' }).one(),
|
|
238
240
|
target_courses: product.target_courses,
|
|
239
241
|
}).one();
|
|
240
242
|
|
|
@@ -255,7 +257,7 @@ describe('CourseProductItem', () => {
|
|
|
255
257
|
render(
|
|
256
258
|
<CourseProductItem
|
|
257
259
|
productId={product.id}
|
|
258
|
-
course={
|
|
260
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
259
261
|
/>,
|
|
260
262
|
);
|
|
261
263
|
|
|
@@ -293,7 +295,7 @@ describe('CourseProductItem', () => {
|
|
|
293
295
|
const relation = CourseProductRelationFactory().one();
|
|
294
296
|
const order: CredentialOrder = CredentialOrderFactory({
|
|
295
297
|
product_id: relation.product.id,
|
|
296
|
-
course:
|
|
298
|
+
course: PacedCourseFactory({ code: '00000' }).one(),
|
|
297
299
|
target_courses: relation.product.target_courses,
|
|
298
300
|
}).one();
|
|
299
301
|
|
|
@@ -314,7 +316,7 @@ describe('CourseProductItem', () => {
|
|
|
314
316
|
render(
|
|
315
317
|
<CourseProductItem
|
|
316
318
|
productId={relation.product.id}
|
|
317
|
-
course={
|
|
319
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
318
320
|
compact
|
|
319
321
|
/>,
|
|
320
322
|
);
|
|
@@ -364,7 +366,7 @@ describe('CourseProductItem', () => {
|
|
|
364
366
|
}).one();
|
|
365
367
|
const order: CredentialOrder = CredentialOrderFactory({
|
|
366
368
|
product_id: product.id,
|
|
367
|
-
course:
|
|
369
|
+
course: PacedCourseFactory({ code: '00000' }).one(),
|
|
368
370
|
target_courses: product.target_courses,
|
|
369
371
|
target_enrollments: [enrollment],
|
|
370
372
|
}).one();
|
|
@@ -386,7 +388,7 @@ describe('CourseProductItem', () => {
|
|
|
386
388
|
render(
|
|
387
389
|
<CourseProductItem
|
|
388
390
|
productId={product.id}
|
|
389
|
-
course={
|
|
391
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
390
392
|
/>,
|
|
391
393
|
);
|
|
392
394
|
|
|
@@ -425,7 +427,7 @@ describe('CourseProductItem', () => {
|
|
|
425
427
|
const { product } = relation;
|
|
426
428
|
const order = CredentialOrderFactory({
|
|
427
429
|
product_id: product.id,
|
|
428
|
-
course:
|
|
430
|
+
course: PacedCourseFactory({ code: '00000' }).one(),
|
|
429
431
|
target_courses: product.target_courses,
|
|
430
432
|
state: OrderState.PENDING,
|
|
431
433
|
}).one();
|
|
@@ -446,7 +448,7 @@ describe('CourseProductItem', () => {
|
|
|
446
448
|
render(
|
|
447
449
|
<CourseProductItem
|
|
448
450
|
productId={product.id}
|
|
449
|
-
course={
|
|
451
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
450
452
|
/>,
|
|
451
453
|
);
|
|
452
454
|
|
|
@@ -495,7 +497,7 @@ describe('CourseProductItem', () => {
|
|
|
495
497
|
render(
|
|
496
498
|
<CourseProductItem
|
|
497
499
|
productId={product.id}
|
|
498
|
-
course={
|
|
500
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
499
501
|
/>,
|
|
500
502
|
);
|
|
501
503
|
|
|
@@ -529,7 +531,7 @@ describe('CourseProductItem', () => {
|
|
|
529
531
|
const { product } = relation;
|
|
530
532
|
const order = CredentialOrderFactory({
|
|
531
533
|
product_id: product.id,
|
|
532
|
-
course:
|
|
534
|
+
course: PacedCourseFactory({ code: '00000' }).one(),
|
|
533
535
|
target_courses: product.target_courses,
|
|
534
536
|
state: OrderState.SUBMITTED,
|
|
535
537
|
}).one();
|
|
@@ -550,7 +552,7 @@ describe('CourseProductItem', () => {
|
|
|
550
552
|
render(
|
|
551
553
|
<CourseProductItem
|
|
552
554
|
productId={product.id}
|
|
553
|
-
course={
|
|
555
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
554
556
|
/>,
|
|
555
557
|
);
|
|
556
558
|
|
|
@@ -600,7 +602,7 @@ describe('CourseProductItem', () => {
|
|
|
600
602
|
const order = CredentialOrderFactory({
|
|
601
603
|
product_id: product.id,
|
|
602
604
|
target_courses: product.target_courses,
|
|
603
|
-
course:
|
|
605
|
+
course: PacedCourseFactory({ code: '00000' }).one(),
|
|
604
606
|
state: orderState,
|
|
605
607
|
}).one();
|
|
606
608
|
fetchMock.get(
|
|
@@ -621,7 +623,7 @@ describe('CourseProductItem', () => {
|
|
|
621
623
|
render(
|
|
622
624
|
<CourseProductItem
|
|
623
625
|
productId={product.id}
|
|
624
|
-
course={
|
|
626
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
625
627
|
/>,
|
|
626
628
|
);
|
|
627
629
|
|
|
@@ -649,7 +651,7 @@ describe('CourseProductItem', () => {
|
|
|
649
651
|
const order = CredentialOrderFactory({
|
|
650
652
|
product_id: product.id,
|
|
651
653
|
target_courses: product.target_courses,
|
|
652
|
-
course:
|
|
654
|
+
course: PacedCourseFactory({ code: '00000' }).one(),
|
|
653
655
|
state: OrderState.VALIDATED,
|
|
654
656
|
}).one();
|
|
655
657
|
fetchMock.get(
|
|
@@ -670,7 +672,7 @@ describe('CourseProductItem', () => {
|
|
|
670
672
|
render(
|
|
671
673
|
<CourseProductItem
|
|
672
674
|
productId={product.id}
|
|
673
|
-
course={
|
|
675
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
674
676
|
/>,
|
|
675
677
|
);
|
|
676
678
|
|
|
@@ -686,7 +688,7 @@ describe('CourseProductItem', () => {
|
|
|
686
688
|
const relation = CourseProductRelationFactory().one();
|
|
687
689
|
const order: CredentialOrder = CredentialOrderFactory({
|
|
688
690
|
product_id: relation.product.id,
|
|
689
|
-
course:
|
|
691
|
+
course: PacedCourseFactory({ code: '00000' }).one(),
|
|
690
692
|
target_courses: relation.product.target_courses,
|
|
691
693
|
state: OrderState.SUBMITTED,
|
|
692
694
|
}).one();
|
|
@@ -707,7 +709,7 @@ describe('CourseProductItem', () => {
|
|
|
707
709
|
render(
|
|
708
710
|
<CourseProductItem
|
|
709
711
|
productId={relation.product.id}
|
|
710
|
-
course={
|
|
712
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
711
713
|
compact={true}
|
|
712
714
|
/>,
|
|
713
715
|
);
|
|
@@ -751,7 +753,7 @@ describe('CourseProductItem', () => {
|
|
|
751
753
|
render(
|
|
752
754
|
<CourseProductItem
|
|
753
755
|
productId={product.id}
|
|
754
|
-
course={
|
|
756
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
755
757
|
/>,
|
|
756
758
|
{ queryOptions: { client: createTestQueryClient({ user: null }) } },
|
|
757
759
|
);
|
|
@@ -767,7 +769,7 @@ describe('CourseProductItem', () => {
|
|
|
767
769
|
const { product } = relation;
|
|
768
770
|
const order = CredentialOrderFactory({
|
|
769
771
|
product_id: product.id,
|
|
770
|
-
course:
|
|
772
|
+
course: PacedCourseFactory({ code: '00000' }).one(),
|
|
771
773
|
target_courses: product.target_courses,
|
|
772
774
|
state: OrderState.PENDING,
|
|
773
775
|
}).one();
|
|
@@ -788,7 +790,7 @@ describe('CourseProductItem', () => {
|
|
|
788
790
|
render(
|
|
789
791
|
<CourseProductItem
|
|
790
792
|
productId={product.id}
|
|
791
|
-
course={
|
|
793
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
792
794
|
/>,
|
|
793
795
|
);
|
|
794
796
|
|
|
@@ -806,7 +808,7 @@ describe('CourseProductItem', () => {
|
|
|
806
808
|
const { product } = relation;
|
|
807
809
|
const order = CredentialOrderFactory({
|
|
808
810
|
product_id: product.id,
|
|
809
|
-
course:
|
|
811
|
+
course: PacedCourseFactory({ code: '00000' }).one(),
|
|
810
812
|
target_courses: product.target_courses,
|
|
811
813
|
state: OrderState.PENDING,
|
|
812
814
|
}).one();
|
|
@@ -827,7 +829,7 @@ describe('CourseProductItem', () => {
|
|
|
827
829
|
render(
|
|
828
830
|
<CourseProductItem
|
|
829
831
|
productId={product.id}
|
|
830
|
-
course={
|
|
832
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
831
833
|
/>,
|
|
832
834
|
);
|
|
833
835
|
|
|
@@ -846,7 +848,7 @@ describe('CourseProductItem', () => {
|
|
|
846
848
|
const { product } = relation;
|
|
847
849
|
const order = CredentialOrderFactory({
|
|
848
850
|
product_id: product.id,
|
|
849
|
-
course:
|
|
851
|
+
course: PacedCourseFactory({ code: '00000' }).one(),
|
|
850
852
|
target_courses: product.target_courses,
|
|
851
853
|
state: OrderState.PENDING,
|
|
852
854
|
}).one();
|
|
@@ -867,7 +869,7 @@ describe('CourseProductItem', () => {
|
|
|
867
869
|
render(
|
|
868
870
|
<CourseProductItem
|
|
869
871
|
productId={product.id}
|
|
870
|
-
course={
|
|
872
|
+
course={PacedCourseFactory({ code: '00000' }).one()}
|
|
871
873
|
/>,
|
|
872
874
|
);
|
|
873
875
|
|
|
@@ -3,7 +3,6 @@ import { QueryClientProvider } from '@tanstack/react-query';
|
|
|
3
3
|
import fetchMock from 'fetch-mock';
|
|
4
4
|
import { StorybookHelper } from 'utils/StorybookHelper';
|
|
5
5
|
import {
|
|
6
|
-
CourseLightFactory,
|
|
7
6
|
CourseProductRelationFactory,
|
|
8
7
|
CourseRunFactory,
|
|
9
8
|
CredentialOrderFactory,
|
|
@@ -12,7 +11,7 @@ import {
|
|
|
12
11
|
TargetCourseFactory,
|
|
13
12
|
} from 'utils/test/factories/joanie';
|
|
14
13
|
import { createTestQueryClient } from 'utils/test/createTestQueryClient';
|
|
15
|
-
import { UserFactory } from 'utils/test/factories/richie';
|
|
14
|
+
import { UserFactory, PacedCourseFactory } from 'utils/test/factories/richie';
|
|
16
15
|
import { CredentialOrder, OrderState } from 'types/Joanie';
|
|
17
16
|
import { Maybe } from 'types/utils';
|
|
18
17
|
import CourseProductItem, { CourseProductItemProps } from '.';
|
|
@@ -49,7 +48,7 @@ export default {
|
|
|
49
48
|
},
|
|
50
49
|
args: {
|
|
51
50
|
productId: 'AAA',
|
|
52
|
-
course:
|
|
51
|
+
course: PacedCourseFactory({ code: 'BBB' }).one(),
|
|
53
52
|
},
|
|
54
53
|
render: (args) => render(args),
|
|
55
54
|
} as Meta<typeof CourseProductItem>;
|
|
@@ -61,7 +60,7 @@ export const Default: Story = {};
|
|
|
61
60
|
export const WithPendingOrder: Story = {
|
|
62
61
|
args: {
|
|
63
62
|
productId: 'AAA',
|
|
64
|
-
course:
|
|
63
|
+
course: PacedCourseFactory({ code: 'BBB' }).one(),
|
|
65
64
|
},
|
|
66
65
|
render: (args) =>
|
|
67
66
|
render(args, { order: CredentialOrderFactory({ state: OrderState.PENDING }).one() }),
|
|
@@ -70,7 +69,7 @@ export const WithPendingOrder: Story = {
|
|
|
70
69
|
export const WithValidatedOrder: Story = {
|
|
71
70
|
args: {
|
|
72
71
|
productId: 'AAA',
|
|
73
|
-
course:
|
|
72
|
+
course: PacedCourseFactory({ code: 'BBB' }).one(),
|
|
74
73
|
},
|
|
75
74
|
render: (args) => {
|
|
76
75
|
const courseRunWithEnrollment = CourseRunFactory().one();
|
|
@@ -95,7 +94,7 @@ export const WithValidatedOrder: Story = {
|
|
|
95
94
|
export const WithSubmittedOrder: Story = {
|
|
96
95
|
args: {
|
|
97
96
|
productId: 'AAA',
|
|
98
|
-
course:
|
|
97
|
+
course: PacedCourseFactory({ code: 'BBB' }).one(),
|
|
99
98
|
},
|
|
100
99
|
render: (args) =>
|
|
101
100
|
render(args, { order: CredentialOrderFactory({ state: OrderState.SUBMITTED }).one() }),
|
|
@@ -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, OrderState,
|
|
4
|
+
import { ProductType, OrderState, 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';
|
|
@@ -12,6 +12,7 @@ import useProductOrder from 'hooks/useProductOrder';
|
|
|
12
12
|
import { OrderHelper } from 'utils/OrderHelper';
|
|
13
13
|
import { handle } from 'utils/errors/handle';
|
|
14
14
|
import { ProductSignatureHeader } from 'widgets/SyllabusCourseRunsList/components/CourseProductItem/components/ProductSignatureHeader';
|
|
15
|
+
import { PacedCourse } from 'types';
|
|
15
16
|
import CertificateItem from './components/CourseProductCertificateItem';
|
|
16
17
|
import CourseRunItem from './components/CourseRunItem';
|
|
17
18
|
import CourseProductItemFooter from './CourseProductItemFooter';
|
|
@@ -48,7 +49,7 @@ const messages = defineMessages({
|
|
|
48
49
|
|
|
49
50
|
export interface CourseProductItemProps {
|
|
50
51
|
compact?: boolean;
|
|
51
|
-
course:
|
|
52
|
+
course: PacedCourse;
|
|
52
53
|
productId: Product['id'];
|
|
53
54
|
}
|
|
54
55
|
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
import { screen } from '@testing-library/react';
|
|
6
6
|
import fetchMock from 'fetch-mock';
|
|
7
7
|
import userEvent from '@testing-library/user-event';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
RichieContextFactory as mockRichieContextFactory,
|
|
10
|
+
PacedCourseFactory,
|
|
11
|
+
} from 'utils/test/factories/richie';
|
|
9
12
|
import { HttpStatusCode } from 'utils/errors/HttpError';
|
|
10
|
-
import { CourseLightFactory } from 'utils/test/factories/joanie';
|
|
11
13
|
import { setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
|
|
12
14
|
import { render } from 'utils/test/render';
|
|
13
15
|
import { expectNoSpinner } from 'utils/test/expectSpinner';
|
|
@@ -29,7 +31,7 @@ jest.mock('utils/context', () => ({
|
|
|
29
31
|
describe('CourseWishButton', () => {
|
|
30
32
|
const joanieSessionData = setupJoanieSession();
|
|
31
33
|
let nbApiCalls: number;
|
|
32
|
-
const course =
|
|
34
|
+
const course = PacedCourseFactory().one();
|
|
33
35
|
|
|
34
36
|
beforeEach(() => {
|
|
35
37
|
nbApiCalls = joanieSessionData.nbSessionApiRequest;
|
|
@@ -5,9 +5,11 @@
|
|
|
5
5
|
import { screen } from '@testing-library/react';
|
|
6
6
|
import userEvent from '@testing-library/user-event';
|
|
7
7
|
import fetchMock from 'fetch-mock';
|
|
8
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
RichieContextFactory as mockRichieContextFactory,
|
|
10
|
+
PacedCourseFactory,
|
|
11
|
+
} from 'utils/test/factories/richie';
|
|
9
12
|
import { location } from 'utils/indirection/window';
|
|
10
|
-
import { CourseLightFactory } from 'utils/test/factories/joanie';
|
|
11
13
|
import { setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
|
|
12
14
|
import { render } from 'utils/test/render';
|
|
13
15
|
import { HttpStatusCode } from 'utils/errors/HttpError';
|
|
@@ -37,7 +39,7 @@ jest.mock('utils/context', () => ({
|
|
|
37
39
|
|
|
38
40
|
describe('CourseWishButton', () => {
|
|
39
41
|
setupJoanieSession();
|
|
40
|
-
const course =
|
|
42
|
+
const course = PacedCourseFactory().one();
|
|
41
43
|
|
|
42
44
|
it('renders a log me link', async () => {
|
|
43
45
|
fetchMock.get(`https://joanie.endpoint/api/v1.0/courses/${course.code}/wish/`, {
|
|
@@ -3,7 +3,7 @@ import { defineMessages, FormattedMessage } from 'react-intl';
|
|
|
3
3
|
import { Button } from '@openfun/cunningham-react';
|
|
4
4
|
import { Spinner } from 'components/Spinner';
|
|
5
5
|
import { useSession } from 'contexts/SessionContext';
|
|
6
|
-
import {
|
|
6
|
+
import { PacedCourse } from 'types';
|
|
7
7
|
import { useCourseWish } from './hooks/useCourseWish';
|
|
8
8
|
|
|
9
9
|
const messages = defineMessages({
|
|
@@ -37,7 +37,7 @@ enum ComponentStates {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export interface Props {
|
|
40
|
-
course:
|
|
40
|
+
course: PacedCourse;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
const CourseWishButton = ({ course }: Props) => {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
import { CourseRun, Priority } from 'types';
|
|
3
|
+
import { PacedCourse, CourseRun, Priority } from 'types';
|
|
4
|
+
import { CourseRunHelper } from 'utils/CourseRunHelper';
|
|
4
5
|
import { SyllabusSimpleCourseRunsList } from 'widgets/SyllabusCourseRunsList/components/SyllabusSimpleCourseRunsList';
|
|
5
6
|
import { SyllabusCourseRun } from 'widgets/SyllabusCourseRunsList/components/SyllabusCourseRun';
|
|
6
|
-
import { CourseLight } from 'types/Joanie';
|
|
7
7
|
|
|
8
8
|
const messages = defineMessages({
|
|
9
9
|
otherCourseRuns: {
|
|
@@ -57,7 +57,7 @@ export const SyllabusAsideList = ({
|
|
|
57
57
|
maxArchivedCourseRuns,
|
|
58
58
|
}: {
|
|
59
59
|
courseRuns: CourseRun[];
|
|
60
|
-
course:
|
|
60
|
+
course: PacedCourse;
|
|
61
61
|
maxArchivedCourseRuns: number;
|
|
62
62
|
}) => {
|
|
63
63
|
const intl = useIntl();
|
|
@@ -106,6 +106,8 @@ export const SyllabusAsideList = ({
|
|
|
106
106
|
[Priority.ARCHIVED_CLOSED].includes(run.state.priority),
|
|
107
107
|
);
|
|
108
108
|
|
|
109
|
+
const showLanguages = CourseRunHelper.IsAllCourseRunsWithSameLanguages(courseRuns);
|
|
110
|
+
|
|
109
111
|
return (
|
|
110
112
|
<>
|
|
111
113
|
<h2 className="course-detail__title">
|
|
@@ -134,7 +136,12 @@ export const SyllabusAsideList = ({
|
|
|
134
136
|
className="course-detail__row course-detail__runs course-detail__runs--open"
|
|
135
137
|
>
|
|
136
138
|
{openedRuns.map((run) => (
|
|
137
|
-
<SyllabusCourseRun
|
|
139
|
+
<SyllabusCourseRun
|
|
140
|
+
key={run.id}
|
|
141
|
+
courseRun={run}
|
|
142
|
+
course={course}
|
|
143
|
+
showLanguages={showLanguages}
|
|
144
|
+
/>
|
|
138
145
|
))}
|
|
139
146
|
</div>
|
|
140
147
|
)}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|
2
2
|
import { Button } from '@openfun/cunningham-react';
|
|
3
|
-
import { CourseRun, CourseRunDisplayMode } from 'types';
|
|
3
|
+
import { CourseRun, CourseRunDisplayMode, PacedCourse } from 'types';
|
|
4
4
|
import useDateFormat from 'hooks/useDateFormat';
|
|
5
5
|
import { extractResourceId, isJoanieResourceLinkProduct } from 'api/lms/joanie';
|
|
6
6
|
import { findLmsBackend } from 'api/configuration';
|
|
7
7
|
import { StringHelper } from 'utils/StringHelper';
|
|
8
8
|
import { IntlHelper } from 'utils/IntlHelper';
|
|
9
9
|
import { DjangoCMSPluginCourseRun, DjangoCMSTemplate } from 'components/DjangoCMSTemplate';
|
|
10
|
-
import { CourseLight } from 'types/Joanie';
|
|
11
10
|
import CourseRunEnrollment from '../CourseRunEnrollment';
|
|
12
11
|
import CourseProductItem from '../CourseProductItem';
|
|
13
12
|
|
|
@@ -39,7 +38,13 @@ const messages = defineMessages({
|
|
|
39
38
|
},
|
|
40
39
|
});
|
|
41
40
|
|
|
42
|
-
const OpenedCourseRun = ({
|
|
41
|
+
const OpenedCourseRun = ({
|
|
42
|
+
courseRun,
|
|
43
|
+
showLanguages,
|
|
44
|
+
}: {
|
|
45
|
+
courseRun: CourseRun;
|
|
46
|
+
showLanguages: boolean;
|
|
47
|
+
}) => {
|
|
43
48
|
const formatDate = useDateFormat();
|
|
44
49
|
const intl = useIntl();
|
|
45
50
|
const enrollmentStart = courseRun.enrollment_start
|
|
@@ -76,10 +81,14 @@ const OpenedCourseRun = ({ courseRun }: { courseRun: CourseRun }) => {
|
|
|
76
81
|
}}
|
|
77
82
|
/>
|
|
78
83
|
</dd>
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
{!showLanguages && (
|
|
85
|
+
<>
|
|
86
|
+
<dt>
|
|
87
|
+
<FormattedMessage {...messages.languages} />
|
|
88
|
+
</dt>
|
|
89
|
+
<dd>{IntlHelper.getLocalizedLanguages(courseRun.languages, intl)}</dd>
|
|
90
|
+
</>
|
|
91
|
+
)}
|
|
83
92
|
</dl>
|
|
84
93
|
{findLmsBackend(courseRun.resource_link) ? (
|
|
85
94
|
<CourseRunEnrollment courseRun={courseRun} />
|
|
@@ -95,9 +104,11 @@ const OpenedCourseRun = ({ courseRun }: { courseRun: CourseRun }) => {
|
|
|
95
104
|
export const SyllabusCourseRun = ({
|
|
96
105
|
courseRun,
|
|
97
106
|
course,
|
|
107
|
+
showLanguages,
|
|
98
108
|
}: {
|
|
99
109
|
courseRun: CourseRun;
|
|
100
|
-
course:
|
|
110
|
+
course: PacedCourse;
|
|
111
|
+
showLanguages: boolean;
|
|
101
112
|
}) => {
|
|
102
113
|
return (
|
|
103
114
|
<DjangoCMSTemplate plugin={DjangoCMSPluginCourseRun(courseRun)}>
|
|
@@ -109,7 +120,7 @@ export const SyllabusCourseRun = ({
|
|
|
109
120
|
compact={courseRun.display_mode === CourseRunDisplayMode.COMPACT}
|
|
110
121
|
/>
|
|
111
122
|
) : (
|
|
112
|
-
<OpenedCourseRun courseRun={courseRun} />
|
|
123
|
+
<OpenedCourseRun courseRun={courseRun} showLanguages={showLanguages} />
|
|
113
124
|
)}
|
|
114
125
|
</div>
|
|
115
126
|
</DjangoCMSTemplate>
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { defineMessages, FormattedMessage, useIntl } from 'react-intl';
|
|
2
|
+
import { CourseRun, CourseRunDisplayMode, PacedCourse } from 'types';
|
|
3
|
+
import useDateFormat from 'hooks/useDateFormat';
|
|
4
|
+
import { extractResourceId, isJoanieResourceLinkProduct } from 'api/lms/joanie';
|
|
5
|
+
import { findLmsBackend } from 'api/configuration';
|
|
6
|
+
import { StringHelper } from 'utils/StringHelper';
|
|
7
|
+
import { IntlHelper } from 'utils/IntlHelper';
|
|
8
|
+
import { DjangoCMSPluginCourseRun, DjangoCMSTemplate } from 'components/DjangoCMSTemplate';
|
|
9
|
+
import CourseRunEnrollment from '../CourseRunEnrollment';
|
|
10
|
+
import CourseProductItem from '../CourseProductItem';
|
|
11
|
+
|
|
12
|
+
const messages = defineMessages({
|
|
13
|
+
course: {
|
|
14
|
+
id: 'components.SyllabusCourseRunCompacted.course',
|
|
15
|
+
description: 'Title of the course dates section of an opened course run block',
|
|
16
|
+
defaultMessage: 'Course',
|
|
17
|
+
},
|
|
18
|
+
languages: {
|
|
19
|
+
id: 'components.SyllabusCourseRunCompacted.languages',
|
|
20
|
+
description: 'Title of the languages section of an opened course run block',
|
|
21
|
+
defaultMessage: 'Languages',
|
|
22
|
+
},
|
|
23
|
+
selfPaceRunPeriod: {
|
|
24
|
+
id: 'components.SyllabusCourseRunCompacted.selfPaceCoursePeriod',
|
|
25
|
+
description: 'Course date of an opened and self paced course run block',
|
|
26
|
+
defaultMessage: 'Available until {endDate}',
|
|
27
|
+
},
|
|
28
|
+
selfPaceNoEndDate: {
|
|
29
|
+
id: 'components.SyllabusCourseRunCompacted.selfPaceNoEndDate',
|
|
30
|
+
description: 'Self paced course run block with no end date',
|
|
31
|
+
defaultMessage: 'Available',
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const OpenedSelfPacedCourseRun = ({
|
|
36
|
+
courseRun,
|
|
37
|
+
showLanguages,
|
|
38
|
+
}: {
|
|
39
|
+
courseRun: CourseRun;
|
|
40
|
+
showLanguages: boolean;
|
|
41
|
+
}) => {
|
|
42
|
+
const formatDate = useDateFormat();
|
|
43
|
+
const intl = useIntl();
|
|
44
|
+
const end = courseRun.end ? formatDate(courseRun.end) : '...';
|
|
45
|
+
const hasEndDate = end !== '...';
|
|
46
|
+
return (
|
|
47
|
+
<>
|
|
48
|
+
{courseRun.title && <h3>{StringHelper.capitalizeFirst(courseRun.title)}</h3>}
|
|
49
|
+
<dl>
|
|
50
|
+
{!showLanguages && (
|
|
51
|
+
<dt>
|
|
52
|
+
<FormattedMessage {...messages.course} />
|
|
53
|
+
</dt>
|
|
54
|
+
)}
|
|
55
|
+
<dd>
|
|
56
|
+
{hasEndDate ? (
|
|
57
|
+
<FormattedMessage
|
|
58
|
+
{...messages.selfPaceRunPeriod}
|
|
59
|
+
values={{
|
|
60
|
+
endDate: end,
|
|
61
|
+
}}
|
|
62
|
+
/>
|
|
63
|
+
) : (
|
|
64
|
+
<FormattedMessage {...messages.selfPaceNoEndDate} />
|
|
65
|
+
)}
|
|
66
|
+
</dd>
|
|
67
|
+
{!showLanguages && (
|
|
68
|
+
<>
|
|
69
|
+
<dt>
|
|
70
|
+
<FormattedMessage {...messages.languages} />
|
|
71
|
+
</dt>
|
|
72
|
+
<dd>{IntlHelper.getLocalizedLanguages(courseRun.languages, intl)}</dd>
|
|
73
|
+
</>
|
|
74
|
+
)}
|
|
75
|
+
</dl>
|
|
76
|
+
{findLmsBackend(courseRun.resource_link) ? (
|
|
77
|
+
<CourseRunEnrollment courseRun={courseRun} />
|
|
78
|
+
) : (
|
|
79
|
+
<a className="course-run-enrollment__cta" href={courseRun.resource_link}>
|
|
80
|
+
{StringHelper.capitalizeFirst(courseRun.state.call_to_action)}
|
|
81
|
+
</a>
|
|
82
|
+
)}
|
|
83
|
+
</>
|
|
84
|
+
);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const SyllabusCourseRunCompacted = ({
|
|
88
|
+
courseRun,
|
|
89
|
+
course,
|
|
90
|
+
showLanguages,
|
|
91
|
+
}: {
|
|
92
|
+
courseRun: CourseRun;
|
|
93
|
+
course: PacedCourse;
|
|
94
|
+
showLanguages: boolean;
|
|
95
|
+
}) => {
|
|
96
|
+
return (
|
|
97
|
+
<DjangoCMSTemplate plugin={DjangoCMSPluginCourseRun(courseRun)}>
|
|
98
|
+
<div className="course-detail__run-descriptions course-detail__run-descriptions--course_and_search">
|
|
99
|
+
{isJoanieResourceLinkProduct(courseRun.resource_link) ? (
|
|
100
|
+
<CourseProductItem
|
|
101
|
+
productId={extractResourceId(courseRun.resource_link, 'product')!}
|
|
102
|
+
course={course}
|
|
103
|
+
compact={courseRun.display_mode === CourseRunDisplayMode.COMPACT}
|
|
104
|
+
/>
|
|
105
|
+
) : (
|
|
106
|
+
<OpenedSelfPacedCourseRun courseRun={courseRun} showLanguages={showLanguages} />
|
|
107
|
+
)}
|
|
108
|
+
</div>
|
|
109
|
+
</DjangoCMSTemplate>
|
|
110
|
+
);
|
|
111
|
+
};
|