richie-education 2.28.1 → 2.28.2-dev24

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
@@ -15,7 +15,7 @@ describe('validationSchema', () => {
15
15
  first_name: faker.person.firstName(),
16
16
  last_name: faker.person.lastName(),
17
17
  postcode: faker.location.zipCode(),
18
- title: faker.lorem.word(),
18
+ title: faker.lorem.word({ length: { min: 2, max: 15 } }),
19
19
  save: false,
20
20
  };
21
21
 
@@ -130,7 +130,7 @@ describe('validationSchema', () => {
130
130
  result.current.setValue('first_name', faker.person.firstName());
131
131
  result.current.setValue('last_name', faker.person.lastName());
132
132
  result.current.setValue('postcode', faker.location.zipCode());
133
- result.current.setValue('title', faker.lorem.word());
133
+ result.current.setValue('title', faker.lorem.word({ length: { min: 2, max: 15 } }));
134
134
  result.current.trigger();
135
135
  });
136
136
 
@@ -25,11 +25,13 @@ const LyraPopIn = ({
25
25
  const intl = useIntl();
26
26
  const shouldAbort = useRef<Boolean>(true);
27
27
 
28
- const handleError = (error?: Error) => {
29
- if (error) handle(`[LyraPopIn] - ${error}`);
28
+ const handleError = (error?: Error | string) => {
29
+ if (error && typeof error === 'string') handle(`[LyraPopIn] - ${error}`);
30
30
 
31
31
  if (shouldAbort.current) {
32
32
  onError(PaymentErrorMessageId.ERROR_ABORTING);
33
+ } else if (typeof error === 'string') {
34
+ onError(error);
33
35
  } else {
34
36
  onError(PaymentErrorMessageId.ERROR_DEFAULT);
35
37
  }
@@ -95,8 +97,13 @@ const LyraPopIn = ({
95
97
  // Do not close the pop-in if the error is a invalid data error (CLIENT_3XX).
96
98
  // https://docs.lyra.com/fr/rest/V4.0/javascript/features/js_error_management.html#client004
97
99
  if (!error.errorCode.startsWith('CLIENT_3')) {
100
+ shouldAbort.current = false;
98
101
  await KR.closePopin(formId);
99
- handleError();
102
+ let errorMessages = error.errorMessage;
103
+ if (error.detailedErrorMessage) {
104
+ errorMessages += `: ${error.detailedErrorMessage}`;
105
+ }
106
+ handleError(errorMessages);
100
107
  }
101
108
  };
102
109
 
@@ -42,5 +42,5 @@ export type Payment = DummyPayment | PayplugPayment | LyraPayment;
42
42
 
43
43
  export type PaymentInterfaceProps<P extends Payment = Payment> = P & {
44
44
  onSuccess: () => void;
45
- onError: (messageId: PaymentErrorMessageId) => void;
45
+ onError: (messageId: string | PaymentErrorMessageId) => void;
46
46
  };
@@ -6,12 +6,12 @@ import userEvent from '@testing-library/user-event';
6
6
  import { CunninghamProvider } from '@openfun/cunningham-react';
7
7
  import {
8
8
  CourseStateFactory,
9
+ PacedCourseFactory,
9
10
  UserFactory,
10
11
  RichieContextFactory as mockRichieContextFactory,
11
12
  } from 'utils/test/factories/richie';
12
13
  import {
13
14
  CertificateProductFactory,
14
- CourseLightFactory,
15
15
  EnrollmentFactory,
16
16
  ProductFactory,
17
17
  } from 'utils/test/factories/joanie';
@@ -89,7 +89,7 @@ describe('PurchaseButton', () => {
89
89
  <PurchaseButton
90
90
  product={product}
91
91
  disabled={false}
92
- course={CourseLightFactory({ code: '00000' }).one()}
92
+ course={PacedCourseFactory({ code: '00000' }).one()}
93
93
  />
94
94
  </Wrapper>,
95
95
  );
@@ -112,7 +112,7 @@ describe('PurchaseButton', () => {
112
112
  <PurchaseButton
113
113
  product={product}
114
114
  disabled={false}
115
- course={CourseLightFactory({ code: courseCode }).one()}
115
+ course={PacedCourseFactory({ code: courseCode }).one()}
116
116
  />
117
117
  </Wrapper>,
118
118
  );
@@ -146,7 +146,7 @@ describe('PurchaseButton', () => {
146
146
  <PurchaseButton
147
147
  product={product}
148
148
  disabled={false}
149
- course={CourseLightFactory({ code: courseCode }).one()}
149
+ course={PacedCourseFactory({ code: courseCode }).one()}
150
150
  />
151
151
  </Wrapper>,
152
152
  );
@@ -181,7 +181,7 @@ describe('PurchaseButton', () => {
181
181
  <PurchaseButton
182
182
  product={product}
183
183
  disabled={false}
184
- course={CourseLightFactory({ code: courseCode }).one()}
184
+ course={PacedCourseFactory({ code: courseCode }).one()}
185
185
  />
186
186
  </Wrapper>,
187
187
  );
@@ -214,7 +214,7 @@ describe('PurchaseButton', () => {
214
214
  <PurchaseButton
215
215
  product={product}
216
216
  disabled={false}
217
- course={CourseLightFactory({ code: courseCode }).one()}
217
+ course={PacedCourseFactory({ code: courseCode }).one()}
218
218
  />
219
219
  </Wrapper>,
220
220
  );
@@ -251,7 +251,7 @@ describe('PurchaseButton', () => {
251
251
  <PurchaseButton
252
252
  product={product}
253
253
  disabled={false}
254
- course={CourseLightFactory({ code: courseCode }).one()}
254
+ course={PacedCourseFactory({ code: courseCode }).one()}
255
255
  />
256
256
  </Wrapper>,
257
257
  );
@@ -384,7 +384,7 @@ describe('PurchaseButton', () => {
384
384
  <PurchaseButton
385
385
  product={product}
386
386
  disabled={false}
387
- course={CourseLightFactory({ code: courseCode }).one()}
387
+ course={PacedCourseFactory({ code: courseCode }).one()}
388
388
  />
389
389
  </Wrapper>,
390
390
  );
@@ -414,7 +414,7 @@ describe('PurchaseButton', () => {
414
414
  <PurchaseButton
415
415
  product={product}
416
416
  disabled={true}
417
- course={CourseLightFactory({ code: courseCode }).one()}
417
+ course={PacedCourseFactory({ code: courseCode }).one()}
418
418
  />
419
419
  </Wrapper>,
420
420
  );
@@ -7,6 +7,7 @@ import * as Joanie from 'types/Joanie';
7
7
  import { isOpenedCourseRunCertificate, isOpenedCourseRunCredential } from 'utils/CourseRuns';
8
8
  import { SaleTunnel, SaleTunnelProps } from 'components/SaleTunnel';
9
9
  import { Organization } from 'types/Joanie';
10
+ import { PacedCourse } from 'types';
10
11
 
11
12
  const messages = defineMessages({
12
13
  loginToPurchase: {
@@ -52,7 +53,7 @@ interface PurchaseButtonPropsBase {
52
53
 
53
54
  interface CredentialPurchaseButtonProps extends PurchaseButtonPropsBase {
54
55
  product: Joanie.CredentialProduct;
55
- course: Joanie.CourseLight;
56
+ course: PacedCourse;
56
57
  enrollment?: undefined;
57
58
  }
58
59
 
@@ -97,7 +97,7 @@ export const GenericPaymentButton = ({ buildOrderPayload }: Props) => {
97
97
  const { methods: orderMethods } = useOrders(undefined, { enabled: false });
98
98
  const [payment, setPayment] = useState<PaymentInfo>();
99
99
  const [state, setState] = useState<ComponentStates>(ComponentStates.IDLE);
100
- const [error, setError] = useState<PaymentErrorMessageId>();
100
+ const [error, setError] = useState<PaymentErrorMessageId | string>();
101
101
  const hasPaymentId = (p: Maybe<Payment>): p is Extract<Payment, PaymentWithId> => {
102
102
  return Boolean(p?.hasOwnProperty('payment_id'));
103
103
  };
@@ -256,7 +256,9 @@ export const GenericPaymentButton = ({ buildOrderPayload }: Props) => {
256
256
  checkOrderValidity();
257
257
  };
258
258
 
259
- const handleError = (messageId: PaymentErrorMessageId = PaymentErrorMessageId.ERROR_DEFAULT) => {
259
+ const handleError = (
260
+ messageId: PaymentErrorMessageId | string = PaymentErrorMessageId.ERROR_DEFAULT,
261
+ ) => {
260
262
  setState(ComponentStates.ERROR);
261
263
  setError(messageId);
262
264
  };
@@ -274,6 +276,8 @@ export const GenericPaymentButton = ({ buildOrderPayload }: Props) => {
274
276
  .catch(() => {
275
277
  handleError();
276
278
  });
279
+ } else if (error && !messages.hasOwnProperty(error)) {
280
+ orderMethods.invalidate();
277
281
  } else if (state === ComponentStates.ERROR) {
278
282
  setPayment(undefined);
279
283
  }
@@ -320,7 +324,13 @@ export const GenericPaymentButton = ({ buildOrderPayload }: Props) => {
320
324
  )}
321
325
  {state === ComponentStates.ERROR && (
322
326
  <p className="payment-button__error" id="sale-tunnel-payment-error" tabIndex={-1}>
323
- <FormattedMessage {...messages[error || PaymentErrorMessageId.ERROR_DEFAULT]} />
327
+ {!error || messages.hasOwnProperty(error) ? (
328
+ <FormattedMessage
329
+ {...messages[(error as PaymentErrorMessageId) || PaymentErrorMessageId.ERROR_DEFAULT]}
330
+ />
331
+ ) : (
332
+ error
333
+ )}
324
334
  </p>
325
335
  )}
326
336
  </>
@@ -29,8 +29,8 @@ export const useTerms = ({
29
29
  error,
30
30
  }: {
31
31
  product: Product;
32
- onError: (error: PaymentErrorMessageId) => void;
33
- error?: PaymentErrorMessageId;
32
+ onError: (error: PaymentErrorMessageId | string) => void;
33
+ error?: PaymentErrorMessageId | string;
34
34
  }) => {
35
35
  const intl = useIntl();
36
36
  const [termsAccepted, setTermsAccepted] = useState(false);
@@ -2,12 +2,12 @@ import fetchMock from 'fetch-mock';
2
2
  import { act, fireEvent, screen, waitFor } from '@testing-library/react';
3
3
  import {
4
4
  RichieContextFactory as mockRichieContextFactory,
5
+ PacedCourseFactory,
5
6
  UserFactory,
6
7
  } from 'utils/test/factories/richie';
7
8
  import { setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
8
9
  import {
9
10
  AddressFactory,
10
- CourseFactory,
11
11
  CredentialOrderWithPaymentFactory,
12
12
  CredentialProductFactory,
13
13
  OrderGroupFactory,
@@ -86,7 +86,7 @@ describe('SaleTunnel / Credential', () => {
86
86
  });
87
87
 
88
88
  it('should create an order with an order group', async () => {
89
- const course = CourseFactory().one();
89
+ const course = PacedCourseFactory().one();
90
90
  const product = CredentialProductFactory().one();
91
91
  const orderGroup = OrderGroupFactory().one();
92
92
  const billingAddress: Joanie.Address = AddressFactory({ is_main: true }).one();
@@ -5,6 +5,7 @@ import userEvent from '@testing-library/user-event';
5
5
  import countries from 'i18n-iso-countries';
6
6
  import {
7
7
  RichieContextFactory as mockRichieContextFactory,
8
+ PacedCourseFactory,
8
9
  UserFactory,
9
10
  } from 'utils/test/factories/richie';
10
11
  import { render } from 'utils/test/render';
@@ -12,8 +13,8 @@ import { setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
12
13
  import CourseProductItem from 'widgets/SyllabusCourseRunsList/components/CourseProductItem';
13
14
  import {
14
15
  AddressFactory,
15
- CourseProductRelationFactory,
16
16
  CredentialOrderWithPaymentFactory,
17
+ CourseProductRelationFactory,
17
18
  } from 'utils/test/factories/joanie';
18
19
  import { ACTIVE_ORDER_STATES, CourseRun } from 'types/Joanie';
19
20
  import { Priority } from 'types';
@@ -101,9 +102,15 @@ describe('SaleTunnel', () => {
101
102
  [],
102
103
  );
103
104
 
104
- render(<CourseProductItem productId={product.id} course={course} />, {
105
- queryOptions: { client: createTestQueryClient({ user: richieUser }) },
106
- });
105
+ render(
106
+ <CourseProductItem
107
+ productId={product.id}
108
+ course={PacedCourseFactory({ id: course.id, code: course.code }).one()}
109
+ />,
110
+ {
111
+ queryOptions: { client: createTestQueryClient({ user: richieUser }) },
112
+ },
113
+ );
107
114
 
108
115
  // Wait for product information to be fetched
109
116
  await screen.findByRole('heading', { level: 3, name: product.title });
@@ -8,7 +8,6 @@ import {
8
8
  CertificateOrderWithOneClickPaymentFactory,
9
9
  CertificateOrderWithPaymentFactory,
10
10
  CertificateProductFactory,
11
- CourseFactory,
12
11
  CredentialOrderWithOneClickPaymentFactory,
13
12
  CredentialOrderWithPaymentFactory,
14
13
  CredentialProductFactory,
@@ -17,6 +16,7 @@ import {
17
16
  } from 'utils/test/factories/joanie';
18
17
  import {
19
18
  RichieContextFactory as mockRichieContextFactory,
19
+ PacedCourseFactory,
20
20
  UserFactory,
21
21
  } from 'utils/test/factories/richie';
22
22
  import { render } from 'utils/test/render';
@@ -70,7 +70,7 @@ describe.each([
70
70
  ({ productType, ProductFactory, OrderWithOneClickPaymentFactory, OrderWithPaymentFactory }) => {
71
71
  let nbApiCalls: number;
72
72
 
73
- const course = CourseFactory().one();
73
+ const course = PacedCourseFactory().one();
74
74
  const enrollment =
75
75
  productType === ProductType.CERTIFICATE ? EnrollmentFactory().one() : undefined;
76
76
 
@@ -1,6 +1,7 @@
1
1
  import { StoryObj, Meta } from '@storybook/react';
2
2
  import { BaseJoanieAppWrapper } from 'utils/test/wrappers/BaseJoanieAppWrapper';
3
- import { CourseFactory, ProductFactory } from 'utils/test/factories/joanie';
3
+ import { ProductFactory } from 'utils/test/factories/joanie';
4
+ import { PacedCourseFactory } from 'utils/test/factories/richie';
4
5
  import { SaleTunnel, SaleTunnelProps } from './index';
5
6
 
6
7
  export default {
@@ -10,7 +11,7 @@ export default {
10
11
  isOpen: true,
11
12
  product: ProductFactory().one(),
12
13
  onClose: () => {},
13
- course: CourseFactory().one(),
14
+ course: PacedCourseFactory().one(),
14
15
  // enrollment?: Enrollment;
15
16
  // product: CredentialProduct | CertificateProduct;
16
17
  // orderGroup?: OrderGroup;
@@ -1,7 +1,6 @@
1
1
  import { ModalProps } from '@openfun/cunningham-react';
2
2
  import {
3
3
  CertificateProduct,
4
- CourseLight,
5
4
  CredentialProduct,
6
5
  Enrollment,
7
6
  Order,
@@ -12,12 +11,13 @@ import {
12
11
  } from 'types/Joanie';
13
12
  import { CredentialSaleTunnel } from 'components/SaleTunnel/CredentialSaleTunnel';
14
13
  import { CertificateSaleTunnel } from 'components/SaleTunnel/CertificateSaleTunnel';
14
+ import { PacedCourse } from 'types';
15
15
 
16
16
  export interface SaleTunnelProps extends Pick<ModalProps, 'isOpen' | 'onClose'> {
17
17
  product: Product;
18
18
  organizations?: Organization[];
19
19
 
20
- course?: CourseLight;
20
+ course?: PacedCourse;
21
21
  enrollment?: Enrollment;
22
22
  orderGroup?: OrderGroup;
23
23
  onFinish?: (order: Order) => void;
@@ -23,7 +23,6 @@ enum FEATURES {
23
23
 
24
24
  export interface RichieContext {
25
25
  authentication: AuthenticationBackend;
26
- csrftoken: string;
27
26
  environment: string;
28
27
  features: Partial<Record<FEATURES, boolean>>;
29
28
  joanie_backend?: JoanieBackend;
package/js/types/index.ts CHANGED
@@ -16,6 +16,12 @@ export enum CourseRunDisplayMode {
16
16
  DETAILED = 'detailed',
17
17
  }
18
18
 
19
+ export interface PacedCourse {
20
+ id: string;
21
+ code: string;
22
+ is_self_paced: boolean;
23
+ }
24
+
19
25
  export interface CourseRun {
20
26
  id: number;
21
27
  resource_link: string;
@@ -0,0 +1,35 @@
1
+ import { CourseRun, Priority } from 'types';
2
+ import { CourseRunHelper } from 'utils/CourseRunHelper/index';
3
+ import { CourseRunFactoryFromPriority } from 'utils/test/factories/richie';
4
+
5
+ describe('CourseRunHelper', () => {
6
+ it('should return true if all course runs have the same languages', () => {
7
+ const courseRuns: CourseRun[] = [
8
+ CourseRunFactoryFromPriority(Priority.ONGOING_OPEN)({
9
+ languages: ['en'],
10
+ }).one(),
11
+ CourseRunFactoryFromPriority(Priority.ARCHIVED_OPEN)({
12
+ languages: ['en'],
13
+ }).one(),
14
+ CourseRunFactoryFromPriority(Priority.FUTURE_OPEN)({
15
+ languages: ['en'],
16
+ }).one(),
17
+ ];
18
+ expect(CourseRunHelper.IsAllCourseRunsWithSameLanguages(courseRuns)).toEqual(true);
19
+ });
20
+
21
+ it('should return false if all course runs have different languages', () => {
22
+ const courseRuns: CourseRun[] = [
23
+ CourseRunFactoryFromPriority(Priority.ONGOING_OPEN)({
24
+ languages: ['en'],
25
+ }).one(),
26
+ CourseRunFactoryFromPriority(Priority.ARCHIVED_OPEN)({
27
+ languages: ['fr'],
28
+ }).one(),
29
+ CourseRunFactoryFromPriority(Priority.FUTURE_OPEN)({
30
+ languages: ['it'],
31
+ }).one(),
32
+ ];
33
+ expect(CourseRunHelper.IsAllCourseRunsWithSameLanguages(courseRuns)).toEqual(false);
34
+ });
35
+ });
@@ -0,0 +1,13 @@
1
+ import { CourseRun } from 'types';
2
+
3
+ export class CourseRunHelper {
4
+ /**
5
+ * Checks if all given runs of a course have the same languages.
6
+ */
7
+ static IsAllCourseRunsWithSameLanguages = (courseRuns: CourseRun[]) => {
8
+ const languages = courseRuns[0].languages.sort().join();
9
+ return courseRuns
10
+ .map((courseRun) => courseRun.languages.sort().join())
11
+ .every((runLanguages) => runLanguages === languages);
12
+ };
13
+ }
@@ -45,7 +45,7 @@ export function useSessionQuery<
45
45
  }, [user, options?.enabled]);
46
46
 
47
47
  const staleTime = useMemo(() => {
48
- if (options?.staleTime !== undefined && options.staleTime >= 0) {
48
+ if (typeof options?.staleTime === 'number' && options.staleTime >= 0) {
49
49
  return options.staleTime;
50
50
  }
51
51
  return REACT_QUERY_SETTINGS.staleTimes.sessionItems;
@@ -2,7 +2,14 @@ import { faker } from '@faker-js/faker';
2
2
  import { User } from 'types/User';
3
3
  import { APIBackend } from 'types/api';
4
4
  import { CommonDataProps } from 'types/commonDataProps';
5
- import { CourseRun, CourseRunDisplayMode, CourseState, CourseStateTextEnum, Priority } from 'types';
5
+ import {
6
+ CourseRun,
7
+ CourseRunDisplayMode,
8
+ CourseState,
9
+ CourseStateTextEnum,
10
+ PacedCourse,
11
+ Priority,
12
+ } from 'types';
6
13
  import { Course } from 'types/Course';
7
14
  import { FactoryHelper } from 'utils/test/factories/helper';
8
15
  import { factory } from './factories';
@@ -54,6 +61,14 @@ export const CourseRunFactory = factory<CourseRun>(() => {
54
61
  };
55
62
  });
56
63
 
64
+ export const PacedCourseFactory = factory((): PacedCourse => {
65
+ return {
66
+ id: faker.string.uuid(),
67
+ code: faker.string.alphanumeric(5),
68
+ is_self_paced: false,
69
+ };
70
+ });
71
+
57
72
  export const CourseRunFactoryFromPriority = (priority: Priority) => {
58
73
  return factory<CourseRun>(() => {
59
74
  let courseRun = CourseRunFactory().one();
@@ -152,7 +167,6 @@ export const RichieContextFactory = factory<CommonDataProps['context']>(() => ({
152
167
  backend: APIBackend.OPENEDX_HAWTHORN,
153
168
  endpoint: 'https://endpoint.test',
154
169
  },
155
- csrftoken: faker.string.alphanumeric(64),
156
170
  environment: 'test',
157
171
  features: {},
158
172
  lms_backends: [
@@ -1,6 +1,7 @@
1
1
  import { FormattedMessage, defineMessages } from 'react-intl';
2
2
  import PurchaseButton from 'components/PurchaseButton';
3
- import { CourseLight, CourseProductRelation, CredentialProduct, OrderGroup } from 'types/Joanie';
3
+ import { CourseProductRelation, CredentialProduct, OrderGroup } from 'types/Joanie';
4
+ import { PacedCourse } from 'types';
4
5
 
5
6
  const messages = defineMessages({
6
7
  noSeatsAvailable: {
@@ -22,7 +23,7 @@ other {# remaining seats}
22
23
  });
23
24
 
24
25
  interface CourseProductItemFooterProps {
25
- course: CourseLight;
26
+ course: PacedCourse;
26
27
  courseProductRelation: CourseProductRelation;
27
28
  canPurchase: boolean;
28
29
  orderGroups: OrderGroup[];