richie-education 3.1.3-dev15 → 3.1.3-dev17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/js/api/joanie.ts +8 -8
- package/js/components/ContractFrame/OrganizationContractFrame.spec.tsx +12 -11
- package/js/components/ContractFrame/OrganizationContractFrame.tsx +4 -4
- package/js/components/CourseGlimpse/utils.ts +28 -22
- package/js/components/CourseGlimpseList/utils.ts +2 -2
- package/js/components/PurchaseButton/index.tsx +3 -3
- package/js/components/SaleTunnel/GenericSaleTunnel.tsx +3 -3
- package/js/components/SaleTunnel/SaleTunnelInformation/index.tsx +2 -2
- package/js/components/SaleTunnel/index.full-process.spec.tsx +3 -3
- package/js/components/SaleTunnel/index.spec.tsx +5 -5
- package/js/components/SaleTunnel/index.tsx +2 -2
- package/js/components/TeacherDashboardCourseList/index.spec.tsx +3 -3
- package/js/components/TeacherDashboardCourseList/index.tsx +2 -2
- package/js/hooks/useContractArchive/index.ts +3 -3
- package/js/hooks/useCourseProductUnion/index.spec.tsx +16 -16
- package/js/hooks/useCourseProductUnion/index.ts +7 -7
- package/js/hooks/useCourseProducts.ts +4 -4
- package/js/hooks/useDefaultOrganizationId/index.tsx +4 -4
- package/js/hooks/useOffering/index.ts +32 -0
- package/js/hooks/useTeacherCoursesSearch/index.tsx +4 -4
- package/js/hooks/useTeacherPendingContractsCount/index.ts +4 -4
- package/js/pages/DashboardCourses/index.spec.tsx +17 -14
- package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.spec.tsx +11 -8
- package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.tsx +6 -3
- package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.spec.tsx +11 -11
- package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.timer.spec.tsx +10 -10
- package/js/pages/TeacherDashboardContractsLayout/components/BulkDownloadContractButton/index.tsx +4 -4
- package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.spec.tsx +5 -5
- package/js/pages/TeacherDashboardContractsLayout/components/ContractActionsBar/index.tsx +8 -8
- package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.spec.tsx +6 -6
- package/js/pages/TeacherDashboardContractsLayout/components/SignOrganizationContractButton/index.tsx +4 -4
- package/js/pages/TeacherDashboardContractsLayout/hooks/useCheckContractArchiveExists/index.spec.tsx +7 -7
- package/js/pages/TeacherDashboardContractsLayout/hooks/useCheckContractArchiveExists/index.tsx +5 -5
- package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/contractArchiveLocalStorage.spec.ts +21 -21
- package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/contractArchiveLocalStorage.ts +19 -13
- package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.spec.tsx +11 -11
- package/js/pages/TeacherDashboardContractsLayout/hooks/useDownloadContractArchive/index.tsx +6 -6
- package/js/pages/TeacherDashboardContractsLayout/hooks/useHasContractToDownload/index.tsx +6 -3
- package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractFilters/index.spec.tsx +16 -16
- package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractFilters/index.tsx +4 -4
- package/js/pages/TeacherDashboardContractsLayout/hooks/useTeacherContractsToSign.tsx +7 -4
- package/js/pages/TeacherDashboardCourseLearnersLayout/hooks/useCourseLearnersFilters/index.spec.tsx +21 -21
- package/js/pages/TeacherDashboardCourseLearnersLayout/hooks/useCourseLearnersFilters/index.ts +5 -5
- package/js/pages/TeacherDashboardCourseLearnersLayout/index.spec.tsx +55 -55
- package/js/pages/TeacherDashboardCourseLearnersLayout/index.tsx +1 -1
- package/js/pages/TeacherDashboardCoursesLoader/index.spec.tsx +11 -11
- package/js/pages/TeacherDashboardOrganizationCourseLoader/index.spec.tsx +11 -11
- package/js/pages/TeacherDashboardTraining/TeacherDashboardTrainingLoader.tsx +7 -7
- package/js/pages/TeacherDashboardTraining/index.spec.tsx +25 -25
- package/js/pages/TeacherDashboardTraining/index.tsx +16 -12
- package/js/types/Joanie.ts +21 -19
- package/js/utils/test/factories/joanie.ts +3 -3
- package/js/utils/test/mockCourseProductWithOrder.ts +4 -4
- package/js/widgets/Dashboard/components/DashboardItem/Enrollment/DashboardItemEnrollment.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/Enrollment/ProductCertificateFooter/index.spec.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrder.tsx +3 -3
- package/js/widgets/Dashboard/components/DashboardItem/Order/DashboardItemOrderContract.useUnionResource.cache.spec.tsx +1 -1
- package/js/widgets/Dashboard/components/DashboardItem/Order/Installment/index.tsx +4 -4
- package/js/widgets/Dashboard/components/DashboardItem/stories.mock.ts +1 -1
- package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.spec.tsx +23 -23
- package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.tsx +4 -4
- package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/index.spec.tsx +20 -17
- package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/index.tsx +22 -16
- package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/utils.ts +4 -4
- package/js/widgets/Dashboard/components/TeacherDashboardOrganizationSidebar/index.tsx +3 -3
- package/js/widgets/Dashboard/utils/teacherDashboardPaths.tsx +4 -4
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/CourseProductItemFooter/index.tsx +14 -10
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.spec.tsx +87 -63
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.stories.tsx +2 -2
- package/js/widgets/SyllabusCourseRunsList/components/CourseProductItem/index.tsx +24 -20
- package/js/widgets/SyllabusCourseRunsList/index.spec.tsx +8 -8
- package/package.json +1 -1
- package/js/hooks/useOffer/index.ts +0 -32
package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.spec.tsx
CHANGED
|
@@ -28,7 +28,7 @@ describe('<ContractNavLink />', () => {
|
|
|
28
28
|
fetchMock.get('https://joanie.endpoint/api/v1.0/organizations/', []);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
|
-
it('should render a ContractNavLink with route and label when neither organizationId and
|
|
31
|
+
it('should render a ContractNavLink with route and label when neither organizationId and offeringId are given', () => {
|
|
32
32
|
const link: MenuLink = {
|
|
33
33
|
to: '/dummy/url/',
|
|
34
34
|
label: 'My contract navigation link',
|
|
@@ -45,31 +45,31 @@ describe('<ContractNavLink />', () => {
|
|
|
45
45
|
it.each([
|
|
46
46
|
{
|
|
47
47
|
organizationId: faker.string.uuid(),
|
|
48
|
-
|
|
48
|
+
offeringId: undefined,
|
|
49
49
|
},
|
|
50
50
|
{
|
|
51
51
|
organizationId: faker.string.uuid(),
|
|
52
|
-
|
|
52
|
+
offeringId: faker.string.uuid(),
|
|
53
53
|
},
|
|
54
54
|
{
|
|
55
55
|
organizationId: undefined,
|
|
56
|
-
|
|
56
|
+
offeringId: faker.string.uuid(),
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
organizationId: undefined,
|
|
60
|
-
|
|
60
|
+
offeringId: undefined,
|
|
61
61
|
},
|
|
62
62
|
])(
|
|
63
|
-
'should never render Badge for organizationId: $organizationId and courseProductId: $
|
|
64
|
-
async ({ organizationId,
|
|
63
|
+
'should never render Badge for organizationId: $organizationId and courseProductId: $offeringId',
|
|
64
|
+
async ({ organizationId, offeringId }) => {
|
|
65
65
|
let contractQueryParams: ContractResourceQuery = {
|
|
66
66
|
signature_state: ContractState.LEARNER_SIGNED,
|
|
67
67
|
page: 1,
|
|
68
68
|
page_size: PER_PAGE.teacherContractList,
|
|
69
69
|
};
|
|
70
|
-
if (
|
|
70
|
+
if (offeringId) {
|
|
71
71
|
contractQueryParams = {
|
|
72
|
-
|
|
72
|
+
offering_id: offeringId,
|
|
73
73
|
...contractQueryParams,
|
|
74
74
|
};
|
|
75
75
|
}
|
|
@@ -94,7 +94,7 @@ describe('<ContractNavLink />', () => {
|
|
|
94
94
|
label: 'My contract navigation link',
|
|
95
95
|
}}
|
|
96
96
|
organizationId={organizationId}
|
|
97
|
-
|
|
97
|
+
offeringId={offeringId}
|
|
98
98
|
/>,
|
|
99
99
|
);
|
|
100
100
|
|
|
@@ -112,25 +112,25 @@ describe('<ContractNavLink />', () => {
|
|
|
112
112
|
// with 1 contracts to sign
|
|
113
113
|
{
|
|
114
114
|
organizationId: faker.string.uuid(),
|
|
115
|
-
|
|
115
|
+
offeringId: undefined,
|
|
116
116
|
nbContractsToSign: 1,
|
|
117
117
|
expectedBadgeCount: 1,
|
|
118
118
|
},
|
|
119
119
|
{
|
|
120
120
|
organizationId: faker.string.uuid(),
|
|
121
|
-
|
|
121
|
+
offeringId: faker.string.uuid(),
|
|
122
122
|
nbContractsToSign: 1,
|
|
123
123
|
expectedBadgeCount: 1,
|
|
124
124
|
},
|
|
125
125
|
{
|
|
126
126
|
organizationId: undefined,
|
|
127
|
-
|
|
127
|
+
offeringId: faker.string.uuid(),
|
|
128
128
|
nbContractsToSign: 1,
|
|
129
129
|
expectedBadgeCount: undefined,
|
|
130
130
|
},
|
|
131
131
|
{
|
|
132
132
|
organizationId: undefined,
|
|
133
|
-
|
|
133
|
+
offeringId: undefined,
|
|
134
134
|
nbContractsToSign: 1,
|
|
135
135
|
expectedBadgeCount: undefined,
|
|
136
136
|
},
|
|
@@ -138,39 +138,39 @@ describe('<ContractNavLink />', () => {
|
|
|
138
138
|
// with 0 contracts to sign
|
|
139
139
|
{
|
|
140
140
|
organizationId: faker.string.uuid(),
|
|
141
|
-
|
|
141
|
+
offeringId: undefined,
|
|
142
142
|
nbContractsToSign: 0,
|
|
143
143
|
expectedBadgeCount: undefined,
|
|
144
144
|
},
|
|
145
145
|
{
|
|
146
146
|
organizationId: faker.string.uuid(),
|
|
147
|
-
|
|
147
|
+
offeringId: faker.string.uuid(),
|
|
148
148
|
nbContractsToSign: 0,
|
|
149
149
|
expectedBadgeCount: undefined,
|
|
150
150
|
},
|
|
151
151
|
{
|
|
152
152
|
organizationId: undefined,
|
|
153
|
-
|
|
153
|
+
offeringId: faker.string.uuid(),
|
|
154
154
|
nbContractsToSign: 0,
|
|
155
155
|
expectedBadgeCount: undefined,
|
|
156
156
|
},
|
|
157
157
|
{
|
|
158
158
|
organizationId: undefined,
|
|
159
|
-
|
|
159
|
+
offeringId: undefined,
|
|
160
160
|
nbContractsToSign: 0,
|
|
161
161
|
expectedBadgeCount: undefined,
|
|
162
162
|
},
|
|
163
163
|
])(
|
|
164
|
-
'should render Badge (count: $expectedBadgeCount) for nb contracts to sign: $nbContractsToSign, organizationId: $organizationId and courseProductId: $
|
|
165
|
-
async ({ nbContractsToSign, organizationId,
|
|
164
|
+
'should render Badge (count: $expectedBadgeCount) for nb contracts to sign: $nbContractsToSign, organizationId: $organizationId and courseProductId: $offeringId',
|
|
165
|
+
async ({ nbContractsToSign, organizationId, offeringId, expectedBadgeCount }) => {
|
|
166
166
|
let contractQueryParams: ContractResourceQuery = {
|
|
167
167
|
signature_state: ContractState.LEARNER_SIGNED,
|
|
168
168
|
page: 1,
|
|
169
169
|
page_size: PER_PAGE.teacherContractList,
|
|
170
170
|
};
|
|
171
|
-
if (
|
|
171
|
+
if (offeringId) {
|
|
172
172
|
contractQueryParams = {
|
|
173
|
-
|
|
173
|
+
offering_id: offeringId,
|
|
174
174
|
...contractQueryParams,
|
|
175
175
|
};
|
|
176
176
|
}
|
|
@@ -194,7 +194,7 @@ describe('<ContractNavLink />', () => {
|
|
|
194
194
|
label: 'My contract navigation link',
|
|
195
195
|
}}
|
|
196
196
|
organizationId={organizationId}
|
|
197
|
-
|
|
197
|
+
offeringId={offeringId}
|
|
198
198
|
/>,
|
|
199
199
|
);
|
|
200
200
|
|
package/js/widgets/Dashboard/components/DashboardSidebar/components/ContractNavLink/index.tsx
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createSearchParams } from 'react-router';
|
|
2
2
|
import { useMemo } from 'react';
|
|
3
3
|
import { MenuLink } from 'widgets/Dashboard/components/DashboardSidebar';
|
|
4
|
-
import { ContractState,
|
|
4
|
+
import { ContractState, Offering, Organization } from 'types/Joanie';
|
|
5
5
|
import useTeacherPendingContractsCount from 'hooks/useTeacherPendingContractsCount';
|
|
6
6
|
import { ContractActions } from 'utils/AbilitiesHelper/types';
|
|
7
7
|
import useContractAbilities from 'hooks/useContractAbilities';
|
|
@@ -11,14 +11,14 @@ import MenuNavLink from '../MenuNavLink';
|
|
|
11
11
|
interface ContractNavLinkProps {
|
|
12
12
|
link: MenuLink;
|
|
13
13
|
organizationId?: Organization['id'];
|
|
14
|
-
|
|
14
|
+
offeringId?: Offering['id'];
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
const ContractNavLink = ({ link, organizationId,
|
|
17
|
+
const ContractNavLink = ({ link, organizationId, offeringId }: ContractNavLinkProps) => {
|
|
18
18
|
const defaultOrganizationId = useDefaultOrganizationId();
|
|
19
19
|
const { contracts: pendingContracts, pendingContractCount } = useTeacherPendingContractsCount({
|
|
20
20
|
organizationId: organizationId || defaultOrganizationId,
|
|
21
|
-
|
|
21
|
+
offeringId,
|
|
22
22
|
});
|
|
23
23
|
const contractAbilities = useContractAbilities(pendingContracts);
|
|
24
24
|
const canSignContracts = contractAbilities.can(ContractActions.SIGN);
|
|
@@ -4,7 +4,7 @@ import { createIntl } from 'react-intl';
|
|
|
4
4
|
import { generatePath } from 'react-router';
|
|
5
5
|
import { CourseListItem } from 'types/Joanie';
|
|
6
6
|
import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
|
|
7
|
-
import { CourseFactory,
|
|
7
|
+
import { CourseFactory, OfferingFactory, OrganizationFactory } from 'utils/test/factories/joanie';
|
|
8
8
|
import { expectNoSpinner } from 'utils/test/expectSpinner';
|
|
9
9
|
import { render } from 'utils/test/render';
|
|
10
10
|
import { setupJoanieSession } from 'utils/test/wrappers/JoanieAppWrapper';
|
|
@@ -61,14 +61,14 @@ describe('<TeacherDashboardCourseSidebar/>', () => {
|
|
|
61
61
|
label: 'course',
|
|
62
62
|
course: CourseFactory().one(),
|
|
63
63
|
organization: undefined,
|
|
64
|
-
|
|
64
|
+
offering: undefined,
|
|
65
65
|
expectedRoutes: [TeacherDashboardPaths.COURSE_GENERAL_INFORMATION],
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
68
|
label: 'training',
|
|
69
69
|
course: CourseFactory().one(),
|
|
70
70
|
organization: undefined,
|
|
71
|
-
|
|
71
|
+
offering: OfferingFactory().one(),
|
|
72
72
|
expectedRoutes: [
|
|
73
73
|
TeacherDashboardPaths.COURSE_PRODUCT,
|
|
74
74
|
TeacherDashboardPaths.COURSE_PRODUCT_CONTRACTS,
|
|
@@ -79,14 +79,14 @@ describe('<TeacherDashboardCourseSidebar/>', () => {
|
|
|
79
79
|
label: "organization's course",
|
|
80
80
|
course: CourseFactory().one(),
|
|
81
81
|
organization: OrganizationFactory().one(),
|
|
82
|
-
|
|
82
|
+
offering: undefined,
|
|
83
83
|
expectedRoutes: [TeacherDashboardPaths.ORGANIZATION_COURSE_GENERAL_INFORMATION],
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
86
|
label: "organization's training",
|
|
87
87
|
course: CourseFactory().one(),
|
|
88
88
|
organization: OrganizationFactory().one(),
|
|
89
|
-
|
|
89
|
+
offering: OfferingFactory().one(),
|
|
90
90
|
expectedRoutes: [
|
|
91
91
|
TeacherDashboardPaths.ORGANIZATION_PRODUCT,
|
|
92
92
|
TeacherDashboardPaths.ORGANIZATION_PRODUCT_CONTRACTS,
|
|
@@ -95,20 +95,20 @@ describe('<TeacherDashboardCourseSidebar/>', () => {
|
|
|
95
95
|
},
|
|
96
96
|
])(
|
|
97
97
|
'should display menu items for "$label" route',
|
|
98
|
-
async ({ course, organization,
|
|
98
|
+
async ({ course, organization, offering, expectedRoutes }) => {
|
|
99
99
|
// mock api for organization's training
|
|
100
|
-
if (organization &&
|
|
100
|
+
if (organization && offering) {
|
|
101
101
|
// fetching training's contracts
|
|
102
102
|
nbApiRequest += 1;
|
|
103
103
|
fetchMock.get(
|
|
104
|
-
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?
|
|
104
|
+
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/contracts/?offering_id=${offering.id}&signature_state=half_signed&page=1&page_size=25`,
|
|
105
105
|
[],
|
|
106
106
|
);
|
|
107
107
|
// fetching organization's training
|
|
108
108
|
nbApiRequest += 1;
|
|
109
109
|
fetchMock.get(
|
|
110
|
-
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/
|
|
111
|
-
|
|
110
|
+
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/offerings/${offering.id}/`,
|
|
111
|
+
offering,
|
|
112
112
|
);
|
|
113
113
|
} else if (organization) {
|
|
114
114
|
// fetching organization's course
|
|
@@ -117,12 +117,15 @@ describe('<TeacherDashboardCourseSidebar/>', () => {
|
|
|
117
117
|
`https://joanie.endpoint/api/v1.0/organizations/${organization.id}/courses/${course.id}/`,
|
|
118
118
|
course,
|
|
119
119
|
);
|
|
120
|
-
} else if (
|
|
120
|
+
} else if (offering) {
|
|
121
121
|
// fetching training
|
|
122
122
|
nbApiRequest += 1;
|
|
123
|
-
fetchMock.get(`https://joanie.endpoint/api/v1.0/
|
|
123
|
+
fetchMock.get(`https://joanie.endpoint/api/v1.0/offerings/${offering.id}/`, offering);
|
|
124
124
|
nbApiRequest += 1;
|
|
125
|
-
fetchMock.get(
|
|
125
|
+
fetchMock.get(
|
|
126
|
+
`https://joanie.endpoint/api/v1.0/organizations/?offering_id=${offering.id}`,
|
|
127
|
+
[],
|
|
128
|
+
);
|
|
126
129
|
} else {
|
|
127
130
|
// mock api for course
|
|
128
131
|
nbApiRequest += 1;
|
|
@@ -132,9 +135,9 @@ describe('<TeacherDashboardCourseSidebar/>', () => {
|
|
|
132
135
|
let routePath = '/:courseId';
|
|
133
136
|
let initialEntry = `/${course.id}`;
|
|
134
137
|
|
|
135
|
-
if (
|
|
136
|
-
routePath += '/:
|
|
137
|
-
initialEntry += `/${
|
|
138
|
+
if (offering) {
|
|
139
|
+
routePath += '/:offeringId';
|
|
140
|
+
initialEntry += `/${offering.id}`;
|
|
138
141
|
}
|
|
139
142
|
if (organization) {
|
|
140
143
|
routePath = '/:organizationId' + routePath;
|
|
@@ -158,7 +161,7 @@ describe('<TeacherDashboardCourseSidebar/>', () => {
|
|
|
158
161
|
generatePath(expectedRoute, {
|
|
159
162
|
organizationId: organization ? organization.id : null,
|
|
160
163
|
courseId: course.id,
|
|
161
|
-
|
|
164
|
+
offeringId: offering ? offering.id : null,
|
|
162
165
|
}),
|
|
163
166
|
);
|
|
164
167
|
});
|
|
@@ -2,7 +2,7 @@ import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
|
|
2
2
|
import { generatePath, useParams } from 'react-router';
|
|
3
3
|
import { useMemo } from 'react';
|
|
4
4
|
import { capitalize } from 'lodash-es';
|
|
5
|
-
import {
|
|
5
|
+
import { useOffering } from 'hooks/useOffering';
|
|
6
6
|
import { DashboardSidebar, MenuLink } from 'widgets/Dashboard/components/DashboardSidebar';
|
|
7
7
|
import { getDashboardRouteLabel } from 'widgets/Dashboard/utils/dashboardRoutes';
|
|
8
8
|
import { useCourse } from 'hooks/useCourses';
|
|
@@ -41,11 +41,11 @@ export const TeacherDashboardCourseSidebar = () => {
|
|
|
41
41
|
const {
|
|
42
42
|
organizationId: routeOrganizationId,
|
|
43
43
|
courseId: routeCourseId,
|
|
44
|
-
|
|
44
|
+
offeringId: routeOfferingId = '',
|
|
45
45
|
} = useParams<{
|
|
46
46
|
organizationId?: string;
|
|
47
47
|
courseId: string;
|
|
48
|
-
|
|
48
|
+
offeringId: string;
|
|
49
49
|
}>();
|
|
50
50
|
|
|
51
51
|
const {
|
|
@@ -54,29 +54,35 @@ export const TeacherDashboardCourseSidebar = () => {
|
|
|
54
54
|
} = useCourse(
|
|
55
55
|
routeCourseId,
|
|
56
56
|
{ organization_id: routeOrganizationId },
|
|
57
|
-
{ enabled: !
|
|
57
|
+
{ enabled: !routeOfferingId },
|
|
58
58
|
);
|
|
59
59
|
|
|
60
60
|
const {
|
|
61
|
-
item:
|
|
62
|
-
states: { fetching:
|
|
63
|
-
} =
|
|
64
|
-
|
|
61
|
+
item: offering,
|
|
62
|
+
states: { fetching: offeringFetching },
|
|
63
|
+
} = useOffering(
|
|
64
|
+
routeOfferingId,
|
|
65
65
|
{
|
|
66
66
|
organization_id: routeOrganizationId,
|
|
67
67
|
},
|
|
68
|
-
{ enabled: !!
|
|
68
|
+
{ enabled: !!routeOfferingId },
|
|
69
69
|
);
|
|
70
70
|
|
|
71
|
-
const fetching = useMemo(
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
const fetching = useMemo(
|
|
72
|
+
() => courseFetching || offeringFetching,
|
|
73
|
+
[courseFetching, offeringFetching],
|
|
74
|
+
);
|
|
75
|
+
const product = useMemo(() => (offering ? offering.product : undefined), [offering]);
|
|
76
|
+
const course = useMemo(
|
|
77
|
+
() => (offering ? offering.course : singleCourse),
|
|
78
|
+
[offering, singleCourse],
|
|
79
|
+
);
|
|
74
80
|
|
|
75
81
|
const getMenuLinkFromPath = (basePath: TeacherDashboardPaths) => {
|
|
76
82
|
const path = generatePath(basePath, {
|
|
77
83
|
organizationId: routeOrganizationId ?? '',
|
|
78
84
|
courseId: routeCourseId ?? '',
|
|
79
|
-
|
|
85
|
+
offeringId: routeOfferingId ?? '',
|
|
80
86
|
});
|
|
81
87
|
const menuLink: MenuLink = {
|
|
82
88
|
to: path,
|
|
@@ -93,7 +99,7 @@ export const TeacherDashboardCourseSidebar = () => {
|
|
|
93
99
|
<ContractNavLink
|
|
94
100
|
link={menuLink}
|
|
95
101
|
organizationId={routeOrganizationId}
|
|
96
|
-
|
|
102
|
+
offeringId={routeOfferingId}
|
|
97
103
|
/>
|
|
98
104
|
);
|
|
99
105
|
}
|
|
@@ -103,10 +109,10 @@ export const TeacherDashboardCourseSidebar = () => {
|
|
|
103
109
|
const menuLinkList = useMemo(
|
|
104
110
|
() =>
|
|
105
111
|
getMenuRoutes({
|
|
106
|
-
|
|
112
|
+
offeringId: routeOfferingId,
|
|
107
113
|
organizationId: routeOrganizationId,
|
|
108
114
|
}).map(getMenuLinkFromPath),
|
|
109
|
-
[routeOrganizationId,
|
|
115
|
+
[routeOrganizationId, routeOfferingId],
|
|
110
116
|
);
|
|
111
117
|
|
|
112
118
|
return (
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { TeacherDashboardPaths } from 'widgets/Dashboard/utils/teacherDashboardPaths';
|
|
2
2
|
|
|
3
3
|
interface GetMenuRoutesArgs {
|
|
4
|
-
|
|
4
|
+
offeringId?: string;
|
|
5
5
|
organizationId?: string;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
export const getMenuRoutes = ({
|
|
8
|
+
export const getMenuRoutes = ({ offeringId, organizationId }: GetMenuRoutesArgs) => {
|
|
9
9
|
if (organizationId) {
|
|
10
|
-
if (
|
|
10
|
+
if (offeringId) {
|
|
11
11
|
return [
|
|
12
12
|
TeacherDashboardPaths.ORGANIZATION_PRODUCT,
|
|
13
13
|
TeacherDashboardPaths.ORGANIZATION_PRODUCT_CONTRACTS,
|
|
@@ -17,7 +17,7 @@ export const getMenuRoutes = ({ offerId, organizationId }: GetMenuRoutesArgs) =>
|
|
|
17
17
|
return [TeacherDashboardPaths.ORGANIZATION_COURSE_GENERAL_INFORMATION];
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
if (
|
|
20
|
+
if (offeringId) {
|
|
21
21
|
return [
|
|
22
22
|
TeacherDashboardPaths.COURSE_PRODUCT,
|
|
23
23
|
TeacherDashboardPaths.COURSE_PRODUCT_CONTRACTS,
|
|
@@ -24,9 +24,9 @@ const messages = defineMessages({
|
|
|
24
24
|
export const TeacherDashboardOrganizationSidebar = () => {
|
|
25
25
|
const intl = useIntl();
|
|
26
26
|
const getRouteLabel = getDashboardRouteLabel(intl);
|
|
27
|
-
const { organizationId,
|
|
27
|
+
const { organizationId, offeringId } = useParams<{
|
|
28
28
|
organizationId: string;
|
|
29
|
-
|
|
29
|
+
offeringId?: string;
|
|
30
30
|
}>();
|
|
31
31
|
const {
|
|
32
32
|
item: organization,
|
|
@@ -43,7 +43,7 @@ export const TeacherDashboardOrganizationSidebar = () => {
|
|
|
43
43
|
|
|
44
44
|
if (basePath === TeacherDashboardPaths.ORGANIZATION_CONTRACTS) {
|
|
45
45
|
menuLink.component = (
|
|
46
|
-
<ContractNavLink link={menuLink} organizationId={organizationId}
|
|
46
|
+
<ContractNavLink link={menuLink} organizationId={organizationId} offeringId={offeringId} />
|
|
47
47
|
);
|
|
48
48
|
}
|
|
49
49
|
|
|
@@ -9,14 +9,14 @@ export enum TeacherDashboardPaths {
|
|
|
9
9
|
ORGANIZATION = `${ROOT}/organizations/:organizationId`,
|
|
10
10
|
ORGANIZATION_CONTRACTS = `${ORGANIZATION}/contracts`,
|
|
11
11
|
ORGANIZATION_COURSES = `${ORGANIZATION}/courses`,
|
|
12
|
-
ORGANIZATION_PRODUCT = `${ORGANIZATION_COURSES}/:courseId/products/:
|
|
12
|
+
ORGANIZATION_PRODUCT = `${ORGANIZATION_COURSES}/:courseId/products/:offeringId`,
|
|
13
13
|
ORGANIZATION_COURSE_CONTRACTS = `${ORGANIZATION_COURSES}/:courseId/contracts`,
|
|
14
|
-
ORGANIZATION_PRODUCT_CONTRACTS = `${ORGANIZATION_COURSES}/:courseId/products/:
|
|
15
|
-
ORGANIZATION_COURSE_PRODUCT_LEARNER_LIST = `${ORGANIZATION_COURSES}/:courseId/products/:
|
|
14
|
+
ORGANIZATION_PRODUCT_CONTRACTS = `${ORGANIZATION_COURSES}/:courseId/products/:offeringId/contracts`,
|
|
15
|
+
ORGANIZATION_COURSE_PRODUCT_LEARNER_LIST = `${ORGANIZATION_COURSES}/:courseId/products/:offeringId/learners`,
|
|
16
16
|
ORGANIZATION_COURSE_GENERAL_INFORMATION = `${ORGANIZATION_COURSES}/:courseId/information`,
|
|
17
17
|
COURSE = `${TEACHER_COURSES}/:courseId`,
|
|
18
18
|
COURSE_GENERAL_INFORMATION = `${COURSE}/information`,
|
|
19
|
-
COURSE_PRODUCT = `${COURSE}/products/:
|
|
19
|
+
COURSE_PRODUCT = `${COURSE}/products/:offeringId`,
|
|
20
20
|
COURSE_PRODUCT_LEARNER_LIST = `${COURSE_PRODUCT}/learners`,
|
|
21
21
|
COURSE_PRODUCT_CONTRACTS = `${COURSE_PRODUCT}/contracts`,
|
|
22
22
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { FormattedMessage, defineMessages } from 'react-intl';
|
|
2
2
|
import PurchaseButton from 'components/PurchaseButton';
|
|
3
|
-
import {
|
|
3
|
+
import { Offering, CredentialProduct } from 'types/Joanie';
|
|
4
4
|
import { PacedCourse } from 'types';
|
|
5
5
|
|
|
6
6
|
const messages = defineMessages({
|
|
@@ -24,12 +24,16 @@ other {# remaining seats}
|
|
|
24
24
|
|
|
25
25
|
interface CourseProductItemFooterProps {
|
|
26
26
|
course: PacedCourse;
|
|
27
|
-
|
|
27
|
+
offering: Offering;
|
|
28
28
|
canPurchase: boolean;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
const CourseProductItemFooter = ({
|
|
32
|
-
|
|
31
|
+
const CourseProductItemFooter = ({
|
|
32
|
+
course,
|
|
33
|
+
offering,
|
|
34
|
+
canPurchase,
|
|
35
|
+
}: CourseProductItemFooterProps) => {
|
|
36
|
+
if (!offering.rules.has_seats_left)
|
|
33
37
|
return (
|
|
34
38
|
<p className="product-widget__footer__message">
|
|
35
39
|
<FormattedMessage {...messages.noSeatsAvailable} />
|
|
@@ -39,18 +43,18 @@ const CourseProductItemFooter = ({ course, offer, canPurchase }: CourseProductIt
|
|
|
39
43
|
<div className="product-widget__footer__order-group">
|
|
40
44
|
<PurchaseButton
|
|
41
45
|
course={course}
|
|
42
|
-
product={
|
|
43
|
-
|
|
44
|
-
organizations={
|
|
45
|
-
isWithdrawable={
|
|
46
|
+
product={offering.product as CredentialProduct}
|
|
47
|
+
offering={offering}
|
|
48
|
+
organizations={offering.organizations}
|
|
49
|
+
isWithdrawable={offering.is_withdrawable}
|
|
46
50
|
disabled={!canPurchase}
|
|
47
51
|
buttonProps={{ fullWidth: true }}
|
|
48
52
|
/>
|
|
49
|
-
{
|
|
53
|
+
{offering.rules.has_seat_limit && (
|
|
50
54
|
<p className="product-widget__footer__message">
|
|
51
55
|
<FormattedMessage
|
|
52
56
|
{...messages.nbSeatsAvailable}
|
|
53
|
-
values={{ nb:
|
|
57
|
+
values={{ nb: offering.rules.nb_available_seats }}
|
|
54
58
|
/>
|
|
55
59
|
</p>
|
|
56
60
|
)}
|