Skip to content

Commit

Permalink
feat(UI): added manage group-users page
Browse files Browse the repository at this point in the history
Signed-off-by: dushimsam <[email protected]>
  • Loading branch information
dushimsam committed Sep 5, 2022
1 parent 615a373 commit b06e0c5
Show file tree
Hide file tree
Showing 11 changed files with 528 additions and 3 deletions.
6 changes: 6 additions & 0 deletions src/Routes.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ const DeleteGroup = React.lazy(() => import("pages/Admin/Group/Delete"));
const DeleteUser = React.lazy(() => import("pages/Admin/Users/Delete"));
const AddUser = React.lazy(() => import("pages/Admin/Users/Add"));
const EditUser = React.lazy(() => import("pages/Admin/Users/Edit"));
const ManageGroup = React.lazy(() => import("pages/Admin/Group/Manage"));
const AddLicense = React.lazy(() => import("pages/Admin/License/Create"));
const SelectLicense = React.lazy(() =>
import("pages/Admin/License/SelectLicense")
Expand Down Expand Up @@ -292,6 +293,11 @@ const Routes = () => {
path={routes.admin.group.delete}
component={DeleteGroup}
/>
<AdminLayout
exact
path={routes.admin.group.manageGroup}
component={ManageGroup}
/>
<AdminLayout
exact
path={routes.admin.license.create}
Expand Down
39 changes: 39 additions & 0 deletions src/api/groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,3 +76,42 @@ export const deleteGroupApi = (id) => {
addGroupName: false,
});
};

// Get all group members
export const getAllGroupMembersApi = (groupId) => {
const url = endpoints.admin.groups.getAllGroupMembers(groupId);
return sendRequest({
url,
method: "GET",
headers: {
Authorization: getToken(),
},
});
};

// Remove Group Member
export const removeGroupMemberApi = (groupId, userId) => {
const url = endpoints.admin.groups.removeGroupMember(groupId, userId);
return sendRequest({
url,
method: "DELETE",
headers: {
Authorization: getToken(),
},
});
};

// Change user permission
export const changeUserPermissionApi = (groupId, userId, permission) => {
const url = endpoints.admin.groups.changeUserPermission(groupId, userId);
return sendRequest({
url,
method: "PUT",
headers: {
Authorization: getToken(),
},
body: {
perm: permission,
},
});
};
66 changes: 65 additions & 1 deletion src/api/groups.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,12 @@

import sendRequest from "api/sendRequest";
import endpoints from "constants/endpoints";
import { createGroupApi, getAllGroupsApi } from "api/groups";
import {
changeUserPermissionApi,
createGroupApi,
getAllGroupMembersApi,
getAllGroupsApi, removeGroupMemberApi,
} from "api/groups";
import { getToken } from "shared/authHelper";

jest.mock("api/sendRequest");
Expand Down Expand Up @@ -56,4 +61,63 @@ describe("groups", () => {
})
);
});

test("removeGroupMemberApi", () => {
const groupId = 2;
const userId = 1;
const url = endpoints.admin.groups.removeGroupMember(groupId, userId);
sendRequest.mockImplementation(() => true);

expect(removeGroupMemberApi(groupId, userId)).toBe(sendRequest({}));
expect(sendRequest).toHaveBeenCalledWith(
expect.objectContaining({
url,
method: "DELETE",
headers: {
Authorization: getToken(),
},
})
);
});

test("getAllGroupMembersApi", () => {
const groupId = 1;
const url = endpoints.admin.groups.getAllGroupMembers(groupId);
sendRequest.mockImplementation(() => true);

expect(getAllGroupMembersApi(groupId)).toBe(sendRequest({}));
expect(sendRequest).toHaveBeenCalledWith(
expect.objectContaining({
url,
method: "GET",
headers: {
Authorization: getToken(),
},
})
);
});

test("changeUserPermissionApi", () => {
const groupId = 1;
const userId = 1;
const permission = 2;
const url = endpoints.admin.groups.changeUserPermission(groupId, userId);
sendRequest.mockImplementation(() => true);

expect(changeUserPermissionApi(groupId, userId, permission)).toBe(
sendRequest({})
);
expect(sendRequest).toHaveBeenCalledWith(
expect.objectContaining({
url,
method: "PUT",
headers: {
Authorization: getToken(),
},
body: {
perm: permission,
},
})
);
});
});
175 changes: 175 additions & 0 deletions src/components/Admin/ChangePermission.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/*
Copyright (C) 2022 Samuel Dushimimana ([email protected])
SPDX-License-Identifier: GPL-2.0
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

import React, { useEffect, useState } from "react";

import { InputContainer } from "components/Widgets";

// Required functions for calling APIs
import { changeUserPermission, removeGroupMember } from "services/groups";

import PropTypes from "prop-types";

// Required constants
import { userPermissions } from "constants/constants";

const ChangePermissionContainer = ({
groupMembers,
noneGroupMembers,
setShowMessage,
setMessage,
currGroup,
handleFetchGroupMembers,
}) => {
const [currUser, setCurrentUser] = useState(null);
const [currNonMember, setCurrentNonMember] = useState(null);

useEffect(() => {
if (groupMembers.length > 0) {
setCurrentUser({
user: groupMembers[0].id,
perm: groupMembers[0].group_perm,
});
}
}, [groupMembers]);

useEffect(() => {
if (noneGroupMembers.length > 0) {
setCurrentNonMember({
user: noneGroupMembers[0].id,
perm: -1,
});
}
}, [noneGroupMembers]);

const handleChangeCurrUser = async (newUser, isMember = true) => {
if (isMember) {
let perm;
groupMembers.forEach((item) => {
if (item.id === parseInt(newUser, 10)) {
perm = item.group_perm;
}
});
setCurrentUser({ user: parseInt(newUser, 10), perm });
} else {
setCurrentNonMember({ user: parseInt(newUser, 10), perm: -1 });
}
};

const handleSetNewPermission = async (newPerm, isMember = true) => {
try {
let res;

if (parseInt(newPerm, 10) === -1) {
res = await removeGroupMember(currGroup, currUser.user);
} else {
res = await changeUserPermission(
currGroup,
isMember ? currUser.user : currNonMember.user,
parseInt(newPerm, 10)
);
}

setShowMessage(true);
setMessage({
type: "success",
text: res.message,
});

handleFetchGroupMembers(currGroup);
} catch (e) {
setMessage({
type: "danger",
text: e.message,
});
} finally {
setTimeout(() => {
setShowMessage(false);
}, [3000]);
}
};
return (
<>
<tr>
<td>
<InputContainer
type="select"
name="name"
options={groupMembers}
id="select-user-tag"
value={currUser?.user}
property="name"
onChange={(e) => handleChangeCurrUser(e.target.value)}
/>
</td>
<td>
<InputContainer
type="select"
name="name"
options={userPermissions}
id="select-tag"
value={currUser?.perm}
property="name"
onChange={(e) => handleSetNewPermission(e.target.value)}
/>
</td>
</tr>

{noneGroupMembers.length > 0 ? (
<tr>
<td>
<InputContainer
type="select"
name="name"
options={noneGroupMembers}
id="select-user-tag"
value={currNonMember?.user}
property="name"
onChange={(e) => handleChangeCurrUser(e.target.value, false)}
/>
</td>
<td>
<InputContainer
type="select"
name="name"
options={userPermissions}
id="select-tag"
value={-1}
property="name"
onChange={(e) => handleSetNewPermission(e.target.value, false)}
/>
</td>
</tr>
) : (
<></>
)}
</>
);
};

ChangePermissionContainer.propTypes = {
groupMembers: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.any)).isRequired,
noneGroupMembers: PropTypes.arrayOf(PropTypes.objectOf(PropTypes.any))
.isRequired,
setMessage: PropTypes.func,
currGroup: PropTypes.number,
handleFetchGroupMembers: PropTypes.func,
setShowMessage: PropTypes.func,
};

export default ChangePermissionContainer;
4 changes: 2 additions & 2 deletions src/components/Header/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -258,9 +258,9 @@ const Header = () => {
</NavDropdown.Item>
<NavDropdown.Item
as={Link}
to={routes.admin.group.delete}
to={routes.admin.group.manageGroup}
>
Delete Group
Manage Group Users
</NavDropdown.Item>
</div>
</DropdownButton>
Expand Down
3 changes: 3 additions & 0 deletions src/components/Widgets/Input/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const InputContainer = ({
id,
className,
onChange,
defaultValue = null,
children,
checked = false,
placeholder = null,
Expand Down Expand Up @@ -70,6 +71,7 @@ const InputContainer = ({
className ? `mr-2 form-control ${className}` : `mr-2 form-control`
}
value={value}
defaultValue={defaultValue}
onChange={onChange}
multiple={multiple && multiple}
size={multiple ? "15" : ""}
Expand Down Expand Up @@ -125,6 +127,7 @@ InputContainer.propTypes = {
onChange: PropTypes.func,
checked: PropTypes.bool,
disabled: PropTypes.bool,
defaultValue: PropTypes.string,
children: PropTypes.node,
options: PropTypes.arrayOf(
PropTypes.shape({
Expand Down
20 changes: 20 additions & 0 deletions src/constants/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,23 @@ export const agents = {
reso: "REUSE.Software Analysis (forces *Ojo License Analysis*)",
heritage: "Software Heritage Analysis",
};

// eslint-disable-next-line camelcase
export const userPermissions = [
{
id: -1,
name: "None",
},
{
id: 0,
name: "User",
},
{
id: 1,
name: "Admin",
},
{
id: 2,
name: "Advisor",
},
];
5 changes: 5 additions & 0 deletions src/constants/endpoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ const endpoints = {
getAll: () => `${apiUrl}/groups`,
getAllDeletable: () => `${apiUrl}/groups/deletable`,
delete: (groupId) => `${apiUrl}/groups/${groupId}`,
getAllGroupMembers: (groupId) => `${apiUrl}/groups/${groupId}/members`,
changeUserPermission: (groupId, userId) =>
`${apiUrl}/groups/${groupId}/user/${userId}`,
removeGroupMember: (groupId, userId) =>
`${apiUrl}/groups/${groupId}/user/${userId}`,
},
},
license: {
Expand Down
1 change: 1 addition & 0 deletions src/constants/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ const routes = {
group: {
create: "/admin/group/create",
delete: "/admin/group/delete",
manageGroup: "/admin/group/manage",
},
users: {
add: "/admin/users/add",
Expand Down
Loading

0 comments on commit b06e0c5

Please sign in to comment.