richie-education 3.1.3-dev11 → 3.1.3-dev12

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 (73) hide show
  1. package/js/api/joanie.ts +8 -8
  2. package/js/components/ContractFrame/OrganizationContractFrame.spec.tsx +11 -20
  3. package/js/components/ContractFrame/OrganizationContractFrame.tsx +4 -4
  4. package/js/components/CourseGlimpse/utils.ts +22 -35
  5. package/js/components/CourseGlimpseList/utils.ts +2 -2
  6. package/js/components/PurchaseButton/index.tsx +3 -3
  7. package/js/components/SaleTunnel/GenericSaleTunnel.tsx +3 -10
  8. package/js/components/SaleTunnel/SaleTunnelInformation/index.tsx +2 -2
  9. package/js/components/SaleTunnel/index.full-process.spec.tsx +3 -3
  10. package/js/components/SaleTunnel/index.spec.tsx +5 -5
  11. package/js/components/SaleTunnel/index.tsx +2 -2
  12. package/js/components/TeacherDashboardCourseList/index.spec.tsx +3 -3
  13. package/js/components/TeacherDashboardCourseList/index.tsx +2 -2
  14. package/js/hooks/useContractArchive/index.ts +3 -3
  15. package/js/hooks/useCourseProductUnion/index.spec.tsx +16 -18
  16. package/js/hooks/useCourseProductUnion/index.ts +7 -7
  17. package/js/hooks/useCourseProducts.ts +4 -8
  18. package/js/hooks/useDefaultOrganizationId/index.tsx +4 -7
  19. package/js/hooks/useOffer/index.ts +32 -0
  20. package/js/hooks/useTeacherCoursesSearch/index.tsx +4 -4
  21. package/js/hooks/useTeacherPendingContractsCount/index.ts +4 -4
  22. package/js/pages/DashboardCourses/index.spec.tsx +14 -17
  23. package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.spec.tsx +8 -14
  24. package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.tsx +4 -12
  25. package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.spec.tsx +11 -11
  26. package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.timer.spec.tsx +10 -13
  27. package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.tsx +4 -4
  28. package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.spec.tsx +20 -28
  29. package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.tsx +8 -11
  30. package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.spec.tsx +6 -6
  31. package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.tsx +4 -4
  32. package/js/pages/TeacherDashboardContractsLayout/hooks/useCheckContractArchiveExists/index.spec.tsx +7 -7
  33. package/js/pages/TeacherDashboardContractsLayout/hooks/useCheckContractArchiveExists/index.tsx +5 -5
  34. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/contractArchiveLocalStorage.spec.ts +21 -28
  35. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/contractArchiveLocalStorage.ts +13 -23
  36. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.spec.tsx +11 -13
  37. package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.tsx +6 -6
  38. package/js/pages/TeacherDashboardContractsLayout/hooks/useHasContractToDownload/index.tsx +3 -6
  39. package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractFilters/index.spec.tsx +16 -16
  40. package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractFilters/index.tsx +4 -4
  41. package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractsToSign.tsx +4 -7
  42. package/js/pages/TeacherDashboardCourseLearnersLayout/hooks/useCourseLearnersFilters/index.spec.tsx +21 -21
  43. package/js/pages/TeacherDashboardCourseLearnersLayout/hooks/useCourseLearnersFilters/index.ts +5 -10
  44. package/js/pages/TeacherDashboardCourseLearnersLayout/index.spec.tsx +61 -79
  45. package/js/pages/TeacherDashboardCourseLearnersLayout/index.tsx +1 -1
  46. package/js/pages/TeacherDashboardCoursesLoader/index.spec.tsx +11 -11
  47. package/js/pages/TeacherDashboardOrganizationCourseLoader/index.spec.tsx +11 -11
  48. package/js/pages/TeacherDashboardTraining/TeacherDashboardTrainingLoader.tsx +7 -7
  49. package/js/pages/TeacherDashboardTraining/index.spec.tsx +25 -33
  50. package/js/pages/TeacherDashboardTraining/index.tsx +12 -20
  51. package/js/types/Joanie.ts +17 -19
  52. package/js/utils/test/factories/joanie.ts +3 -3
  53. package/js/utils/test/mockCourseProductWithOrder.ts +4 -4
  54. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/DashboardItemEnrollment.tsx +1 -1
  55. package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.spec.tsx +1 -1
  56. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +3 -3
  57. package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.useUnionResource.cache.spec.tsx +1 -1
  58. package/js/widgets/Dashboard/components/DashboardItem/Order/Installment/index.tsx +4 -4
  59. package/js/widgets/Dashboard/components/DashboardItem/stories.mock.ts +1 -1
  60. package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.spec.tsx +23 -28
  61. package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.tsx +4 -8
  62. package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/index.spec.tsx +17 -27
  63. package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/index.tsx +16 -25
  64. package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/utils.ts +4 -4
  65. package/js/widgets/Dashboard/components/TeacherDashboardOrganizationSidebar/index.tsx +3 -7
  66. package/js/widgets/Dashboard/utils/teacherDashboardPaths.tsx +4 -4
  67. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/CourseProductItemFooter/index.tsx +9 -13
  68. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +63 -87
  69. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +2 -2
  70. package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +20 -34
  71. package/js/widgets/SyllabusCourseRunsList/index.spec.tsx +8 -8
  72. package/package.json +1 -1
  73. package/js/hooks/useCourseProductRelation/index.ts +0 -44
@@ -4,7 +4,7 @@ import userEvent from '@testing-library/user-event';
4
4
  import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
5
5
  import {
6
6
  CourseListItemFactory,
7
- CourseProductRelationFactory,
7
+ OfferFactory,
8
8
  OrganizationFactory,
9
9
  } from 'utils/test/factories/joanie';
10
10
  import { expectNoSpinner } from 'utils/test/expectSpinner';
@@ -59,8 +59,8 @@ describe('components/TeacherDashboardOrganizationCourseLoader', () => {
59
59
  mockPaginatedResponse(CourseListItemFactory().many(15), 15, false),
60
60
  );
61
61
  fetchMock.get(
62
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/course-product-relations/?product_type=credential&page=1&page_size=${perPage}`,
63
- mockPaginatedResponse(CourseProductRelationFactory().many(15), 15, false),
62
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offers/?product_type=credential&page=1&page_size=${perPage}`,
63
+ mockPaginatedResponse(OfferFactory().many(15), 15, false),
64
64
  );
65
65
  fetchMock.get(
66
66
  `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?signature_state=half_signed&page=1`,
@@ -76,7 +76,7 @@ describe('components/TeacherDashboardOrganizationCourseLoader', () => {
76
76
  await expectNoSpinner('Loading courses...');
77
77
 
78
78
  nbApiCalls += 1; // course api call
79
- nbApiCalls += 1; // course-product-relations api call
79
+ nbApiCalls += 1; // offers api call
80
80
  nbApiCalls += 1; // contracts api call
81
81
  const calledUrls = fetchMock.calls().map((call) => call[0]);
82
82
  expect(calledUrls).toHaveLength(nbApiCalls);
@@ -84,7 +84,7 @@ describe('components/TeacherDashboardOrganizationCourseLoader', () => {
84
84
  `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/courses/?has_listed_course_runs=true&page=1&page_size=${perPage}`,
85
85
  );
86
86
  expect(calledUrls).toContain(
87
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/course-product-relations/?product_type=credential&page=1&page_size=${perPage}`,
87
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offers/?product_type=credential&page=1&page_size=${perPage}`,
88
88
  );
89
89
  await expectNoSpinner('Loading organization...');
90
90
 
@@ -117,8 +117,8 @@ describe('components/TeacherDashboardOrganizationCourseLoader', () => {
117
117
  mockPaginatedResponse(CourseListItemFactory().many(15), 15, false),
118
118
  );
119
119
  fetchMock.get(
120
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/course-product-relations/?product_type=credential&page=1&page_size=${perPage}`,
121
- mockPaginatedResponse(CourseProductRelationFactory().many(15), 15, false),
120
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offers/?product_type=credential&page=1&page_size=${perPage}`,
121
+ mockPaginatedResponse(OfferFactory().many(15), 15, false),
122
122
  );
123
123
 
124
124
  render(<TeacherDashboardOrganizationCourseLoader />, {
@@ -136,22 +136,22 @@ describe('components/TeacherDashboardOrganizationCourseLoader', () => {
136
136
  mockPaginatedResponse(CourseListItemFactory().many(5), 5, false),
137
137
  );
138
138
  fetchMock.get(
139
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/course-product-relations/?query=text+query&product_type=credential&page=1&page_size=${perPage}`,
140
- mockPaginatedResponse(CourseProductRelationFactory().many(5), 5, false),
139
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offers/?query=text+query&product_type=credential&page=1&page_size=${perPage}`,
140
+ mockPaginatedResponse(OfferFactory().many(5), 5, false),
141
141
  );
142
142
  const user = userEvent.setup();
143
143
  await user.type(screen.getByRole('textbox', { name: /Search/ }), 'text query');
144
144
  await user.click(screen.getByRole('button', { name: /Search/ }));
145
145
 
146
146
  nbApiCalls = 1; // course api call
147
- nbApiCalls += 1; // course-product-relations api call
147
+ nbApiCalls += 1; // offers api call
148
148
  const calledUrls = fetchMock.calls().map((call) => call[0]);
149
149
  expect(calledUrls).toHaveLength(nbApiCalls);
150
150
  expect(calledUrls).toContain(
151
151
  `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/courses/?query=text+query&has_listed_course_runs=true&page=1&page_size=${perPage}`,
152
152
  );
153
153
  expect(calledUrls).toContain(
154
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/course-product-relations/?query=text+query&product_type=credential&page=1&page_size=${perPage}`,
154
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offers/?query=text+query&product_type=credential&page=1&page_size=${perPage}`,
155
155
  );
156
156
 
157
157
  await waitFor(() => {
@@ -1,10 +1,10 @@
1
1
  import { FormattedMessage, defineMessages } from 'react-intl';
2
2
  import { useParams } from 'react-router';
3
3
 
4
+ import { useOffer } from 'hooks/useOffer';
4
5
  import { DashboardLayout } from 'widgets/Dashboard/components/DashboardLayout';
5
6
  import { TeacherDashboardCourseSidebar } from 'widgets/Dashboard/components/TeacherDashboardCourseSidebar';
6
7
  import { Spinner } from 'components/Spinner';
7
- import { useCourseProductRelation } from 'hooks/useCourseProductRelation';
8
8
  import { useBreadcrumbsPlaceholders } from 'hooks/useBreadcrumbsPlaceholders';
9
9
  import { TeacherDashboardTraining } from '.';
10
10
 
@@ -22,17 +22,17 @@ const messages = defineMessages({
22
22
  });
23
23
 
24
24
  export const TeacherDashboardTrainingLoader = () => {
25
- const { courseProductRelationId, organizationId } = useParams<{
26
- courseProductRelationId: string;
25
+ const { offerId, organizationId } = useParams<{
26
+ offerId: string;
27
27
  organizationId?: string;
28
28
  }>();
29
29
 
30
30
  const {
31
- item: courseProductRelation,
31
+ item: offer,
32
32
  states: { fetching },
33
- } = useCourseProductRelation(courseProductRelationId, { organization_id: organizationId });
33
+ } = useOffer(offerId, { organization_id: organizationId });
34
34
  useBreadcrumbsPlaceholders({
35
- courseTitle: courseProductRelation?.product.title ?? '',
35
+ courseTitle: offer?.product.title ?? '',
36
36
  });
37
37
  return (
38
38
  <DashboardLayout sidebar={<TeacherDashboardCourseSidebar />}>
@@ -48,7 +48,7 @@ export const TeacherDashboardTrainingLoader = () => {
48
48
  </span>
49
49
  </Spinner>
50
50
  ) : (
51
- <TeacherDashboardTraining courseProductRelation={courseProductRelation} />
51
+ <TeacherDashboardTraining offer={offer} />
52
52
  )}
53
53
  </DashboardLayout>
54
54
  );
@@ -11,7 +11,7 @@ import {
11
11
  UserFactory,
12
12
  } from 'utils/test/factories/richie';
13
13
  import JoanieSessionProvider from 'contexts/SessionContext/JoanieSessionProvider';
14
- import { CourseProductRelationFactory, OrganizationFactory } from 'utils/test/factories/joanie';
14
+ import { OfferFactory, OrganizationFactory } from 'utils/test/factories/joanie';
15
15
  import { createTestQueryClient } from 'utils/test/createTestQueryClient';
16
16
  import { expectNoSpinner } from 'utils/test/expectSpinner';
17
17
  import { DashboardBreadcrumbsProvider } from 'widgets/Dashboard/contexts/DashboardBreadcrumbsContext';
@@ -46,15 +46,9 @@ describe('components/TeacherDashboardTrainingLoader', () => {
46
46
  });
47
47
 
48
48
  it('should render TeacherDashboardTrainingLoader page', async () => {
49
- const courseProductRelation = CourseProductRelationFactory().one();
50
- fetchMock.get(
51
- `https://joanie.endpoint/api/v1.0/organizations/?course_product_relation_id=${courseProductRelation.id}`,
52
- [],
53
- );
54
- fetchMock.get(
55
- `https://joanie.endpoint/api/v1.0/course-product-relations/${courseProductRelation.id}/`,
56
- courseProductRelation,
57
- );
49
+ const offer = OfferFactory().one();
50
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/organizations/?offer_id=${offer.id}`, []);
51
+ fetchMock.get(`https://joanie.endpoint/api/v1.0/offers/${offer.id}/`, offer);
58
52
 
59
53
  const user = UserFactory().one();
60
54
  render(
@@ -67,12 +61,12 @@ describe('components/TeacherDashboardTrainingLoader', () => {
67
61
  router={createMemoryRouter(
68
62
  [
69
63
  {
70
- path: ':courseProductRelationId',
64
+ path: ':offerId',
71
65
  element: <TeacherDashboardTrainingLoader />,
72
66
  },
73
67
  ],
74
68
  {
75
- initialEntries: [`/${courseProductRelation.id}`],
69
+ initialEntries: [`/${offer.id}`],
76
70
  },
77
71
  )}
78
72
  />
@@ -88,12 +82,10 @@ describe('components/TeacherDashboardTrainingLoader', () => {
88
82
  await expectNoSpinner('Loading course...');
89
83
 
90
84
  nbApiCalls += 1; // organizations api call
91
- nbApiCalls += 1; // course-product-relations api call
85
+ nbApiCalls += 1; // offers api call
92
86
  const calledUrls = fetchMock.calls().map((call) => call[0]);
93
87
  expect(calledUrls).toHaveLength(nbApiCalls);
94
- expect(calledUrls).toContain(
95
- `https://joanie.endpoint/api/v1.0/course-product-relations/${courseProductRelation.id}/`,
96
- );
88
+ expect(calledUrls).toContain(`https://joanie.endpoint/api/v1.0/offers/${offer.id}/`);
97
89
 
98
90
  // main titles
99
91
  expect(
@@ -102,28 +94,28 @@ describe('components/TeacherDashboardTrainingLoader', () => {
102
94
  }),
103
95
  ).toBeInTheDocument();
104
96
 
105
- expect(
106
- screen.getAllByRole('heading', { name: capitalize(courseProductRelation.product.title) }),
107
- ).toHaveLength(2);
97
+ expect(screen.getAllByRole('heading', { name: capitalize(offer.product.title) })).toHaveLength(
98
+ 2,
99
+ );
108
100
 
109
- const nbCourseRun = courseProductRelation.product.target_courses.reduce(
101
+ const nbCourseRun = offer.product.target_courses.reduce(
110
102
  (acc, course) => acc + course.course_runs.length,
111
103
  0,
112
104
  );
113
105
  expect(screen.getAllByRole('link', { name: 'Go to course area' })).toHaveLength(nbCourseRun);
114
106
  });
115
107
 
116
- it('should fetch course product relation with organization id if there is one in the path', async () => {
108
+ it('should fetch offer with organization id if there is one in the path', async () => {
117
109
  const organization = OrganizationFactory().one();
118
- const courseProductRelation = CourseProductRelationFactory({
110
+ const offer = OfferFactory({
119
111
  organizations: [organization],
120
112
  }).one();
121
113
  fetchMock.get(
122
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/course-product-relations/${courseProductRelation.id}/`,
123
- courseProductRelation,
114
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offers/${offer.id}/`,
115
+ offer,
124
116
  );
125
117
  fetchMock.get(
126
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?course_product_relation_id=${courseProductRelation.id}&signature_state=half_signed&page=1&page_size=25`,
118
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?offer_id=${offer.id}&signature_state=half_signed&page=1&page_size=25`,
127
119
  [],
128
120
  );
129
121
 
@@ -138,12 +130,12 @@ describe('components/TeacherDashboardTrainingLoader', () => {
138
130
  router={createMemoryRouter(
139
131
  [
140
132
  {
141
- path: '/:organizationId/:courseProductRelationId',
133
+ path: '/:organizationId/:offerId',
142
134
  element: <TeacherDashboardTrainingLoader />,
143
135
  },
144
136
  ],
145
137
  {
146
- initialEntries: [`/${organization.id}/${courseProductRelation.id}`],
138
+ initialEntries: [`/${organization.id}/${offer.id}`],
147
139
  },
148
140
  )}
149
141
  />
@@ -159,11 +151,11 @@ describe('components/TeacherDashboardTrainingLoader', () => {
159
151
  await expectNoSpinner('Loading course...');
160
152
 
161
153
  nbApiCalls += 1; // contracts api call
162
- nbApiCalls += 1; // course-product-relations api call
154
+ nbApiCalls += 1; // offers api call
163
155
  const calledUrls = fetchMock.calls().map((call) => call[0]);
164
156
  expect(calledUrls).toHaveLength(nbApiCalls);
165
157
  expect(calledUrls).toContain(
166
- `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/course-product-relations/${courseProductRelation.id}/`,
158
+ `https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offers/${offer.id}/`,
167
159
  );
168
160
 
169
161
  // main titles
@@ -173,11 +165,11 @@ describe('components/TeacherDashboardTrainingLoader', () => {
173
165
  }),
174
166
  ).toBeInTheDocument();
175
167
 
176
- expect(
177
- screen.getAllByRole('heading', { name: capitalize(courseProductRelation.product.title) }),
178
- ).toHaveLength(2);
168
+ expect(screen.getAllByRole('heading', { name: capitalize(offer.product.title) })).toHaveLength(
169
+ 2,
170
+ );
179
171
 
180
- const nbCourseRun = courseProductRelation.product.target_courses.reduce(
172
+ const nbCourseRun = offer.product.target_courses.reduce(
181
173
  (acc, course) => acc + course.course_runs.length,
182
174
  0,
183
175
  );
@@ -6,37 +6,33 @@ import { DashboardLayout } from 'widgets/Dashboard/components/DashboardLayout';
6
6
  import { DashboardCard } from 'widgets/Dashboard/components/DashboardCard';
7
7
  import { Icon, IconTypeEnum } from 'components/Icon';
8
8
  import Banner, { BannerType } from 'components/Banner';
9
- import { CourseProductRelation } from 'types/Joanie';
9
+ import { Offer } from 'types/Joanie';
10
10
 
11
11
  const messages = defineMessages({
12
- errorNoCourseProductRelation: {
12
+ errorNoOffer: {
13
13
  defaultMessage: "This product doesn't exist",
14
- description: 'Message displayed when requested course product relation is not found',
15
- id: 'components.TeacherDashboardTraining.errorNoCourseProductRelation',
14
+ description: 'Message displayed when requested offer is not found',
15
+ id: 'components.TeacherDashboardTraining.errorNoOffer',
16
16
  },
17
17
  });
18
18
 
19
19
  interface TeacherDashboardTrainingProps {
20
- courseProductRelation: CourseProductRelation;
20
+ offer: Offer;
21
21
  }
22
22
 
23
- export const TeacherDashboardTraining = ({
24
- courseProductRelation,
25
- }: TeacherDashboardTrainingProps) => {
23
+ export const TeacherDashboardTraining = ({ offer }: TeacherDashboardTrainingProps) => {
26
24
  const intl = useIntl();
27
- return courseProductRelation ? (
25
+ return offer ? (
28
26
  <div className="teacher-course-page">
29
27
  <DashboardCard
30
28
  className="icon-arrow-right-rounded"
31
29
  header={
32
30
  <div>
33
31
  <div className="dashboard__title_container--small">
34
- <h2 className="dashboard__title--large">
35
- {capitalize(courseProductRelation.product.title)}
36
- </h2>
32
+ <h2 className="dashboard__title--large">{capitalize(offer.product.title)}</h2>
37
33
  </div>
38
- {courseProductRelation.product.description && (
39
- <div className="dashboard__quote">{courseProductRelation.product.description}</div>
34
+ {offer.product.description && (
35
+ <div className="dashboard__quote">{offer.product.description}</div>
40
36
  )}
41
37
  </div>
42
38
  }
@@ -44,7 +40,7 @@ export const TeacherDashboardTraining = ({
44
40
  fullWidth
45
41
  />
46
42
  <DashboardLayout.NestedSection>
47
- {courseProductRelation.product.target_courses.map((course) => (
43
+ {offer.product.target_courses.map((course) => (
48
44
  <DashboardLayout.Section key={`course_target_${course.code}`}>
49
45
  <DashboardCard
50
46
  className="icon-arrow-right-rounded"
@@ -66,11 +62,7 @@ export const TeacherDashboardTraining = ({
66
62
  </DashboardLayout.NestedSection>
67
63
  </div>
68
64
  ) : (
69
- <Banner
70
- message={intl.formatMessage(messages.errorNoCourseProductRelation)}
71
- type={BannerType.ERROR}
72
- rounded
73
- />
65
+ <Banner message={intl.formatMessage(messages.errorNoOffer)} type={BannerType.ERROR} rounded />
74
66
  );
75
67
  };
76
68
 
@@ -38,7 +38,7 @@ export interface Organization {
38
38
  }
39
39
 
40
40
  export interface OrganizationResourceQuery extends ResourcesQuery {
41
- course_product_relation_id?: CourseProductRelation['id'];
41
+ offer_id?: Offer['id'];
42
42
  }
43
43
 
44
44
  export interface ContractDefinition {
@@ -174,7 +174,7 @@ export interface DefinitionResourcesProduct {
174
174
  contract_definition_id: Nullable<ContractDefinition['id']>;
175
175
  }
176
176
 
177
- export interface CourseProductRelationLight {
177
+ export interface OfferLight {
178
178
  id: string;
179
179
  course: CourseLight;
180
180
  organizations: Organization[];
@@ -182,7 +182,7 @@ export interface CourseProductRelationLight {
182
182
  created_on: string;
183
183
  }
184
184
 
185
- export interface CourseProductRelation extends CourseProductRelationLight {
185
+ export interface Offer extends OfferLight {
186
186
  is_withdrawable: boolean;
187
187
  discounted_price: Nullable<number>;
188
188
  discount_rate: Nullable<number>;
@@ -193,9 +193,7 @@ export interface CourseProductRelation extends CourseProductRelationLight {
193
193
  nb_seats_available: Nullable<number>;
194
194
  seats: Nullable<number>;
195
195
  }
196
- export function isCourseProductRelation(
197
- entity: CourseListItem | CourseProductRelationLight | RichieCourse,
198
- ): entity is CourseProductRelationLight {
196
+ export function isOffer(entity: CourseListItem | OfferLight | RichieCourse): entity is OfferLight {
199
197
  return 'course' in entity && 'product' in entity;
200
198
  }
201
199
 
@@ -240,7 +238,7 @@ export interface Enrollment {
240
238
  was_created_by_order: boolean;
241
239
  created_on: string;
242
240
  orders: OrderEnrollment[];
243
- product_relations: CourseProductRelation[];
241
+ offers: Offer[];
244
242
  certificate_id: Nullable<string>;
245
243
  }
246
244
  export const isEnrollment = (obj: unknown | Enrollment | OpenEdXEnrollment): obj is Enrollment => {
@@ -254,7 +252,7 @@ export const isEnrollment = (obj: unknown | Enrollment | OpenEdXEnrollment): obj
254
252
  'was_created_by_order' in obj &&
255
253
  'created_on' in obj &&
256
254
  'orders' in obj &&
257
- 'product_relations' in obj &&
255
+ 'offers' in obj &&
258
256
  'certificate_id' in obj
259
257
  );
260
258
  };
@@ -404,7 +402,7 @@ export interface NestedCourseOrder {
404
402
 
405
403
  export interface CourseOrderResourceQuery extends PaginatedResourceQuery {
406
404
  course_id?: CourseListItem['id'];
407
- course_product_relation_id?: CourseProductRelation['id'];
405
+ offer_id?: Offer['id'];
408
406
  organization_id?: Organization['id'];
409
407
  product_id?: Product['id'];
410
408
  }
@@ -539,8 +537,8 @@ export interface CourseProductQueryFilters extends ResourcesQuery {
539
537
  id?: Product['id'];
540
538
  course_id?: CourseListItem['id'];
541
539
  }
542
- export interface CourseProductRelationQueryFilters extends PaginatedResourceQuery {
543
- id?: CourseProductRelation['id'];
540
+ export interface OfferQueryFilters extends PaginatedResourceQuery {
541
+ id?: Offer['id'];
544
542
  organization_id?: Organization['id'];
545
543
  product_type?: ProductType;
546
544
  query?: string;
@@ -553,7 +551,7 @@ export enum ContractState {
553
551
  }
554
552
  export interface ContractResourceQuery extends PaginatedResourceQuery {
555
553
  organization_id?: Organization['id'];
556
- course_product_relation_id?: CourseProductRelation['id'];
554
+ offer_id?: Offer['id'];
557
555
  contract_ids?: Contract['id'][];
558
556
  signature_state?: ContractState;
559
557
  }
@@ -561,7 +559,7 @@ export interface ContractResourceQuery extends PaginatedResourceQuery {
561
559
  export interface OrganizationContractSignatureLinksFilters {
562
560
  contracts_ids?: string[];
563
561
  organization_id: Organization['id'];
564
- course_product_relation_ids?: CourseProductRelation['id'][];
562
+ offer_ids?: Offer['id'][];
565
563
  }
566
564
 
567
565
  export interface ContractInvitationLinkResponse {
@@ -655,10 +653,10 @@ interface APIUser {
655
653
  check: (id: string) => Promise<Response>;
656
654
  create: ({
657
655
  organization_id,
658
- course_product_relation_id,
656
+ offer_id,
659
657
  }: {
660
658
  organization_id?: Organization['id'];
661
- course_product_relation_id?: CourseProductRelation['id'];
659
+ offer_id?: Offer['id'];
662
660
  }) => Promise<{ url: string }>;
663
661
  get: (id: string) => Promise<File>;
664
662
  };
@@ -674,7 +672,7 @@ export interface API {
674
672
  ? Promise<Nullable<CourseListItem>>
675
673
  : Promise<PaginatedResponse<CourseListItem>>;
676
674
  products: {
677
- get(filters?: CourseProductQueryFilters): Promise<Nullable<CourseProductRelation>>;
675
+ get(filters?: CourseProductQueryFilters): Promise<Nullable<Offer>>;
678
676
  paymentSchedule: {
679
677
  get(filters?: CourseProductQueryFilters): Promise<Nullable<PaymentSchedule>>;
680
678
  };
@@ -707,12 +705,12 @@ export interface API {
707
705
  filters?: CourseRunFilters,
708
706
  ): CourseRunFilters extends { id: string } ? Promise<Nullable<CourseRun>> : Promise<CourseRun>;
709
707
  };
710
- courseProductRelations: {
708
+ offers: {
711
709
  get<Filters extends PaginatedResourceQuery = PaginatedResourceQuery>(
712
710
  filters?: Filters,
713
711
  ): Filters extends { id: string }
714
- ? Promise<Nullable<CourseProductRelation>>
715
- : Promise<PaginatedResponse<CourseProductRelationLight>>;
712
+ ? Promise<Nullable<Offer>>
713
+ : Promise<PaginatedResponse<OfferLight>>;
716
714
  };
717
715
  contractDefinitions: {
718
716
  previewTemplate(id: string): Promise<File>;
@@ -12,7 +12,7 @@ import {
12
12
  CourseLight,
13
13
  CourseListItem,
14
14
  CourseProduct,
15
- CourseProductRelation,
15
+ Offer,
16
16
  CourseRun,
17
17
  CredentialOrder,
18
18
  CredentialProduct,
@@ -89,7 +89,7 @@ export const EnrollmentFactory = factory((): Enrollment => {
89
89
  id: faker.string.uuid(),
90
90
  course_run: CourseRunWithCourseFactory().one(),
91
91
  is_active: true,
92
- product_relations: CourseProductRelationFactory().many(1),
92
+ offers: OfferFactory().many(1),
93
93
  state: EnrollmentState.SET,
94
94
  was_created_by_order: false,
95
95
  created_on: faker.date.past({ years: 1 }).toISOString(),
@@ -309,7 +309,7 @@ export const NestedCourseOrderFactory = factory((): NestedCourseOrder => {
309
309
  };
310
310
  });
311
311
 
312
- export const CourseProductRelationFactory = factory((): CourseProductRelation => {
312
+ export const OfferFactory = factory((): Offer => {
313
313
  return {
314
314
  id: faker.string.uuid(),
315
315
  created_on: faker.date.past().toISOString(),
@@ -3,14 +3,14 @@ import { CredentialOrder } from 'types/Joanie';
3
3
  import {
4
4
  ContractDefinitionFactory,
5
5
  CourseFactory,
6
- CourseProductRelationFactory,
6
+ OfferFactory,
7
7
  ProductFactory,
8
8
  } from 'utils/test/factories/joanie';
9
9
 
10
10
  export const mockCourseProductWithOrder = (order: CredentialOrder) => {
11
11
  const courseCode = order.course.code;
12
12
  const productId = order.product_id;
13
- const relation = CourseProductRelationFactory({
13
+ const offer = OfferFactory({
14
14
  product: ProductFactory({
15
15
  id: order.product_id,
16
16
  contract_definition: order.contract ? ContractDefinitionFactory().one() : undefined,
@@ -22,7 +22,7 @@ export const mockCourseProductWithOrder = (order: CredentialOrder) => {
22
22
 
23
23
  fetchMock.get(
24
24
  `https://joanie.endpoint/api/v1.0/courses/${courseCode}/products/${productId}/`,
25
- relation,
25
+ offer,
26
26
  );
27
- return relation;
27
+ return offer;
28
28
  };
@@ -33,7 +33,7 @@ export const DashboardItemEnrollment = ({ enrollment }: DashboardItemCourseRunPr
33
33
  </div>
34
34
  </div>,
35
35
  ];
36
- enrollment.product_relations.forEach(({ product, is_withdrawable }) => {
36
+ enrollment.offers.forEach(({ product, is_withdrawable }) => {
37
37
  if (isCertificateProduct(product)) {
38
38
  partialFooterList.push(
39
39
  <ProductCertificateFooter
@@ -249,7 +249,7 @@ describe('<ProductCertificateFooter/>', () => {
249
249
  course,
250
250
  }).one(),
251
251
  }).one();
252
- enrollment.product_relations[0].product = CertificateProductFactory().one();
252
+ enrollment.offers[0].product = CertificateProductFactory().one();
253
253
 
254
254
  fetchMock.get(
255
255
  `https://joanie.endpoint/api/v1.0/enrollments/?was_created_by_order=false&is_active=true&page=1&page_size=${PER_PAGE.useOrdersEnrollments}`,
@@ -67,15 +67,15 @@ export const DashboardItemOrder = ({
67
67
  }: DashboardItemOrderProps) => {
68
68
  const { course } = order;
69
69
  const intl = useIntl();
70
- const { item: courseProductRelation } = useCourseProduct({
70
+ const { item: offer } = useCourseProduct({
71
71
  product_id: order.product_id,
72
72
  course_id: course.code,
73
73
  });
74
- const { product } = courseProductRelation || {};
74
+ const { product } = offer || {};
75
75
  const needsSignature = OrderHelper.orderNeedsSignature(order);
76
76
  const needsPaymentMethod = order.state === OrderState.TO_SAVE_PAYMENT_METHOD;
77
77
  const isActive = OrderHelper.isActive(order);
78
- const isProductPurchasable = ProductHelper.isPurchasable(courseProductRelation?.product);
78
+ const isProductPurchasable = ProductHelper.isPurchasable(offer?.product);
79
79
  const isNotResumable = !isActive && !isProductPurchasable;
80
80
  const canEnroll = OrderHelper.allowEnrollment(order);
81
81
 
@@ -57,7 +57,7 @@ describe('<DashboardItemOrder/> Contract', () => {
57
57
  contract: ContractFactory({ student_signed_on: null }).one(),
58
58
  }).one();
59
59
 
60
- // learner dashboard course page do one call to course product relation per order
60
+ // learner dashboard course page do one call to offer per order
61
61
  const { product } = mockCourseProductWithOrder(order);
62
62
 
63
63
  // overwrite useOmniscientOrders call
@@ -94,7 +94,7 @@ const Installment = ({ order }: Props) => {
94
94
 
95
95
  const PaymentMethodManager = ({ order }: Props) => {
96
96
  const needsPaymentMethod = order.state === OrderState.TO_SAVE_PAYMENT_METHOD;
97
- const { item: relation, states } = useCourseProduct({
97
+ const { item: offer, states } = useCourseProduct({
98
98
  course_id: order.course.code,
99
99
  product_id: order.product_id,
100
100
  });
@@ -118,9 +118,9 @@ const PaymentMethodManager = ({ order }: Props) => {
118
118
  )}
119
119
  <SaleTunnel
120
120
  {...modal}
121
- product={relation.product as CredentialProduct}
122
- course={relation.course}
123
- isWithdrawable={relation.is_withdrawable}
121
+ product={offer.product as CredentialProduct}
122
+ course={offer.course}
123
+ isWithdrawable={offer.is_withdrawable}
124
124
  />
125
125
  </>
126
126
  );
@@ -25,6 +25,6 @@ export const enrollment: Enrollment = {
25
25
  },
26
26
  languages: ['en'],
27
27
  },
28
- product_relations: [],
28
+ offers: [],
29
29
  certificate_id: null,
30
30
  };