richie-education 3.1.3-dev3 → 3.1.3-dev30

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 (93) hide show
  1. package/.storybook/__mocks__/utils/context.ts +4 -0
  2. package/js/api/joanie.ts +8 -8
  3. package/js/components/ContractFrame/OrganizationContractFrame.spec.tsx +11 -19
  4. package/js/components/ContractFrame/OrganizationContractFrame.tsx +4 -4
  5. package/js/components/CourseGlimpse/CourseGlimpseFooter.tsx +30 -5
  6. package/js/components/CourseGlimpse/index.spec.tsx +18 -0
  7. package/js/components/CourseGlimpse/index.stories.tsx +75 -4
  8. package/js/components/CourseGlimpse/index.tsx +4 -0
  9. package/js/components/CourseGlimpse/utils.ts +35 -30
  10. package/js/components/CourseGlimpseList/utils.ts +2 -2
  11. package/js/components/PurchaseButton/index.tsx +3 -3
  12. package/js/components/SaleTunnel/CredentialSaleTunnel/index.tsx +1 -3
  13. package/js/components/SaleTunnel/GenericSaleTunnel.tsx +13 -1
  14. package/js/components/SaleTunnel/SaleTunnelInformation/index.tsx +9 -7
  15. package/js/components/SaleTunnel/SubscriptionButton/index.tsx +1 -2
  16. package/js/components/SaleTunnel/index.credential.spec.tsx +5 -19
  17. package/js/components/SaleTunnel/index.full-process.spec.tsx +3 -3
  18. package/js/components/SaleTunnel/index.spec.tsx +171 -29
  19. package/js/components/SaleTunnel/index.stories.tsx +17 -3
  20. package/js/components/SaleTunnel/index.tsx +2 -2
  21. package/js/components/TeacherDashboardCourseList/index.spec.tsx +3 -3
  22. package/js/components/TeacherDashboardCourseList/index.tsx +2 -2
  23. package/js/hooks/useContractArchive/index.ts +3 -3
  24. package/js/hooks/useCourseProductUnion/index.spec.tsx +16 -18
  25. package/js/hooks/useCourseProductUnion/index.ts +7 -7
  26. package/js/hooks/useCourseProducts.ts +4 -8
  27. package/js/hooks/useDefaultOrganizationId/index.tsx +4 -7
  28. package/js/hooks/useOffering/index.ts +32 -0
  29. package/js/hooks/useTeacherCoursesSearch/index.tsx +2 -2
  30. package/js/hooks/useTeacherPendingContractsCount/index.ts +4 -4
  31. package/js/pages/DashboardCourses/index.spec.tsx +14 -14
  32. package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.spec.tsx +11 -14
  33. package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.tsx +4 -9
  34. package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.spec.tsx +11 -11
  35. package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.timer.spec.tsx +10 -13
  36. package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.tsx +4 -4
  37. package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.spec.tsx +20 -28
  38. package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.tsx +8 -11
  39. package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.spec.tsx +6 -6
  40. package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.tsx +4 -4
  41. package/js/pages/TeacherDashboardContractsLayout/hooks/useCheckContractArchiveExists/index.spec.tsx +7 -7
  42. package/js/pages/TeacherDashboardContractsLayout/hooks/useCheckContractArchiveExists/index.tsx +5 -5
  43. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/contractArchiveLocalStorage.spec.ts +21 -28
  44. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/contractArchiveLocalStorage.ts +13 -17
  45. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.spec.tsx +11 -13
  46. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.tsx +6 -6
  47. package/js/pages/TeacherDashboardContractsLayout/hooks/useHasContractToDownload/index.tsx +3 -3
  48. package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractFilters/index.spec.tsx +16 -16
  49. package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractFilters/index.tsx +4 -4
  50. package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractsToSign.tsx +4 -4
  51. package/js/pages/TeacherDashboardCourseLearnersLayout/hooks/useCourseLearnersFilters/index.spec.tsx +21 -21
  52. package/js/pages/TeacherDashboardCourseLearnersLayout/hooks/useCourseLearnersFilters/index.ts +5 -10
  53. package/js/pages/TeacherDashboardCourseLearnersLayout/index.spec.tsx +61 -79
  54. package/js/pages/TeacherDashboardCourseLearnersLayout/index.tsx +1 -1
  55. package/js/pages/TeacherDashboardCoursesLoader/index.spec.tsx +11 -11
  56. package/js/pages/TeacherDashboardOrganizationCourseLoader/index.spec.tsx +11 -11
  57. package/js/pages/TeacherDashboardTraining/TeacherDashboardTrainingLoader.tsx +7 -7
  58. package/js/pages/TeacherDashboardTraining/index.spec.tsx +21 -29
  59. package/js/pages/TeacherDashboardTraining/index.tsx +12 -16
  60. package/js/types/Course.ts +4 -0
  61. package/js/types/Joanie.ts +36 -29
  62. package/js/types/index.ts +6 -2
  63. package/js/utils/ProductHelper/index.ts +1 -5
  64. package/js/utils/test/factories/joanie.ts +19 -25
  65. package/js/utils/test/factories/richie.ts +10 -2
  66. package/js/utils/test/mockCourseProductWithOrder.ts +4 -4
  67. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/DashboardItemEnrollment.tsx +1 -1
  68. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.spec.tsx +1 -1
  69. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +3 -3
  70. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.useUnionResource.cache.spec.tsx +1 -1
  71. package/js/widgets/Dashboard/components/DashboardItem/Order/Installment/index.tsx +4 -4
  72. package/js/widgets/Dashboard/components/DashboardItem/stories.mock.ts +1 -1
  73. package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.spec.tsx +23 -28
  74. package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.tsx +4 -8
  75. package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/index.spec.tsx +17 -24
  76. package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/index.tsx +18 -21
  77. package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/utils.ts +4 -4
  78. package/js/widgets/Dashboard/components/TeacherDashboardOrganizationSidebar/index.tsx +3 -7
  79. package/js/widgets/Dashboard/utils/teacherDashboardPaths.tsx +4 -4
  80. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/CourseProductItemFooter/index.tsx +19 -34
  81. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/_styles.scss +35 -8
  82. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/CourseRunList.tsx +3 -3
  83. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/components/CourseProductCourseRuns/_styles.scss +9 -0
  84. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +186 -140
  85. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +11 -2
  86. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +111 -24
  87. package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRun/index.stories.tsx +81 -0
  88. package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRun/index.tsx +14 -0
  89. package/js/widgets/SyllabusCourseRunsList/components/SyllabusCourseRunCompacted/index.tsx +14 -0
  90. package/js/widgets/SyllabusCourseRunsList/index.spec.tsx +54 -8
  91. package/package.json +1 -1
  92. package/scss/objects/_course_glimpses.scss +16 -0
  93. package/js/hooks/useCourseProductRelation/index.ts +0 -44
@@ -1,7 +1,7 @@
1
1
  import fetchMock from 'fetch-mock';
2
2
  import { renderHook, waitFor, act } from '@testing-library/react';
3
3
  import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
4
- import { CourseProductRelationFactory, OrganizationFactory } from 'utils/test/factories/joanie';
4
+ import { OfferingFactory, OrganizationFactory } from 'utils/test/factories/joanie';
5
5
  import { JoanieAppWrapper, setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
6
6
  import useCourseLearnersFilters from '.';
7
7
 
@@ -32,12 +32,12 @@ describe('useCourseLearnersFilters', () => {
32
32
  expect(result.current.initialFilters).toStrictEqual({
33
33
  organization_id: undefined,
34
34
  course_id: undefined,
35
- course_product_relation_id: undefined,
35
+ offering_id: undefined,
36
36
  });
37
37
  expect(result.current.filters).toStrictEqual({
38
38
  organization_id: undefined,
39
39
  course_id: undefined,
40
- course_product_relation_id: undefined,
40
+ offering_id: undefined,
41
41
  });
42
42
  });
43
43
  });
@@ -46,7 +46,7 @@ describe('useCourseLearnersFilters', () => {
46
46
  const defaultOrganization = OrganizationFactory().one();
47
47
  const filteredOrganization = OrganizationFactory({ id: 'filtered' }).one();
48
48
  const routeOrganization = OrganizationFactory({ id: 'route' }).one();
49
- const routeCourseProductRelation = CourseProductRelationFactory().one();
49
+ const routeOffering = OfferingFactory().one();
50
50
  // fetching user's organizations to initialize default organizationId.
51
51
  fetchMock.get('https://joanie.endpoint/api/v1.0/organizations/', [
52
52
  defaultOrganization,
@@ -56,9 +56,9 @@ describe('useCourseLearnersFilters', () => {
56
56
  wrapper: ({ children }) => (
57
57
  <JoanieAppWrapper
58
58
  routerOptions={{
59
- path: '/:organizationId/:courseId/:courseProductRelationId',
59
+ path: '/:organizationId/:courseId/:offeringId',
60
60
  initialEntries: [
61
- `/${routeOrganization.id}/${routeCourseProductRelation.course.id}/${routeCourseProductRelation.id}?organization_id=${filteredOrganization.id}`,
61
+ `/${routeOrganization.id}/${routeOffering.course.id}/${routeOffering.id}?organization_id=${filteredOrganization.id}`,
62
62
  ],
63
63
  }}
64
64
  >
@@ -70,13 +70,13 @@ describe('useCourseLearnersFilters', () => {
70
70
  await waitFor(() => {
71
71
  expect(result.current.initialFilters).toStrictEqual({
72
72
  organization_id: routeOrganization.id,
73
- course_id: routeCourseProductRelation.course.id,
74
- course_product_relation_id: routeCourseProductRelation.id,
73
+ course_id: routeOffering.course.id,
74
+ offering_id: routeOffering.id,
75
75
  });
76
76
  expect(result.current.filters).toStrictEqual({
77
77
  organization_id: routeOrganization.id,
78
- course_id: routeCourseProductRelation.course.id,
79
- course_product_relation_id: routeCourseProductRelation.id,
78
+ course_id: routeOffering.course.id,
79
+ offering_id: routeOffering.id,
80
80
  });
81
81
  });
82
82
  });
@@ -84,7 +84,7 @@ describe('useCourseLearnersFilters', () => {
84
84
  it("should use organizationId from query parameters when it's not in route params", async () => {
85
85
  const defaultOrganization = OrganizationFactory().one();
86
86
  const filteredOrganization = OrganizationFactory({ id: 'filtered' }).one();
87
- const routeCourseProductRelation = CourseProductRelationFactory({ id: 'route' }).one();
87
+ const routeOffering = OfferingFactory({ id: 'route' }).one();
88
88
  // fetching user's organizations to initialize default organizationId.
89
89
  fetchMock.get('https://joanie.endpoint/api/v1.0/organizations/', [
90
90
  defaultOrganization,
@@ -94,9 +94,9 @@ describe('useCourseLearnersFilters', () => {
94
94
  wrapper: ({ children }) => (
95
95
  <JoanieAppWrapper
96
96
  routerOptions={{
97
- path: '/:courseId/:courseProductRelationId',
97
+ path: '/:courseId/:offeringId',
98
98
  initialEntries: [
99
- `/${routeCourseProductRelation.course.id}/${routeCourseProductRelation.id}/?organization_id=${filteredOrganization.id}`,
99
+ `/${routeOffering.course.id}/${routeOffering.id}/?organization_id=${filteredOrganization.id}`,
100
100
  ],
101
101
  }}
102
102
  >
@@ -108,13 +108,13 @@ describe('useCourseLearnersFilters', () => {
108
108
  await waitFor(() => {
109
109
  expect(result.current.initialFilters).toStrictEqual({
110
110
  organization_id: filteredOrganization.id,
111
- course_id: routeCourseProductRelation.course.id,
112
- course_product_relation_id: routeCourseProductRelation.id,
111
+ course_id: routeOffering.course.id,
112
+ offering_id: routeOffering.id,
113
113
  });
114
114
  expect(result.current.filters).toStrictEqual({
115
115
  organization_id: filteredOrganization.id,
116
- course_id: routeCourseProductRelation.course.id,
117
- course_product_relation_id: routeCourseProductRelation.id,
116
+ course_id: routeOffering.course.id,
117
+ offering_id: routeOffering.id,
118
118
  });
119
119
  });
120
120
  });
@@ -122,7 +122,7 @@ describe('useCourseLearnersFilters', () => {
122
122
  it('setFilters should update filter state', async () => {
123
123
  const defaultOrganization = OrganizationFactory({ id: 'all' }).one();
124
124
  const routeOrganization = OrganizationFactory({ id: 'route' }).one();
125
- const routeCourseProductRelation = CourseProductRelationFactory().one();
125
+ const routeOffering = OfferingFactory().one();
126
126
  // fetching user's organizations to initialize default organizationId.
127
127
  fetchMock.get('https://joanie.endpoint/api/v1.0/organizations/', [defaultOrganization]);
128
128
  const { result } = renderHook(useCourseLearnersFilters, {
@@ -136,7 +136,7 @@ describe('useCourseLearnersFilters', () => {
136
136
  const expectedInitialFilters = {
137
137
  organization_id: defaultOrganization.id,
138
138
  course_id: undefined,
139
- course_product_relation_id: undefined,
139
+ offering_id: undefined,
140
140
  };
141
141
  await waitFor(() => {
142
142
  expect(result.current.initialFilters).toStrictEqual(expectedInitialFilters);
@@ -144,8 +144,8 @@ describe('useCourseLearnersFilters', () => {
144
144
 
145
145
  const newFilters = {
146
146
  organization_id: routeOrganization.id,
147
- course_id: routeCourseProductRelation.course.id,
148
- course_product_relation_id: routeCourseProductRelation.id,
147
+ course_id: routeOffering.course.id,
148
+ offering_id: routeOffering.id,
149
149
  };
150
150
  act(() => {
151
151
  result.current.setFilters(newFilters);
@@ -1,27 +1,22 @@
1
1
  import { useEffect, useMemo, useState } from 'react';
2
2
  import { useParams, useSearchParams } from 'react-router';
3
3
  import useDefaultOrganizationId from 'hooks/useDefaultOrganizationId';
4
- import {
5
- CourseListItem,
6
- CourseOrderResourceQuery,
7
- CourseProductRelation,
8
- Organization,
9
- } from 'types/Joanie';
4
+ import { CourseListItem, CourseOrderResourceQuery, Offering, Organization } from 'types/Joanie';
10
5
 
11
6
  export type CourseLearnersParams = {
12
7
  courseId: CourseListItem['id'];
13
- courseProductRelationId?: CourseProductRelation['id'];
8
+ offeringId?: Offering['id'];
14
9
  organizationId?: Organization['id'];
15
10
  };
16
11
 
17
12
  const useCourseLearnersFilters = () => {
18
- const { courseId, courseProductRelationId } = useParams<CourseLearnersParams>();
13
+ const { courseId, offeringId } = useParams<CourseLearnersParams>();
19
14
  const [searchParams] = useSearchParams();
20
15
  const searchFilters: CourseOrderResourceQuery = useMemo(() => {
21
16
  return {
22
17
  course_id: courseId,
23
18
  organization_id: searchParams.get('organization_id') || undefined,
24
- course_product_relation_id: searchParams.get('course_product_relation_id') || undefined,
19
+ offering_id: searchParams.get('offering_id') || undefined,
25
20
  };
26
21
  }, Array.from(searchParams.entries()));
27
22
 
@@ -32,7 +27,7 @@ const useCourseLearnersFilters = () => {
32
27
  return {
33
28
  ...searchFilters,
34
29
  organization_id: defaultOrganizationId,
35
- course_product_relation_id: courseProductRelationId,
30
+ offering_id: offeringId,
36
31
  };
37
32
  }, [defaultOrganizationId]);
38
33
  const [filters, setFilters] = useState<CourseOrderResourceQuery>(initialFilters);
@@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event';
4
4
  import queryString from 'query-string';
5
5
  import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
6
6
  import {
7
- CourseProductRelationFactory,
7
+ OfferingFactory,
8
8
  NestedCourseOrderFactory,
9
9
  OrganizationFactory,
10
10
  } from 'utils/test/factories/joanie';
@@ -49,47 +49,44 @@ describe('pages/TeacherDashboardCourseLearnersLayout', () => {
49
49
  organizationFilterShouldBeDisplayed: true,
50
50
  },
51
51
  ])('$expectedLabel', async ({ nbOrganization, organizationFilterShouldBeDisplayed }) => {
52
- const courseProductRelation = CourseProductRelationFactory().one();
52
+ const offering = OfferingFactory().one();
53
53
  const organizationList = OrganizationFactory().many(nbOrganization);
54
54
  fetchMock.get(
55
- `https://joanie.endpoint/api/v1.0/organizations/?course_product_relation_id=${courseProductRelation.id}`,
55
+ `https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`,
56
56
  organizationList,
57
57
  );
58
58
 
59
59
  // Course sidebar query
60
- fetchMock.get(
61
- `https://joanie.endpoint/api/v1.0/course-product-relations/${courseProductRelation.id}/`,
62
- {},
63
- );
60
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/offerings/${offering.id}/`, {});
64
61
 
65
62
  // First request before finding default organizationId
66
63
  const courseOrderListQueryParams = {
67
- course_product_relation_id: courseProductRelation.id,
64
+ offering_id: offering.id,
68
65
  page: 1,
69
66
  page_size: PER_PAGE.courseLearnerList,
70
67
  };
71
68
  fetchMock.get(
72
- `https://joanie.endpoint/api/v1.0/courses/${courseProductRelation.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
69
+ `https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
73
70
  [],
74
71
  );
75
72
 
76
73
  if (organizationList.length > 0) {
77
74
  // Course sidebar query
78
75
  fetchMock.get(
79
- `https://joanie.endpoint/api/v1.0/organizations/${organizationList[0].id}/contracts/?course_product_relation_id=${courseProductRelation.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
76
+ `https://joanie.endpoint/api/v1.0/organizations/${organizationList[0].id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
80
77
  [],
81
78
  );
82
79
 
83
80
  // Second request when default organizationId is fetched
84
81
  fetchMock.get(
85
- `https://joanie.endpoint/api/v1.0/courses/${courseProductRelation.course.id}/orders/?${queryString.stringify({ organization_id: organizationList[0].id, ...courseOrderListQueryParams }, { sort: false })}`,
82
+ `https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify({ organization_id: organizationList[0].id, ...courseOrderListQueryParams }, { sort: false })}`,
86
83
  [],
87
84
  );
88
85
  }
89
86
  render(<TeacherDashboardCourseLearnersLayout />, {
90
87
  routerOptions: {
91
- path: '/:courseId/:courseProductRelationId',
92
- initialEntries: [`/${courseProductRelation.course.id}/${courseProductRelation.id}`],
88
+ path: '/:courseId/:offeringId',
89
+ initialEntries: [`/${offering.course.id}/${offering.id}`],
93
90
  },
94
91
  });
95
92
 
@@ -112,45 +109,42 @@ describe('pages/TeacherDashboardCourseLearnersLayout', () => {
112
109
  });
113
110
 
114
111
  it('should call onFiltersChange on organization filter change', async () => {
115
- const courseProductRelation = CourseProductRelationFactory().one();
112
+ const offering = OfferingFactory().one();
116
113
  const defaultOrganization = OrganizationFactory().one();
117
114
  const otherOrganization = OrganizationFactory().one();
118
115
  const organizationList = [defaultOrganization, otherOrganization];
119
116
 
120
117
  // Course sidebar queries
121
118
  fetchMock.get(
122
- `https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?course_product_relation_id=${courseProductRelation.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
119
+ `https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
123
120
  [],
124
121
  );
125
- fetchMock.get(
126
- `https://joanie.endpoint/api/v1.0/course-product-relations/${courseProductRelation.id}/`,
127
- {},
128
- );
122
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/offerings/${offering.id}/`, {});
129
123
 
130
124
  fetchMock.get(
131
- `https://joanie.endpoint/api/v1.0/organizations/?course_product_relation_id=${courseProductRelation.id}`,
125
+ `https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`,
132
126
  organizationList,
133
127
  );
134
128
  // First request before finding default organizationId
135
129
  const courseOrderListQueryParams = {
136
- course_product_relation_id: courseProductRelation.id,
130
+ offering_id: offering.id,
137
131
  page: 1,
138
132
  page_size: PER_PAGE.courseLearnerList,
139
133
  };
140
134
  fetchMock.get(
141
- `https://joanie.endpoint/api/v1.0/courses/${courseProductRelation.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
135
+ `https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
142
136
  [],
143
137
  );
144
138
  // Second request when default organizationId is fetched
145
139
  fetchMock.get(
146
- `https://joanie.endpoint/api/v1.0/courses/${courseProductRelation.course.id}/orders/?${queryString.stringify({ organization_id: defaultOrganization.id, ...courseOrderListQueryParams }, { sort: false })}`,
140
+ `https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify({ organization_id: defaultOrganization.id, ...courseOrderListQueryParams }, { sort: false })}`,
147
141
  [],
148
142
  );
149
143
 
150
144
  render(<TeacherDashboardCourseLearnersLayout />, {
151
145
  routerOptions: {
152
- path: '/:courseId/:courseProductRelationId',
153
- initialEntries: [`/${courseProductRelation.course.id}/${courseProductRelation.id}`],
146
+ path: '/:courseId/:offeringId',
147
+ initialEntries: [`/${offering.course.id}/${offering.id}`],
154
148
  },
155
149
  });
156
150
 
@@ -164,60 +158,57 @@ describe('pages/TeacherDashboardCourseLearnersLayout', () => {
164
158
  const optionToSelect = screen.getByRole('option', { name: organizationList[1].title });
165
159
 
166
160
  fetchMock.get(
167
- `https://joanie.endpoint/api/v1.0/courses/${courseProductRelation.course.id}/orders/?${queryString.stringify({ organization_id: otherOrganization.id, ...courseOrderListQueryParams }, { sort: false })}`,
161
+ `https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify({ organization_id: otherOrganization.id, ...courseOrderListQueryParams }, { sort: false })}`,
168
162
  [],
169
163
  );
170
164
  await user.click(optionToSelect);
171
165
  // onload default value is undefine and is onFiltersChange called once
172
166
  expect(
173
167
  fetchMock.called(
174
- `https://joanie.endpoint/api/v1.0/courses/${courseProductRelation.course.id}/orders/?${queryString.stringify({ organization_id: otherOrganization.id, ...courseOrderListQueryParams }, { sort: false })}`,
168
+ `https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify({ organization_id: otherOrganization.id, ...courseOrderListQueryParams }, { sort: false })}`,
175
169
  ),
176
170
  ).toBe(true);
177
171
  });
178
172
 
179
- it('should render a list of course learners for a course product relation', async () => {
173
+ it('should render a list of course learners for an offering', async () => {
180
174
  const defaultOrganization = OrganizationFactory().one();
181
175
  const otherOrganization = OrganizationFactory().one();
182
176
  const organizationList = [defaultOrganization, otherOrganization];
183
- const courseProductRelation = CourseProductRelationFactory().one();
177
+ const offering = OfferingFactory().one();
184
178
  const courseOrderList = NestedCourseOrderFactory().many(3);
185
179
 
186
180
  // Course sidebar queries
187
181
  fetchMock.get(
188
- `https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?course_product_relation_id=${courseProductRelation.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
182
+ `https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
189
183
  [],
190
184
  );
191
- fetchMock.get(
192
- `https://joanie.endpoint/api/v1.0/course-product-relations/${courseProductRelation.id}/`,
193
- {},
194
- );
185
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/offerings/${offering.id}/`, {});
195
186
 
196
187
  fetchMock.get(
197
- `https://joanie.endpoint/api/v1.0/organizations/?course_product_relation_id=${courseProductRelation.id}`,
188
+ `https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`,
198
189
  organizationList,
199
190
  );
200
191
 
201
192
  // First request before finding default organizationId
202
193
  const courseOrderListQueryParams = {
203
- course_product_relation_id: courseProductRelation.id,
194
+ offering_id: offering.id,
204
195
  page: 1,
205
196
  page_size: PER_PAGE.courseLearnerList,
206
197
  };
207
198
  fetchMock.get(
208
- `https://joanie.endpoint/api/v1.0/courses/${courseProductRelation.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
199
+ `https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
209
200
  courseOrderList,
210
201
  );
211
202
  // Second request when default organizationId is fetched
212
203
  fetchMock.get(
213
- `https://joanie.endpoint/api/v1.0/courses/${courseProductRelation.course.id}/orders/?${queryString.stringify({ organization_id: defaultOrganization.id, ...courseOrderListQueryParams }, { sort: false })}`,
204
+ `https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify({ organization_id: defaultOrganization.id, ...courseOrderListQueryParams }, { sort: false })}`,
214
205
  courseOrderList,
215
206
  );
216
207
 
217
208
  render(<TeacherDashboardCourseLearnersLayout />, {
218
209
  routerOptions: {
219
- path: '/:courseId/:courseProductRelationId',
220
- initialEntries: [`/${courseProductRelation.course.id}/${courseProductRelation.id}`],
210
+ path: '/:courseId/:offeringId',
211
+ initialEntries: [`/${offering.course.id}/${offering.id}`],
221
212
  },
222
213
  });
223
214
 
@@ -239,42 +230,39 @@ describe('pages/TeacherDashboardCourseLearnersLayout', () => {
239
230
 
240
231
  it('should render a list of course learners for an organization', async () => {
241
232
  const organization = OrganizationFactory().one();
242
- const courseProductRelation = CourseProductRelationFactory().one();
233
+ const offering = OfferingFactory().one();
243
234
  const courseOrderList = NestedCourseOrderFactory().many(3);
244
235
 
245
236
  // Course sidebar queries
246
237
  fetchMock.get(
247
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?course_product_relation_id=${courseProductRelation.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
238
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
248
239
  [],
249
240
  );
250
241
  fetchMock.get(
251
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/course-product-relations/${courseProductRelation.id}/`,
242
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offerings/${offering.id}/`,
252
243
  {},
253
244
  );
254
245
 
255
246
  // before default organization's fetched, we query all organization to decide if we should display organization filter or not.
256
- fetchMock.get(
257
- `https://joanie.endpoint/api/v1.0/organizations/?course_product_relation_id=${courseProductRelation.id}`,
258
- [organization],
259
- );
247
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`, [
248
+ organization,
249
+ ]);
260
250
 
261
251
  const courseOrderListQueryParams = {
262
252
  organization_id: organization.id,
263
- course_product_relation_id: courseProductRelation.id,
253
+ offering_id: offering.id,
264
254
  page: 1,
265
255
  page_size: PER_PAGE.courseLearnerList,
266
256
  };
267
257
  fetchMock.get(
268
- `https://joanie.endpoint/api/v1.0/courses/${courseProductRelation.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
258
+ `https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
269
259
  courseOrderList,
270
260
  );
271
261
 
272
262
  render(<TeacherDashboardCourseLearnersLayout />, {
273
263
  routerOptions: {
274
- path: '/:organizationId/:courseId/:courseProductRelationId',
275
- initialEntries: [
276
- `/${organization.id}/${courseProductRelation.course.id}/${courseProductRelation.id}`,
277
- ],
264
+ path: '/:organizationId/:courseId/:offeringId',
265
+ initialEntries: [`/${organization.id}/${offering.course.id}/${offering.id}`],
278
266
  },
279
267
  });
280
268
 
@@ -294,41 +282,38 @@ describe('pages/TeacherDashboardCourseLearnersLayout', () => {
294
282
 
295
283
  it('should render an empty table if there are no course learners', async () => {
296
284
  const organization = OrganizationFactory().one();
297
- const courseProductRelation = CourseProductRelationFactory().one();
285
+ const offering = OfferingFactory().one();
298
286
 
299
287
  // Course sidebar queries
300
288
  fetchMock.get(
301
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?course_product_relation_id=${courseProductRelation.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
289
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
302
290
  [],
303
291
  );
304
292
  fetchMock.get(
305
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/course-product-relations/${courseProductRelation.id}/`,
293
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offerings/${offering.id}/`,
306
294
  {},
307
295
  );
308
296
 
309
297
  // before default organization's fetched, we query all organization to decide if we should display organization filter or not.
310
- fetchMock.get(
311
- `https://joanie.endpoint/api/v1.0/organizations/?course_product_relation_id=${courseProductRelation.id}`,
312
- [organization],
313
- );
298
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`, [
299
+ organization,
300
+ ]);
314
301
 
315
302
  const courseOrderListQueryParams = {
316
303
  organization_id: organization.id,
317
- course_product_relation_id: courseProductRelation.id,
304
+ offering_id: offering.id,
318
305
  page: 1,
319
306
  page_size: PER_PAGE.courseLearnerList,
320
307
  };
321
308
  fetchMock.get(
322
- `https://joanie.endpoint/api/v1.0/courses/${courseProductRelation.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
309
+ `https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
323
310
  [],
324
311
  );
325
312
 
326
313
  render(<TeacherDashboardCourseLearnersLayout />, {
327
314
  routerOptions: {
328
- path: '/:organizationId/:courseId/:courseProductRelationId',
329
- initialEntries: [
330
- `/${organization.id}/${courseProductRelation.course.id}/${courseProductRelation.id}`,
331
- ],
315
+ path: '/:organizationId/:courseId/:offeringId',
316
+ initialEntries: [`/${organization.id}/${offering.course.id}/${offering.id}`],
332
317
  },
333
318
  });
334
319
 
@@ -341,41 +326,38 @@ describe('pages/TeacherDashboardCourseLearnersLayout', () => {
341
326
 
342
327
  it('should render an error banner if an error occured during course learners fetching', async () => {
343
328
  const organization = OrganizationFactory().one();
344
- const courseProductRelation = CourseProductRelationFactory().one();
329
+ const offering = OfferingFactory().one();
345
330
 
346
331
  // Course sidebar queries
347
332
  fetchMock.get(
348
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?course_product_relation_id=${courseProductRelation.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
333
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=${PER_PAGE.teacherContractList}`,
349
334
  [],
350
335
  );
351
336
  fetchMock.get(
352
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/course-product-relations/${courseProductRelation.id}/`,
337
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offerings/${offering.id}/`,
353
338
  {},
354
339
  );
355
340
 
356
341
  // before default organization's fetched, we query all organization to decide if we should display organization filter or not.
357
- fetchMock.get(
358
- `https://joanie.endpoint/api/v1.0/organizations/?course_product_relation_id=${courseProductRelation.id}`,
359
- [organization],
360
- );
342
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`, [
343
+ organization,
344
+ ]);
361
345
 
362
346
  const courseOrderListQueryParams = {
363
347
  organization_id: organization.id,
364
- course_product_relation_id: courseProductRelation.id,
348
+ offering_id: offering.id,
365
349
  page: 1,
366
350
  page_size: PER_PAGE.courseLearnerList,
367
351
  };
368
352
  fetchMock.get(
369
- `https://joanie.endpoint/api/v1.0/courses/${courseProductRelation.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
353
+ `https://joanie.endpoint/api/v1.0/courses/${offering.course.id}/orders/?${queryString.stringify(courseOrderListQueryParams, { sort: false })}`,
370
354
  new Response('', { status: HttpStatusCode.NOT_FOUND }),
371
355
  );
372
356
 
373
357
  render(<TeacherDashboardCourseLearnersLayout />, {
374
358
  routerOptions: {
375
- path: '/:organizationId/:courseId/:courseProductRelationId',
376
- initialEntries: [
377
- `/${organization.id}/${courseProductRelation.course.id}/${courseProductRelation.id}`,
378
- ],
359
+ path: '/:organizationId/:courseId/:offeringId',
360
+ initialEntries: [`/${organization.id}/${offering.course.id}/${offering.id}`],
379
361
  },
380
362
  });
381
363
 
@@ -65,7 +65,7 @@ export const TeacherDashboardCourseLearnersLayout = () => {
65
65
  const {
66
66
  items: organizations,
67
67
  states: { isFetched: isOrganizationFetched },
68
- } = useOrganizations({ course_product_relation_id: filters.course_product_relation_id });
68
+ } = useOrganizations({ offering_id: filters.offering_id });
69
69
  const {
70
70
  items: courseOrders,
71
71
  meta,
@@ -2,7 +2,7 @@ import { screen, waitFor } from '@testing-library/react';
2
2
  import fetchMock from 'fetch-mock';
3
3
  import userEvent from '@testing-library/user-event';
4
4
  import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
5
- import { CourseListItemFactory, CourseProductRelationFactory } from 'utils/test/factories/joanie';
5
+ import { CourseListItemFactory, OfferingFactory } from 'utils/test/factories/joanie';
6
6
  import { expectNoSpinner } from 'utils/test/expectSpinner';
7
7
  import { mockPaginatedResponse } from 'utils/test/mockPaginatedResponse';
8
8
  import { PER_PAGE } from 'settings';
@@ -48,19 +48,19 @@ describe('components/TeacherDashboardCoursesLoader', () => {
48
48
  mockPaginatedResponse(CourseListItemFactory().many(15), 15, false),
49
49
  );
50
50
  fetchMock.get(
51
- `https://joanie.endpoint/api/v1.0/course-product-relations/?product_type=credential&page=1&page_size=${perPage}`,
52
- mockPaginatedResponse(CourseProductRelationFactory().many(15), 15, false),
51
+ `https://joanie.endpoint/api/v1.0/offerings/?product_type=credential&page=1&page_size=${perPage}`,
52
+ mockPaginatedResponse(OfferingFactory().many(15), 15, false),
53
53
  );
54
54
 
55
55
  render(<TeacherDashboardCoursesLoader />);
56
56
  await expectNoSpinner('Loading courses...');
57
57
 
58
58
  nbApiCalls += 1; // course api call
59
- nbApiCalls += 1; // course-product-relations api call
59
+ nbApiCalls += 1; // offerings api call
60
60
  const calledUrls = fetchMock.calls().map((call) => call[0]);
61
61
  expect(calledUrls).toHaveLength(nbApiCalls);
62
62
  expect(calledUrls).toContain(
63
- `https://joanie.endpoint/api/v1.0/course-product-relations/?product_type=credential&page=1&page_size=${perPage}`,
63
+ `https://joanie.endpoint/api/v1.0/offerings/?product_type=credential&page=1&page_size=${perPage}`,
64
64
  );
65
65
 
66
66
  // section titles
@@ -80,8 +80,8 @@ describe('components/TeacherDashboardCoursesLoader', () => {
80
80
  mockPaginatedResponse(CourseListItemFactory().many(15), 15, false),
81
81
  );
82
82
  fetchMock.get(
83
- `https://joanie.endpoint/api/v1.0/course-product-relations/?product_type=credential&page=1&page_size=${perPage}`,
84
- mockPaginatedResponse(CourseProductRelationFactory().many(15), 15, false),
83
+ `https://joanie.endpoint/api/v1.0/offerings/?product_type=credential&page=1&page_size=${perPage}`,
84
+ mockPaginatedResponse(OfferingFactory().many(15), 15, false),
85
85
  );
86
86
 
87
87
  render(<TeacherDashboardCoursesLoader />);
@@ -93,22 +93,22 @@ describe('components/TeacherDashboardCoursesLoader', () => {
93
93
  mockPaginatedResponse(CourseListItemFactory().many(5), 5, false),
94
94
  );
95
95
  fetchMock.get(
96
- `https://joanie.endpoint/api/v1.0/course-product-relations/?query=text+query&product_type=credential&page=1&page_size=${perPage}`,
97
- mockPaginatedResponse(CourseProductRelationFactory().many(5), 5, false),
96
+ `https://joanie.endpoint/api/v1.0/offerings/?query=text+query&product_type=credential&page=1&page_size=${perPage}`,
97
+ mockPaginatedResponse(OfferingFactory().many(5), 5, false),
98
98
  );
99
99
  const user = userEvent.setup();
100
100
  await user.type(screen.getByRole('textbox', { name: /Search/ }), 'text query');
101
101
  await user.click(screen.getByRole('button', { name: /Search/ }));
102
102
 
103
103
  nbApiCalls = 1; // course api call
104
- nbApiCalls += 1; // course-product-relations api call
104
+ nbApiCalls += 1; // offerings api call
105
105
  const calledUrls = fetchMock.calls().map((call) => call[0]);
106
106
  expect(calledUrls).toHaveLength(nbApiCalls);
107
107
  expect(calledUrls).toContain(
108
108
  `https://joanie.endpoint/api/v1.0/courses/?query=text+query&has_listed_course_runs=true&page=1&page_size=${perPage}`,
109
109
  );
110
110
  expect(calledUrls).toContain(
111
- `https://joanie.endpoint/api/v1.0/course-product-relations/?query=text+query&product_type=credential&page=1&page_size=${perPage}`,
111
+ `https://joanie.endpoint/api/v1.0/offerings/?query=text+query&product_type=credential&page=1&page_size=${perPage}`,
112
112
  );
113
113
 
114
114
  await waitFor(() => {