diff --git a/src/Routes.jsx b/src/Routes.jsx index 7f5f48603..34466f8dd 100644 --- a/src/Routes.jsx +++ b/src/Routes.jsx @@ -91,6 +91,7 @@ const UploadDelete = React.lazy(() => import("pages/Organize/Uploads/Delete")); // Admin Pages const GroupCreate = React.lazy(() => import("pages/Admin/Group/Create")); const DeleteUser = React.lazy(() => import("pages/Admin/Users/Delete")); +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") @@ -284,6 +285,11 @@ const Routes = () => { path={routes.admin.group.create} component={GroupCreate} /> + { 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(), + }, + }); +}; + +// 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, + }, + }); +}; diff --git a/src/components/Header/index.jsx b/src/components/Header/index.jsx index b1c310d0a..5236a4c42 100644 --- a/src/components/Header/index.jsx +++ b/src/components/Header/index.jsx @@ -255,9 +255,9 @@ const Header = () => { - Delete Group + Manage Group Users diff --git a/src/components/Widgets/Input/index.jsx b/src/components/Widgets/Input/index.jsx index e1a404435..60fbd6b50 100644 --- a/src/components/Widgets/Input/index.jsx +++ b/src/components/Widgets/Input/index.jsx @@ -26,6 +26,7 @@ const InputContainer = ({ id, className, onChange, + defaultValue = null, children, checked = false, placeholder = null, @@ -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" : ""} @@ -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({ diff --git a/src/constants/constants.js b/src/constants/constants.js index 857b325f9..47fb01b07 100644 --- a/src/constants/constants.js +++ b/src/constants/constants.js @@ -260,3 +260,23 @@ export const initialMantainanceFields = { rmvRepoOldFiles1: false, rmvRepoOldFiles2: false, }; + +// eslint-disable-next-line camelcase +export const userPermissions = [ + { + id: -1, + name: "None", + }, + { + id: 0, + name: "User", + }, + { + id: 1, + name: "Admin", + }, + { + id: 2, + name: "Advisor", + }, +]; diff --git a/src/constants/endpoints.js b/src/constants/endpoints.js index ef4986113..3cb9a850d 100644 --- a/src/constants/endpoints.js +++ b/src/constants/endpoints.js @@ -71,6 +71,9 @@ const endpoints = { groups: { create: () => `${apiUrl}/groups`, getAll: () => `${apiUrl}/groups`, + getAllGroupMembers: (groupId) => `${apiUrl}/groups/${groupId}/members`, + changeUserPermission: (groupId, userId) => + `${apiUrl}/groups/${groupId}/user/${userId}`, }, }, license: { diff --git a/src/constants/routes.js b/src/constants/routes.js index 358782b2b..5643df79c 100644 --- a/src/constants/routes.js +++ b/src/constants/routes.js @@ -67,6 +67,7 @@ const routes = { group: { create: "/admin/group/create", delete: "/admin/group/delete", + manageGroup: "/admin/group/manage", }, users: { delete: "/admin/users/delete", diff --git a/src/pages/Admin/Group/Manage/index.jsx b/src/pages/Admin/Group/Manage/index.jsx new file mode 100644 index 000000000..cff2c3ab1 --- /dev/null +++ b/src/pages/Admin/Group/Manage/index.jsx @@ -0,0 +1,206 @@ +/* + Copyright (C) 2022 Samuel Dushimimana (dushsam100@gmail.com) + + 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 messages from "constants/messages"; + +// Title +import Title from "components/Title"; + +// Widgets +import { Alert, InputContainer } from "components/Widgets"; + +// Required functions for calling APIs +import { + changeUserPermission, + fetchAllGroupMembers, + fetchAllGroups, +} from "services/groups"; + +import PropTypes from "prop-types"; +import { userPermissions } from "../../../../constants/constants"; + +const ChangePermissionContainer = ({ + perm, + setShowMessage, + setMessage, + currGroup, + member, + handleFetchGroupMembers, +}) => { + const [selectedPerm, setSelectedPerm] = useState(perm); + + useEffect(() => { + setSelectedPerm(perm); + }, [perm]); + + const handleSetNewPermission = async (newPerm) => { + setSelectedPerm(newPerm); + try { + const res = await changeUserPermission( + currGroup, + member?.user.id, + newPerm + ); + setShowMessage(true); + setMessage({ + type: "success", + text: res.message, + }); + handleFetchGroupMembers(currGroup); + } catch (e) { + setMessage({ + type: "danger", + text: e.message, + }); + } finally { + setTimeout(() => { + setShowMessage(false); + }, [3000]); + } + }; + return ( + + {member?.user.name} + + handleSetNewPermission(e.target.value)} + /> + + + ); +}; + +ChangePermissionContainer.propTypes = { + perm: PropTypes.number, + setMessage: PropTypes.func, + currGroup: PropTypes.number, + member: PropTypes.node, + handleFetchGroupMembers: PropTypes.func, + setShowMessage: PropTypes.func, +}; + +const GroupCreate = () => { + const initialMessage = { + type: "success", + text: "", + }; + + const [currGroup, setCurrentGroup] = useState(null); + const [groupMembers, setGroupMembers] = useState([]); + const [groups, setGroups] = useState([]); + + const [showMessage, setShowMessage] = useState(false); + const [message, setMessage] = useState(initialMessage); + + useEffect(async () => { + try { + const res = await fetchAllGroups(); + const resGrpMembers = await fetchAllGroupMembers(res[0].id); + setCurrentGroup(res[0].id); + setGroups(res); + setGroupMembers(resGrpMembers); + } catch (e) { + setMessage({ + type: "danger", + text: e.message, + }); + } + }, []); + + const handleFetchGroupMembers = async (groupId) => { + try { + const res = await fetchAllGroupMembers(groupId); + setGroupMembers(res); + } catch (error) { + setMessage({ + type: "danger", + text: error.message, + }); + } + }; + + const handleGroupChange = async (e) => { + setCurrentGroup(e.target.value); + await handleFetchGroupMembers(e.target.value); + }; + + return ( + <> + + <div className="main-container my-3"> + {showMessage && ( + <Alert + type={message.type} + setShow={setShowMessage} + message={message.text} + /> + )} + <h1 className="font-size-main-heading">Manage Group Users</h1> + <br /> + <div className="row"> + <div className="col-12 col-lg-8"> + <form> + <InputContainer + type="select" + name="name" + options={groups} + id="select-tag" + property="name" + onChange={(e) => handleGroupChange(e)} + value={currGroup} + > + Select group to manage: + </InputContainer> + </form> + + <table className="table table-striped table-bordered rounded mt-5"> + <thead className="bg-dark text-light font-weight-bold"> + <tr> + <th>User</th> + <th>Permission</th> + </tr> + </thead> + <tbody> + {groupMembers?.map((member) => ( + <ChangePermissionContainer + currGroup={currGroup} + member={member} + handleFetchGroupMembers={handleFetchGroupMembers} + perm={member.group_perm} + setShowMessage={setShowMessage} + setMessage={setMessage} + key={member.user.id} + /> + ))} + </tbody> + </table> + </div> + </div> + </div> + </> + ); +}; + +export default GroupCreate; diff --git a/src/services/groups.js b/src/services/groups.js index ff91fd019..57bd7798f 100644 --- a/src/services/groups.js +++ b/src/services/groups.js @@ -16,7 +16,12 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { getAllGroupsApi, createGroupApi } from "api/groups"; +import { + getAllGroupsApi, + createGroupApi, + getAllGroupMembersApi, + changeUserPermissionApi, +} from "api/groups"; import { setLocalStorage, getLocalStorage } from "shared/storageHelper"; // Fetching all the groups @@ -37,3 +42,17 @@ export const createGroup = (name) => { return res; }); }; + +// Get all group members +export const fetchAllGroupMembers = (groupId) => { + return getAllGroupMembersApi(groupId).then((res) => { + return res; + }); +}; + +// Change user permission +export const changeUserPermission = (groupId, userId, permission) => { + return changeUserPermissionApi(groupId, userId, permission).then((res) => { + return res; + }); +};