richie-education 2.25.0-b2.dev116 → 2.25.0-b2.dev118
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/hooks/useDefaultOrganizationId/index.tsx +12 -5
- package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.spec.tsx +15 -5
- package/js/pages/TeacherDashboardContractsLayout/TeacherDashboardContracts/index.tsx +9 -2
- package/js/pages/TeacherDashboardContractsLayout/components/ContractFiltersBar/index.tsx +4 -1
- package/js/widgets/Dashboard/components/TeacherDashboardCourseSidebar/index.spec.tsx +4 -1
- package/js/widgets/UserLogin/components/UserMenu/DesktopUserMenu.tsx +24 -2
- package/package.json +1 -1
- package/scss/objects/_selector.scss +6 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useParams, useSearchParams } from 'react-router-dom';
|
|
2
2
|
import { useOrganizations } from 'hooks/useOrganizations';
|
|
3
|
-
import { Organization } from 'types/Joanie';
|
|
3
|
+
import { CourseProductRelation, Organization } from 'types/Joanie';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* return organization id with this priority:
|
|
@@ -9,14 +9,21 @@ import { Organization } from 'types/Joanie';
|
|
|
9
9
|
* * first organization of user's organizations
|
|
10
10
|
*/
|
|
11
11
|
const useDefaultOrganizationId = () => {
|
|
12
|
-
const {
|
|
12
|
+
const {
|
|
13
|
+
organizationId: routeOrganizationId,
|
|
14
|
+
courseProductRelationId: routeCourseProductRelationId,
|
|
15
|
+
} = useParams<{
|
|
13
16
|
organizationId?: Organization['id'];
|
|
17
|
+
courseProductRelationId: CourseProductRelation['id'];
|
|
14
18
|
}>();
|
|
15
19
|
const [searchParams] = useSearchParams();
|
|
16
20
|
const queryOrganizationId = searchParams.get('organization_id') || undefined;
|
|
17
|
-
const { items: organizations } = useOrganizations(
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
const { items: organizations } = useOrganizations(
|
|
22
|
+
{ course_product_relation_id: routeCourseProductRelationId },
|
|
23
|
+
{
|
|
24
|
+
enabled: !routeOrganizationId && !queryOrganizationId,
|
|
25
|
+
},
|
|
26
|
+
);
|
|
20
27
|
|
|
21
28
|
return (
|
|
22
29
|
routeOrganizationId ||
|
|
@@ -3,7 +3,11 @@ import { screen } from '@testing-library/react';
|
|
|
3
3
|
import { getAllByRole } from '@testing-library/dom';
|
|
4
4
|
import userEvent from '@testing-library/user-event';
|
|
5
5
|
import { RichieContextFactory as mockRichieContextFactory } from 'utils/test/factories/richie';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
ContractFactory,
|
|
8
|
+
CourseProductRelationFactory,
|
|
9
|
+
OrganizationFactory,
|
|
10
|
+
} from 'utils/test/factories/joanie';
|
|
7
11
|
import { expectNoSpinner } from 'utils/test/expectSpinner';
|
|
8
12
|
import { expectBannerError } from 'utils/test/expectBanner';
|
|
9
13
|
import { HttpStatusCode } from 'utils/errors/HttpError';
|
|
@@ -34,6 +38,7 @@ describe('pages/TeacherDashboardContracts', () => {
|
|
|
34
38
|
setupJoanieSession();
|
|
35
39
|
|
|
36
40
|
it('should render a list of contracts for a course product relation', async () => {
|
|
41
|
+
const courseProductRelation = CourseProductRelationFactory().one();
|
|
37
42
|
const contracts = ContractFactory({
|
|
38
43
|
student_signed_on: Date.toString(),
|
|
39
44
|
organization_signed_on: Date.toString(),
|
|
@@ -42,22 +47,27 @@ describe('pages/TeacherDashboardContracts', () => {
|
|
|
42
47
|
const defaultOrganization = organizations[0];
|
|
43
48
|
|
|
44
49
|
// OrganizationContractFilter request all organizations forwho the user have access
|
|
45
|
-
fetchMock.get(
|
|
50
|
+
fetchMock.get(
|
|
51
|
+
`https://joanie.endpoint/api/v1.0/organizations/?course_product_relation_id=${courseProductRelation.id}`,
|
|
52
|
+
organizations,
|
|
53
|
+
);
|
|
46
54
|
// TeacherDashboardContracts request a paginated list of contracts to display
|
|
47
55
|
fetchMock.get(
|
|
48
|
-
`https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?course_product_relation_id
|
|
56
|
+
`https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?course_product_relation_id=${courseProductRelation.id}&signature_state=signed&page=1&page_size=25`,
|
|
49
57
|
{ results: contracts, count: 0, previous: null, next: null },
|
|
50
58
|
);
|
|
51
59
|
// useTeacherContractsToSign request all contract to sign, without pagination
|
|
52
60
|
fetchMock.get(
|
|
53
|
-
`https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?signature_state=half_signed&course_product_relation_id
|
|
61
|
+
`https://joanie.endpoint/api/v1.0/organizations/${defaultOrganization.id}/contracts/?signature_state=half_signed&course_product_relation_id=${courseProductRelation.id}`,
|
|
54
62
|
{ results: [], count: 0, previous: null, next: null },
|
|
55
63
|
);
|
|
56
64
|
|
|
57
65
|
render(<TeacherDashboardContracts />, {
|
|
58
66
|
routerOptions: {
|
|
59
67
|
path: '/courses/:courseId/products/:courseProductRelationId/contracts',
|
|
60
|
-
initialEntries: [
|
|
68
|
+
initialEntries: [
|
|
69
|
+
`/courses/${courseProductRelation.course.id}/products/${courseProductRelation.id}/contracts`,
|
|
70
|
+
],
|
|
61
71
|
},
|
|
62
72
|
});
|
|
63
73
|
|
|
@@ -42,14 +42,20 @@ const TeacherDashboardContracts = () => {
|
|
|
42
42
|
defaultPage: page ? parseInt(page, 10) : 1,
|
|
43
43
|
pageSize: PER_PAGE.teacherContractList,
|
|
44
44
|
});
|
|
45
|
-
const {
|
|
45
|
+
const {
|
|
46
|
+
organizationId: routeOrganizationId,
|
|
47
|
+
courseProductRelationId: routeCourseProductRelationId,
|
|
48
|
+
} = useParams<TeacherDashboardContractsParams>();
|
|
46
49
|
// organization list is used to show/hide organization filter.
|
|
47
50
|
// when organizationId is in route's params this filter is always hidden.
|
|
48
51
|
// therefore we don't need to enable this query.
|
|
49
52
|
const {
|
|
50
53
|
items: organizationList,
|
|
51
54
|
states: { isFetched: isOrganizationListFetched },
|
|
52
|
-
} = useOrganizations(
|
|
55
|
+
} = useOrganizations(
|
|
56
|
+
{ course_product_relation_id: routeCourseProductRelationId },
|
|
57
|
+
{ enabled: !routeOrganizationId },
|
|
58
|
+
);
|
|
53
59
|
const hasMultipleOrganizations = isOrganizationListFetched && organizationList.length > 1;
|
|
54
60
|
const { initialFilters, filters, setFilters } = useTeacherContractFilters();
|
|
55
61
|
const {
|
|
@@ -97,6 +103,7 @@ const TeacherDashboardContracts = () => {
|
|
|
97
103
|
<ContractFiltersBar
|
|
98
104
|
defaultValues={initialFilters}
|
|
99
105
|
onFiltersChange={handleFiltersChange}
|
|
106
|
+
organizationList={organizationList}
|
|
100
107
|
hideFilterOrganization={!!(routeOrganizationId || !hasMultipleOrganizations)}
|
|
101
108
|
/>
|
|
102
109
|
</div>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Select, SelectProps } from '@openfun/cunningham-react';
|
|
2
2
|
import { defineMessages, useIntl } from 'react-intl';
|
|
3
3
|
import FiltersBar from 'widgets/Dashboard/components/FiltersBar';
|
|
4
|
-
import { ContractState } from 'types/Joanie';
|
|
4
|
+
import { ContractState, Organization } from 'types/Joanie';
|
|
5
5
|
import { ContractHelper, ContractStatePoV } from 'utils/ContractHelper';
|
|
6
6
|
import FilterOrganization from 'widgets/Dashboard/components/FilterOrganization';
|
|
7
7
|
|
|
@@ -28,11 +28,13 @@ interface ContractFiltersBarProps {
|
|
|
28
28
|
defaultValues?: ContractListFilters;
|
|
29
29
|
hideFilterOrganization?: boolean;
|
|
30
30
|
hideFilterSignatureState?: boolean;
|
|
31
|
+
organizationList?: Organization[];
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
const ContractFiltersBar = ({
|
|
34
35
|
defaultValues,
|
|
35
36
|
onFiltersChange,
|
|
37
|
+
organizationList,
|
|
36
38
|
hideFilterOrganization = false,
|
|
37
39
|
hideFilterSignatureState = false,
|
|
38
40
|
}: ContractFiltersBarProps) => {
|
|
@@ -42,6 +44,7 @@ const ContractFiltersBar = ({
|
|
|
42
44
|
<FilterOrganization
|
|
43
45
|
defaultValue={defaultValues?.organization_id}
|
|
44
46
|
onChange={onFiltersChange}
|
|
47
|
+
organizationList={organizationList}
|
|
45
48
|
/>
|
|
46
49
|
)}
|
|
47
50
|
{!hideFilterSignatureState && (
|
|
@@ -129,7 +129,10 @@ describe('<TeacherDashboardCourseSidebar/>', () => {
|
|
|
129
129
|
courseProductRelation,
|
|
130
130
|
);
|
|
131
131
|
nbApiRequest += 1;
|
|
132
|
-
fetchMock.get(
|
|
132
|
+
fetchMock.get(
|
|
133
|
+
`https://joanie.endpoint/api/v1.0/organizations/?course_product_relation_id=${courseProductRelation.id}`,
|
|
134
|
+
[],
|
|
135
|
+
);
|
|
133
136
|
} else {
|
|
134
137
|
// mock api for course
|
|
135
138
|
nbApiRequest += 1;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { FC } from 'react';
|
|
2
2
|
import { defineMessages, FormattedMessage } from 'react-intl';
|
|
3
3
|
import { useSelect } from 'downshift';
|
|
4
|
+
import classNames from 'classnames';
|
|
4
5
|
import { location } from 'utils/indirection/window';
|
|
5
6
|
import { UserHelper } from 'utils/UserHelper';
|
|
6
7
|
import { UserMenuProps } from '.';
|
|
@@ -36,6 +37,21 @@ export const DesktopUserMenu: FC<UserMenuProps> = ({ user }) => {
|
|
|
36
37
|
},
|
|
37
38
|
});
|
|
38
39
|
|
|
40
|
+
const teacherDasbhoardUrl = user.urls.find((link) => {
|
|
41
|
+
return link.key === 'dashboard_teacher';
|
|
42
|
+
});
|
|
43
|
+
let menuLinkList;
|
|
44
|
+
if (teacherDasbhoardUrl) {
|
|
45
|
+
menuLinkList = [
|
|
46
|
+
teacherDasbhoardUrl,
|
|
47
|
+
...user.urls.filter((link) => {
|
|
48
|
+
return link.key !== 'dashboard_teacher';
|
|
49
|
+
}),
|
|
50
|
+
];
|
|
51
|
+
} else {
|
|
52
|
+
menuLinkList = user.urls;
|
|
53
|
+
}
|
|
54
|
+
|
|
39
55
|
return (
|
|
40
56
|
<div className="user-menu user-menu--desktop selector">
|
|
41
57
|
<label {...getLabelProps()} className="offscreen">
|
|
@@ -52,8 +68,14 @@ export const DesktopUserMenu: FC<UserMenuProps> = ({ user }) => {
|
|
|
52
68
|
className={`selector__list ${isOpen ? '' : 'selector__list--is-closed'}`}
|
|
53
69
|
>
|
|
54
70
|
{isOpen &&
|
|
55
|
-
|
|
56
|
-
<li
|
|
71
|
+
menuLinkList.map((link, index) => (
|
|
72
|
+
<li
|
|
73
|
+
key={link.key}
|
|
74
|
+
{...getItemProps({ item: link, index })}
|
|
75
|
+
className={classNames({
|
|
76
|
+
'selector__list__item--bordered': link.key === 'dashboard_teacher',
|
|
77
|
+
})}
|
|
78
|
+
>
|
|
57
79
|
{typeof link.action === 'string' ? (
|
|
58
80
|
<a
|
|
59
81
|
className={`selector__list__link ${
|
package/package.json
CHANGED
|
@@ -57,6 +57,12 @@
|
|
|
57
57
|
margin-left: calc(3rem - 12px);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
&__item {
|
|
61
|
+
&--bordered:not(:last-child) {
|
|
62
|
+
border-bottom: $onepixel solid r-theme-val(topbar, item-divider-border);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
60
66
|
&__link {
|
|
61
67
|
@include button-reset-style();
|
|
62
68
|
background: r-theme-val(selector, base-background);
|