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.
Files changed (32) hide show
  1. package/js/components/AddressesManagement/AddressForm/validationSchema.spec.ts +2 -2
  2. package/js/components/PaymentInterfaces/LyraPopIn.tsx +10 -3
  3. package/js/components/PaymentInterfaces/types.ts +1 -1
  4. package/js/components/PurchaseButton/index.spec.tsx +9 -9
  5. package/js/components/PurchaseButton/index.tsx +2 -1
  6. package/js/components/SaleTunnel/GenericPaymentButton/index.tsx +13 -3
  7. package/js/components/SaleTunnel/hooks/useTerms.tsx +2 -2
  8. package/js/components/SaleTunnel/index.credential.spec.tsx +2 -2
  9. package/js/components/SaleTunnel/index.full-process.spec.tsx +11 -4
  10. package/js/components/SaleTunnel/index.spec.tsx +2 -2
  11. package/js/components/SaleTunnel/index.stories.tsx +3 -2
  12. package/js/components/SaleTunnel/index.tsx +2 -2
  13. package/js/types/commonDataProps.ts +0 -1
  14. package/js/types/index.ts +6 -0
  15. package/js/utils/CourseRunHelper/index.spec.ts +35 -0
  16. package/js/utils/CourseRunHelper/index.ts +13 -0
  17. package/js/utils/react-query/useSessionQuery/index.ts +1 -1
  18. package/js/utils/test/factories/richie.ts +16 -2
  19. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/CourseProductItemFooter/index.tsx +3 -2
  20. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +32 -30
  21. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +5 -6
  22. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +3 -2
  23. package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.login.spec.tsx +5 -3
  24. package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.logout.spec.tsx +5 -3
  25. package/js/widgets/SyllabusCourseRunsList/components/CourseWishButton/index.tsx +2 -2
  26. package/js/widgets/SyllabusCourseRunsList/components/SyllabusAsideList/index.tsx +11 -4
  27. package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRun/index.tsx +20 -9
  28. package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRunCompacted/index.tsx +111 -0
  29. package/js/widgets/SyllabusCourseRunsList/index.spec.tsx +311 -15
  30. package/js/widgets/SyllabusCourseRunsList/index.tsx +24 -8
  31. package/package.json +44 -44
  32. 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
- CourseLightFactory,
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={CourseLightFactory({ code: '00000' }).one()}
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={CourseLightFactory({ code: '00000' }).one()}
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={CourseLightFactory({ code: '00000' }).one()}
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={CourseLightFactory({ code: '00000' }).one()}
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: CourseLightFactory({ code: '00000' }).one(),
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={CourseLightFactory({ code: '00000' }).one()}
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: CourseLightFactory({ code: '00000' }).one(),
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={CourseLightFactory({ code: '00000' }).one()}
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: CourseLightFactory({ code: '00000' }).one(),
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={CourseLightFactory({ code: '00000' }).one()}
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: CourseLightFactory({ code: '00000' }).one(),
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={CourseLightFactory({ code: '00000' }).one()}
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={CourseLightFactory({ code: '00000' }).one()}
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: CourseLightFactory({ code: '00000' }).one(),
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={CourseLightFactory({ code: '00000' }).one()}
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: CourseLightFactory({ code: '00000' }).one(),
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={CourseLightFactory({ code: '00000' }).one()}
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: CourseLightFactory({ code: '00000' }).one(),
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={CourseLightFactory({ code: '00000' }).one()}
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: CourseLightFactory({ code: '00000' }).one(),
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={CourseLightFactory({ code: '00000' }).one()}
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={CourseLightFactory({ code: '00000' }).one()}
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: CourseLightFactory({ code: '00000' }).one(),
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={CourseLightFactory({ code: '00000' }).one()}
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: CourseLightFactory({ code: '00000' }).one(),
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={CourseLightFactory({ code: '00000' }).one()}
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: CourseLightFactory({ code: '00000' }).one(),
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={CourseLightFactory({ code: '00000' }).one()}
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: CourseLightFactory({ code: 'BBB' }).one(),
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: CourseLightFactory({ code: 'BBB' }).one(),
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: CourseLightFactory({ code: 'BBB' }).one(),
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: CourseLightFactory({ code: 'BBB' }).one(),
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, CourseLight, Product, CredentialOrder } from 'types/Joanie';
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: CourseLight;
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 { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
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 = CourseLightFactory().one();
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 { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
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 = CourseLightFactory().one();
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 { CourseLight } from 'types/Joanie';
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: CourseLight;
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: CourseLight;
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 key={run.id} courseRun={run} course={course} />
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 = ({ courseRun }: { courseRun: CourseRun }) => {
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
- <dt>
80
- <FormattedMessage {...messages.languages} />
81
- </dt>
82
- <dd>{IntlHelper.getLocalizedLanguages(courseRun.languages, intl)}</dd>
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: CourseLight;
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
+ };