Skip to content

Commit 1c21ca1

Browse files
committed
New implementation of user-group membership (admin, supervisor) management (to reflect changes in API as well as increasing ux comforts).
1 parent ffb79c2 commit 1c21ca1

File tree

21 files changed

+403
-315
lines changed

21 files changed

+403
-315
lines changed
Lines changed: 94 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,110 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3+
import ImmutablePropTypes from 'react-immutable-proptypes';
4+
import { OverlayTrigger, Tooltip } from 'react-bootstrap';
5+
import { FormattedMessage } from 'react-intl';
6+
import { defaultMemoize } from 'reselect';
37

4-
import MakeRemoveSupervisorButtonContainer from '../../../containers/MakeRemoveSupervisorButtonContainer';
8+
import Button, { TheButtonGroup } from '../../widgets/TheButton';
59
import AddUserContainer from '../../../containers/AddUserContainer';
610
import { knownRoles, isSupervisorRole } from '../../helpers/usersRoles';
11+
import { AdminIcon, SupervisorIcon, LoadingIcon } from '../../icons';
712

813
const ROLES_FILTER = knownRoles.filter(isSupervisorRole);
914

10-
const AddSupervisor = ({ groupId, instanceId }) => (
11-
<AddUserContainer
12-
instanceId={instanceId}
13-
id={`add-supervisor-${groupId}`}
14-
rolesFilter={ROLES_FILTER}
15-
createActions={({ id }) => <MakeRemoveSupervisorButtonContainer userId={id} groupId={groupId} />}
16-
/>
15+
const buildMembersIndex = defaultMemoize(
16+
(primaryAdmins, supervisors, students) =>
17+
new Set([...primaryAdmins.map(u => u.id), ...supervisors.map(u => u.id), ...students.map(u => u.id)])
1718
);
1819

20+
const AddSupervisor = ({
21+
groupId,
22+
instanceId,
23+
primaryAdmins,
24+
supervisors,
25+
students,
26+
addAdmin = null,
27+
addSupervisor = null,
28+
pendingMemberships,
29+
}) => {
30+
const membersIndex = buildMembersIndex(primaryAdmins, supervisors, students);
31+
return (
32+
<AddUserContainer
33+
instanceId={instanceId}
34+
id={`add-supervisor-${groupId}`}
35+
rolesFilter={ROLES_FILTER}
36+
createActions={({ id }) => {
37+
const isMember = membersIndex.has(id);
38+
const isPending = pendingMemberships.includes(id);
39+
return isPending ? (
40+
<LoadingIcon />
41+
) : (
42+
<TheButtonGroup>
43+
{addAdmin && (
44+
<OverlayTrigger
45+
placement="bottom"
46+
overlay={
47+
<Tooltip id={`adminButtonTooltip-${id}`}>
48+
{isMember ? (
49+
<FormattedMessage
50+
id="app.membersList.alreadyMember"
51+
defaultMessage="Selected user is already a member of the group"
52+
/>
53+
) : (
54+
<FormattedMessage id="app.membersList.addAsAdmin" defaultMessage="Add as administrator" />
55+
)}
56+
</Tooltip>
57+
}>
58+
<Button
59+
size="xs"
60+
onClick={() => addAdmin(groupId, id)}
61+
disabled={isMember}
62+
variant={isMember ? 'secondary' : 'success'}>
63+
<AdminIcon smallGapRight smallGapLeft fixedWidth />
64+
</Button>
65+
</OverlayTrigger>
66+
)}
67+
68+
{addSupervisor && (
69+
<OverlayTrigger
70+
placement="bottom"
71+
overlay={
72+
<Tooltip id={`supervisorButtonTooltip-${id}`}>
73+
{isMember ? (
74+
<FormattedMessage
75+
id="app.membersList.alreadyMember"
76+
defaultMessage="Selected user is already a member of the group"
77+
/>
78+
) : (
79+
<FormattedMessage id="app.membersList.addAsSupervisor" defaultMessage="Add as supervisor" />
80+
)}
81+
</Tooltip>
82+
}>
83+
<Button
84+
size="xs"
85+
onClick={() => addSupervisor(groupId, id)}
86+
disabled={isMember}
87+
variant={isMember ? 'secondary' : 'success'}>
88+
<SupervisorIcon smallGapRight smallGapLeft fixedWidth />
89+
</Button>
90+
</OverlayTrigger>
91+
)}
92+
</TheButtonGroup>
93+
);
94+
}}
95+
/>
96+
);
97+
};
98+
1999
AddSupervisor.propTypes = {
20100
instanceId: PropTypes.string.isRequired,
21101
groupId: PropTypes.string.isRequired,
102+
primaryAdmins: PropTypes.array.isRequired,
103+
supervisors: PropTypes.array.isRequired,
104+
students: PropTypes.array.isRequired,
105+
addAdmin: PropTypes.func,
106+
addSupervisor: PropTypes.func,
107+
pendingMemberships: ImmutablePropTypes.list,
22108
};
23109

24110
export default AddSupervisor;

src/components/Groups/MakeGroupAdminButton/MakeGroupAdminButton.js

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/components/Groups/MakeGroupAdminButton/index.js

Lines changed: 0 additions & 2 deletions
This file was deleted.

src/components/Groups/MakeSupervisorButton/MakeSupervisorButton.js

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/components/Groups/MakeSupervisorButton/index.js

Lines changed: 0 additions & 2 deletions
This file was deleted.

src/components/Groups/RemoveGroupAdminButton/RemoveGroupAdminButton.js

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/components/Groups/RemoveGroupAdminButton/index.js

Lines changed: 0 additions & 2 deletions
This file was deleted.

src/components/Groups/RemoveSupervisorButton/RemoveSupervisorButton.js

Lines changed: 0 additions & 18 deletions
This file was deleted.

src/components/Groups/RemoveSupervisorButton/index.js

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 59 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,78 @@
11
import React from 'react';
22
import PropTypes from 'prop-types';
3+
import ImmutablePropTypes from 'react-immutable-proptypes';
34
import { Table } from 'react-bootstrap';
45
import { FormattedMessage } from 'react-intl';
5-
import SupervisorsListItem, { LoadingSupervisorsListItem } from '../SupervisorsListItem';
66

7-
const SupervisorsList = ({ groupId, users, isLoaded = true, isAdmin, primaryAdminsIds }) => (
8-
<Table hover>
9-
<tbody>
10-
{isLoaded &&
11-
users.map(user => (
7+
import SupervisorsListItem from '../SupervisorsListItem';
8+
9+
const SupervisorsList = ({
10+
groupId,
11+
primaryAdmins,
12+
supervisors,
13+
isLoaded = true,
14+
showButtons = false,
15+
addAdmin = null,
16+
addSupervisor = null,
17+
removeMember = null,
18+
pendingMemberships,
19+
}) =>
20+
isLoaded ? (
21+
<Table hover responsive className="mb-1">
22+
<tbody>
23+
{primaryAdmins.map(user => (
1224
<SupervisorsListItem
1325
key={user.id}
1426
{...user}
1527
groupId={groupId}
16-
isAdmin={isAdmin}
17-
primaryAdminsIds={primaryAdminsIds}
28+
showButtons={showButtons}
29+
type="admin"
30+
addSupervisor={addSupervisor}
31+
removeMember={removeMember}
32+
pendingMembership={pendingMemberships.includes(user.id)}
1833
/>
1934
))}
2035

21-
{users.length === 0 && isLoaded && (
22-
<tr>
23-
<td className="text-center">
24-
<FormattedMessage id="app.userList.noSupervisors" defaultMessage="There are no supervisors on the list." />
25-
</td>
26-
</tr>
27-
)}
28-
29-
{!isLoaded && <LoadingSupervisorsListItem isAdmin={isAdmin} />}
30-
</tbody>
31-
</Table>
32-
);
36+
{supervisors.map(user => (
37+
<SupervisorsListItem
38+
key={user.id}
39+
{...user}
40+
groupId={groupId}
41+
showButtons={showButtons}
42+
type="supervisor"
43+
addAdmin={addAdmin}
44+
removeMember={removeMember}
45+
pendingMembership={pendingMemberships.includes(user.id)}
46+
/>
47+
))}
48+
{primaryAdmins.length === 0 && supervisors.length === 0 && (
49+
<tr>
50+
<td className="text-center text-muted">
51+
<FormattedMessage
52+
id="app.membersList.noMembers"
53+
defaultMessage="The group has no supervisors or admins."
54+
/>
55+
</td>
56+
</tr>
57+
)}
58+
</tbody>
59+
</Table>
60+
) : (
61+
<div>
62+
<FormattedMessage id="generic.loading" defaultMessage="Loading..." />
63+
</div>
64+
);
3365

3466
SupervisorsList.propTypes = {
35-
users: PropTypes.array.isRequired,
67+
primaryAdmins: PropTypes.array.isRequired,
68+
supervisors: PropTypes.array.isRequired,
3669
groupId: PropTypes.string.isRequired,
3770
isLoaded: PropTypes.bool,
38-
isAdmin: PropTypes.bool,
39-
primaryAdminsIds: PropTypes.array.isRequired,
71+
showButtons: PropTypes.bool,
72+
addAdmin: PropTypes.func,
73+
addSupervisor: PropTypes.func,
74+
removeMember: PropTypes.func,
75+
pendingMemberships: ImmutablePropTypes.list,
4076
};
4177

4278
export default SupervisorsList;

0 commit comments

Comments
 (0)