diff --git a/src/components/Groups/GroupDetail/GroupDetail.js b/src/components/Groups/GroupDetail/GroupDetail.js index 8a702c145..ea78b406e 100644 --- a/src/components/Groups/GroupDetail/GroupDetail.js +++ b/src/components/Groups/GroupDetail/GroupDetail.js @@ -20,7 +20,7 @@ const GroupDetail = ({ parentGroupId, isPublic = false, childGroups, - adminId, + primaryAdminsIds, ...group }, groups, @@ -131,7 +131,7 @@ const GroupDetail = ({ groupId={id} users={supervisors} isAdmin={isAdmin} - mainAdminId={adminId} + primaryAdminsIds={primaryAdminsIds} isLoaded={supervisors.length === group.supervisors.length} /> @@ -151,7 +151,8 @@ GroupDetail.propTypes = { }), threshold: PropTypes.number, isPublic: PropTypes.bool, - supervisors: PropTypes.array.isRequired + supervisors: PropTypes.array.isRequired, + primaryAdminsIds: PropTypes.array.isRequired }), groups: PropTypes.object.isRequired, publicGroups: ImmutablePropTypes.map.isRequired, diff --git a/src/components/Groups/RemoveGroupAdminButton/RemoveGroupAdminButton.js b/src/components/Groups/RemoveGroupAdminButton/RemoveGroupAdminButton.js new file mode 100644 index 000000000..3a9de31c1 --- /dev/null +++ b/src/components/Groups/RemoveGroupAdminButton/RemoveGroupAdminButton.js @@ -0,0 +1,20 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'react-intl'; +import Button from '../../widgets/FlatButton'; +import Icon from 'react-fontawesome'; + +const RemoveGroupAdminButton = ({ onClick, ...props }) => + ; + +RemoveGroupAdminButton.propTypes = { + onClick: PropTypes.func.isRequired +}; + +export default RemoveGroupAdminButton; diff --git a/src/components/Groups/RemoveGroupAdminButton/index.js b/src/components/Groups/RemoveGroupAdminButton/index.js new file mode 100644 index 000000000..6b3acf5d1 --- /dev/null +++ b/src/components/Groups/RemoveGroupAdminButton/index.js @@ -0,0 +1 @@ +export default from './RemoveGroupAdminButton'; diff --git a/src/components/Users/SupervisorsList/SupervisorsList.js b/src/components/Users/SupervisorsList/SupervisorsList.js index 5d6631998..efaab793a 100644 --- a/src/components/Users/SupervisorsList/SupervisorsList.js +++ b/src/components/Users/SupervisorsList/SupervisorsList.js @@ -11,7 +11,7 @@ const SupervisorsList = ({ users, isLoaded = true, isAdmin, - mainAdminId + primaryAdminsIds }) =>
@@ -22,10 +24,20 @@ const SupervisorsListItem = ({ | {isAdmin &&
-
+ }
+ {primaryAdminsIds.indexOf(id) >= 0 &&
+ | }
@@ -37,8 +49,9 @@ SupervisorsListItem.propTypes = {
groupId: PropTypes.string.isRequired,
fullName: PropTypes.string.isRequired,
avatarUrl: PropTypes.string.isRequired,
- makeAdmin: PropTypes.func.isRequired,
- mainAdminId: PropTypes.string.isRequired
+ addAdmin: PropTypes.func.isRequired,
+ removeAdmin: PropTypes.func.isRequired,
+ primaryAdminsIds: PropTypes.array.isRequired
};
const mapStateToProps = (state, { groupId }) => ({
@@ -46,7 +59,8 @@ const mapStateToProps = (state, { groupId }) => ({
});
const mapDispatchToProps = {
- makeAdmin
+ addAdmin,
+ removeAdmin
};
export default connect(mapStateToProps, mapDispatchToProps)(
diff --git a/src/redux/modules/groups.js b/src/redux/modules/groups.js
index 08c39d10c..43993e9f4 100644
--- a/src/redux/modules/groups.js
+++ b/src/redux/modules/groups.js
@@ -37,10 +37,14 @@ export const additionalActionTypes = {
REMOVE_SUPERVISOR_PENDING: 'recodex/groups/REMOVE_SUPERVISOR_PENDING',
REMOVE_SUPERVISOR_FULFILLED: 'recodex/groups/REMOVE_SUPERVISOR_FULFILLED',
REMOVE_SUPERVISOR_REJECTED: 'recodex/groups/REMOVE_SUPERVISOR_REJECTED',
- MAKE_ADMIN: 'recodex/groups/MAKE_ADMIN',
- MAKE_ADMIN_PENDING: 'recodex/groups/MAKE_ADMIN_PENDING',
- MAKE_ADMIN_FULFILLED: 'recodex/groups/MAKE_ADMIN_FULFILLED',
- MAKE_ADMIN_REJECTED: 'recodex/groups/MAKE_ADMIN_REJECTED'
+ ADD_ADMIN: 'recodex/groups/ADD_ADMIN',
+ ADD_ADMIN_PENDING: 'recodex/groups/ADD_ADMIN_PENDING',
+ ADD_ADMIN_FULFILLED: 'recodex/groups/ADD_ADMIN_FULFILLED',
+ ADD_ADMIN_REJECTED: 'recodex/groups/ADD_ADMIN_REJECTED',
+ REMOVE_ADMIN: 'recodex/groups/REMOVE_ADMIN',
+ REMOVE_ADMIN_PENDING: 'recodex/groups/REMOVE_ADMIN_PENDING',
+ REMOVE_ADMIN_FULFILLED: 'recodex/groups/REMOVE_ADMIN_FULFILLED',
+ REMOVE_ADMIN_REJECTED: 'recodex/groups/REMOVE_ADMIN_REJECTED'
};
export const loadGroup = actions.pushResource;
@@ -52,9 +56,10 @@ export const validateAddGroup = (name, instanceId, parentGroupId = null) =>
type: 'VALIDATE_ADD_GROUP_DATA',
endpoint: '/groups/validate-add-group-data',
method: 'POST',
- body: parentGroupId === null
- ? { name, instanceId }
- : { name, instanceId, parentGroupId }
+ body:
+ parentGroupId === null
+ ? { name, instanceId }
+ : { name, instanceId, parentGroupId }
});
export const fetchSubgroups = groupId =>
@@ -132,10 +137,10 @@ export const removeSupervisor = (groupId, userId) => dispatch =>
})
).catch(() => dispatch(addNotification('Cannot remove supervisor.', false))); // @todo: Make translatable
-export const makeAdmin = (groupId, userId) => dispatch =>
+export const addAdmin = (groupId, userId) => dispatch =>
dispatch(
createApiAction({
- type: additionalActionTypes.MAKE_ADMIN,
+ type: additionalActionTypes.ADD_ADMIN,
endpoint: `/groups/${groupId}/admin`,
method: 'POST',
meta: { groupId, userId },
@@ -147,6 +152,23 @@ export const makeAdmin = (groupId, userId) => dispatch =>
)
); // @todo: Make translatable
+export const removeAdmin = (groupId, userId) => dispatch =>
+ dispatch(
+ createApiAction({
+ type: additionalActionTypes.REMOVE_ADMIN,
+ endpoint: `/groups/${groupId}/admin/${userId}`,
+ method: 'DELETE',
+ meta: { groupId, userId }
+ })
+ ).catch(() =>
+ dispatch(
+ addNotification(
+ 'Cannot remove this person from admins of the group.',
+ false
+ )
+ )
+ ); // @todo: Make translatable
+
/**
* Reducer
*/
@@ -250,41 +272,60 @@ const reducer = handleActions(
supervisors => supervisors.filter(id => id !== userId)
),
- [additionalActionTypes.MAKE_ADMIN_PENDING]: (
+ [additionalActionTypes.ADD_ADMIN_PENDING]: (
state,
- { payload: { userId }, meta: { groupId } }
+ { payload, meta: { groupId, userId } }
) =>
- state.updateIn(['resources', groupId, 'data'], group =>
- group
- .set('oldAdminId', group.get('adminId'))
- .update('admins', admins =>
- admins.filter(id => id !== userId).push(userId)
- )
- .set('adminId', userId)
+ state.updateIn(
+ ['resources', groupId, 'data', 'primaryAdminsIds'],
+ admins => admins.filter(id => id !== userId).concat([userId])
+ ),
+
+ [additionalActionTypes.ADD_ADMIN_FAILED]: (
+ state,
+ { payload, meta: { groupId, userId } }
+ ) =>
+ state.updateIn(
+ ['resources', groupId, 'data', 'primaryAdminsIds'],
+ admins => admins.filter(id => id !== userId)
),
- [additionalActionTypes.MAKE_ADMIN_FAILED]: (
+ [additionalActionTypes.ADD_ADMIN_FULFILLED]: (
state,
- { payload: { userId }, meta: { groupId } }
+ { payload: { primaryAdminsIds, admins }, meta: { groupId } }
) =>
state.updateIn(['resources', groupId, 'data'], group =>
group
- .update('admins', admins =>
- admins.filter(id => id !== group.get('adminId')).push(userId)
- )
- .set('adminId', group.get('oldAdminId'))
- .remove('oldAdminId')
+ .set('admins', List(admins))
+ .set('primaryAdminsIds', primaryAdminsIds)
+ ),
+
+ [additionalActionTypes.REMOVE_ADMIN_PENDING]: (
+ state,
+ { payload, meta: { groupId, userId } }
+ ) =>
+ state.updateIn(
+ ['resources', groupId, 'data', 'primaryAdminsIds'],
+ admins => admins.filter(id => id !== userId)
+ ),
+
+ [additionalActionTypes.REMOVE_ADMIN_FAILED]: (
+ state,
+ { payload, meta: { groupId, userId } }
+ ) =>
+ state.updateIn(
+ ['resources', groupId, 'data', 'primaryAdminsIds'],
+ admins => admins.filter(id => id !== userId).concat([userId])
),
- [additionalActionTypes.MAKE_ADMIN_FULFILLED]: (
+ [additionalActionTypes.REMOVE_ADMIN_FULFILLED]: (
state,
- { payload: { adminId, admins }, meta: { groupId } }
+ { payload: { primaryAdminsIds, admins }, meta: { groupId } }
) =>
state.updateIn(['resources', groupId, 'data'], group =>
group
- .remove('oldAdminId')
.set('admins', List(admins))
- .set('adminId', adminId)
+ .set('primaryAdminsIds', primaryAdminsIds)
),
[additionalActionTypes.LOAD_USERS_GROUPS_FULFILLED]: (