From 8de861b2c22bf39b09e7de5ffe39a028e522409e Mon Sep 17 00:00:00 2001 From: Pursottam6003 Date: Tue, 23 Apr 2024 00:35:14 +0530 Subject: [PATCH 1/8] updated the front end design for details view page --- src/config/apiendpoint.js | 1 + .../Organization/ApproveManagerSuggestions.js | 43 ++++++ .../api/Organization/GetManagerSuggestions.js | 43 ++++++ .../Organization/RejectManagerSuggestions.js | 43 ++++++ .../Organization/RequestManagerSuggestions.js | 51 +++++++ src/redux/constants.js | 4 + .../Organization/GetManagerSuggestions.js | 21 +++ src/redux/reducers/index.js | 3 +- .../pages/component/Project/MembersTable.jsx | 144 ++++++++++++------ src/ui/pages/component/Tabs/Invites.jsx | 108 ++++++++++++- .../component/common/DetailsViewPage.jsx | 113 +++++++++++++- 11 files changed, 516 insertions(+), 58 deletions(-) create mode 100644 src/redux/actions/api/Organization/ApproveManagerSuggestions.js create mode 100644 src/redux/actions/api/Organization/GetManagerSuggestions.js create mode 100644 src/redux/actions/api/Organization/RejectManagerSuggestions.js create mode 100644 src/redux/actions/api/Organization/RequestManagerSuggestions.js create mode 100644 src/redux/reducers/Organization/GetManagerSuggestions.js diff --git a/src/config/apiendpoint.js b/src/config/apiendpoint.js index 284fe6659..52d1fff26 100644 --- a/src/config/apiendpoint.js +++ b/src/config/apiendpoint.js @@ -5,6 +5,7 @@ const apiendpoint = { fetch:"/users/account/", getTasks:"/task/", getOrganizations:"/organizations/", + getPendingUsers:"/users/invite/", notification:"/notifications/", getLanguages:"/users/languages/", getDatasets:"/data/", diff --git a/src/redux/actions/api/Organization/ApproveManagerSuggestions.js b/src/redux/actions/api/Organization/ApproveManagerSuggestions.js new file mode 100644 index 000000000..9f9db84b7 --- /dev/null +++ b/src/redux/actions/api/Organization/ApproveManagerSuggestions.js @@ -0,0 +1,43 @@ +/** + * ApproveManagerSuggestions API + */ +import API from "../../../api"; +import ENDPOINTS from "../../../../config/apiendpoint"; +import constants from "../../../constants"; + +export default class RejectManagerSuggestionsAPI extends API { + constructor(userId, timeout = 2000) { + super("PATCH", timeout, false); + this.type = constants.APPROVE_MANAGER_SUGGESTIONS; + this.userId = userId; + this.endpoint = `${super.apiEndPointAuto()}${ENDPOINTS.getPendingUsers}approve_user/?userId=${this.userId}`; + } + + processResponse(res) { + super.processResponse(res); + if (res) { + this.approveManagerSuggestions = res; + } + } + + apiEndPoint() { + return this.endpoint; + } + + getBody() {} + + getHeaders() { + this.headers = { + headers: { + "Content-Type": "application/json", + "Authorization":`JWT ${localStorage.getItem('shoonya_access_token')}` + }, + }; + return this.headers; + } + + getPayload() { + return this.approveManagerSuggestions; + } + } + \ No newline at end of file diff --git a/src/redux/actions/api/Organization/GetManagerSuggestions.js b/src/redux/actions/api/Organization/GetManagerSuggestions.js new file mode 100644 index 000000000..82ee77580 --- /dev/null +++ b/src/redux/actions/api/Organization/GetManagerSuggestions.js @@ -0,0 +1,43 @@ +/** + * GetManagerSuggestion API + */ +import API from "../../../api"; +import ENDPOINTS from "../../../../config/apiendpoint"; +import constants from "../../../constants"; + +export default class GetManagerSuggestionsAPI extends API { + constructor(orgId, timeout = 2000) { + super("GET", timeout, false); + this.type = constants.GET_MANAGER_SUGGESTIONS; + this.orgId = orgId; + this.endpoint = `${super.apiEndPointAuto()}${ENDPOINTS.getPendingUsers}pending_users/?organisation_id=${this.orgId}`; + } + + processResponse(res) { + super.processResponse(res); + if (res) { + this.managerSuggestions = res; + } + } + + apiEndPoint() { + return this.endpoint; + } + + getBody() {} + + getHeaders() { + this.headers = { + headers: { + "Content-Type": "application/json", + "Authorization":`JWT ${localStorage.getItem('shoonya_access_token')}` + }, + }; + return this.headers; + } + + getPayload() { + return this.managerSuggestions; + } + } + \ No newline at end of file diff --git a/src/redux/actions/api/Organization/RejectManagerSuggestions.js b/src/redux/actions/api/Organization/RejectManagerSuggestions.js new file mode 100644 index 000000000..c818b4140 --- /dev/null +++ b/src/redux/actions/api/Organization/RejectManagerSuggestions.js @@ -0,0 +1,43 @@ +/** + * RejectManagerSuggestions API + */ +import API from "../../../api"; +import ENDPOINTS from "../../../../config/apiendpoint"; +import constants from "../../../constants"; + +export default class RejectManagerSuggestionsAPI extends API { + constructor(userId, timeout = 2000) { + super("DELETE", timeout, false); + this.type = constants.DELETE_MANAGER_SUGGESTIONS; + this.userId = userId; + this.endpoint = `${super.apiEndPointAuto()}${ENDPOINTS.getPendingUsers}reject_user/?userId=${this.userId}`; + } + + processResponse(res) { + super.processResponse(res); + if (res) { + this.rejecManagerSuggestion = res; + } + } + + apiEndPoint() { + return this.endpoint; + } + + getBody() {} + + getHeaders() { + this.headers = { + headers: { + "Content-Type": "application/json", + "Authorization":`JWT ${localStorage.getItem('shoonya_access_token')}` + }, + }; + return this.headers; + } + + getPayload() { + return this.rejecManagerSuggestion; + } + } + \ No newline at end of file diff --git a/src/redux/actions/api/Organization/RequestManagerSuggestions.js b/src/redux/actions/api/Organization/RequestManagerSuggestions.js new file mode 100644 index 000000000..5f489a6a7 --- /dev/null +++ b/src/redux/actions/api/Organization/RequestManagerSuggestions.js @@ -0,0 +1,51 @@ +/** + * RequestUsersToOrg API + */ +import API from "../../../api"; +import ENDPOINTS from "../../../../config/apiendpoint"; +import constants from "../../../constants"; + +export default class RequestManagerSuggestions extends API { + constructor(orgId, emails, role, timeout = 2000) { + super("POST", timeout, false); + this.type = constants.INVITE_USERS_TO_ORG; + this.organization_id = orgId; + this.emails = emails; + this.role = role; + this.endpoint = `${super.apiEndPointAuto()}${ENDPOINTS.getPendingUsers}request_user`; + } + + processResponse(res) { + super.processResponse(res); + if (res) { + this.RequestManagerSuggestions = res; + } + } + + apiEndPoint() { + return this.endpoint; + } + + getBody() { + return { + organization_id: this.organization_id, + emails : this.emails, + role : this.role + } + } + + getHeaders() { + this.headers = { + headers: { + "Content-Type": "application/json", + "Authorization":`JWT ${localStorage.getItem('shoonya_access_token')}` + }, + }; + return this.headers; + } + + getPayload() { + return this.RequestManagerSuggestions; + } + } + \ No newline at end of file diff --git a/src/redux/constants.js b/src/redux/constants.js index 48d8d1b77..c1aaae310 100644 --- a/src/redux/constants.js +++ b/src/redux/constants.js @@ -20,6 +20,10 @@ const constants = { GET_TASK_PREDICTION:"GET_TASK_PREDICTION", GET_LANGUAGES:"GET_LANGUAGES", GET_ORGANIZATION_USERS:"GET_ORGANIZATION_USERS", + GET_MANAGER_SUGGESTIONS:"GET_MANAGER_SUGGESTIONS", + DELETE_MANAGER_SUGGESTIONS:"DEL_MANAGER_SUGGESTIONS", + APPROVE_MANAGER_SUGGESTIONS:"APPROVE_MANAGER_SUGGESTIONS", + REQUEST_MANAGER_SUGGESTIONS:"REQUEST_MANAGER_SUGGESTIONS", GET_DATASET_LIST:"GET_DATASET_LIST", GET_TASK_DETAILS:"GET_TASK_DETAILS", GET_QUEUED_TASK_DETAILS:"GET_QUEUED_TASK_DETAILS", diff --git a/src/redux/reducers/Organization/GetManagerSuggestions.js b/src/redux/reducers/Organization/GetManagerSuggestions.js new file mode 100644 index 000000000..e5b66cd49 --- /dev/null +++ b/src/redux/reducers/Organization/GetManagerSuggestions.js @@ -0,0 +1,21 @@ +import constants from "../../constants"; + +let initialState = { + data: [] +} +const reducer = (state = initialState, action) => { + switch (action.type) { + case constants.GET_MANAGER_SUGGESTIONS: + return { + ...state, + data: action.payload + } + + default: + return { + ...state + } + } +}; + +export default reducer; diff --git a/src/redux/reducers/index.js b/src/redux/reducers/index.js index d1a4c66c4..83fc50f34 100644 --- a/src/redux/reducers/index.js +++ b/src/redux/reducers/index.js @@ -13,6 +13,7 @@ import getWorkspaceDetails from './WorkspaceDetails/GetWorkspaceDetails' import getTaskPrediction from './Tasks/GetTaskPrediction'; import fetchLanguages from './UserManagement/FetchLanguages'; import getOrganizationUsers from './Organization/GetOragnizationUsers'; +import getManagerSuggestions from './Organization/GetManagerSuggestions'; import getDatasetList from './Dataset/GetDatasetList'; import getTaskDetails from './Tasks/GetTaskDetails' import getQueuedTaskDetails from './Tasks/GetQueuedTaskDetails' @@ -202,7 +203,7 @@ const index = { getAnnotationsTask, patchAnnotation, updateUIPrefs, - + getManagerSuggestions, }; export default index; \ No newline at end of file diff --git a/src/ui/pages/component/Project/MembersTable.jsx b/src/ui/pages/component/Project/MembersTable.jsx index 302f19e56..c8d117fc2 100644 --- a/src/ui/pages/component/Project/MembersTable.jsx +++ b/src/ui/pages/component/Project/MembersTable.jsx @@ -34,45 +34,11 @@ import RemoveFrozenUserAPI from "../../../../redux/actions/api/ProjectDetails/Re import userRoles from "../../../../utils/UserMappedByRole/Roles"; import TextField from '@mui/material/TextField'; import LoginAPI from "../../../../redux/actions/api/UserManagement/Login"; +import RejectManagerSuggestionsAPI from "../../../../redux/actions/api/Organization/RejectManagerSuggestions"; +import ApproveManagerSuggestionsAPI from "../../../../redux/actions/api/Organization/ApproveManagerSuggestions"; + + -const columns = [ - { - name: "Name", - label: "Name", - options: { - filter: false, - sort: false, - align: "center", - setCellHeaderProps: (sort) => ({ - style: { height: "70px", padding: "16px" }, - }), - }, - }, - { - name: "Email", - label: "Email", - options: { - filter: false, - sort: false, - }, - }, - { - name: "Role", - label: "Role", - options: { - filter: false, - sort: false, - }, - }, - { - name: "Actions", - label: "Actions", - options: { - filter: false, - sort: false, - }, - }, -]; const options = { filterType: "checkbox", @@ -103,8 +69,12 @@ const MembersTable = (props) => { const { dataSource, hideButton, + showInvitedBy, onRemoveSuccessGetUpdatedMembers, reSendButton, + approveButton, + rejectButton, + hideViewButton, } = props; const [snackbar, setSnackbarInfo] = useState({ open: false, @@ -127,6 +97,54 @@ const MembersTable = (props) => { (state) => state.fetchLoggedInUserData.data ); + const columns = [ + { + name: "Name", + label: "Name", + options: { + filter: false, + sort: false, + align: "center", + setCellHeaderProps: (sort) => ({ + style: { height: "70px", padding: "16px" }, + }), + }, + }, + { + name: "Email", + label: "Email", + options: { + filter: false, + sort: false, + }, + }, + { + name: "Role", + label: "Role", + options: { + filter: false, + sort: false, + }, + }, + { + name: "Actions", + label: "Actions", + options: { + filter: false, + sort: false, + }, + }, + // showInvitedBy && { + // name: "Invited By", + // label: "Invited By", + // options: { + // filter: false, + // sort: false, + // }, + + // }, + ]; + const pageSearch = () => { return dataSource.filter((el) => { if (SearchWorkspaceMembers == "") { @@ -149,6 +167,16 @@ const MembersTable = (props) => { userDetails && setUserRole(userDetails.role); }, []); + const handleApproveUser=(userId)=>{ + const projectObj = new ApproveManagerSuggestionsAPI(userId); + dispatch(APITransport(projectObj)); + } + + const handleRejectUser=(userId)=>{ + const projectObj = new RejectManagerSuggestionsAPI(userId); + dispatch(APITransport(projectObj)); + + } const handleUserDialogClose = () => { setAddUserDialogOpen(false); }; @@ -328,15 +356,18 @@ const MembersTable = (props) => { el.username, el.email, userRole ? userRole : el.role, - <> - { - navigate(`/profile/${el.id}`); - }} - label={"View"} - /> - + + <> + + {!hideViewButton && ( + { + navigate(`/profile/${el.id}`); + }} + label={"View"} + /> + )} {(userRoles.WorkspaceManager === loggedInUserData?.role || userRoles.OrganizationOwner === loggedInUserData?.role || userRoles.Admin === loggedInUserData?.role && props.type === addUserTypes.PROJECT_ANNOTATORS) && ( { label={"Resend"} /> )} + { + approveButton && ( + handleApproveUser(el.id)} + label={"Approve"} + /> + ) + } + { + rejectButton && ( + handleRejectUser(el.id)} + label={"Reject"} + /> + ) + } , diff --git a/src/ui/pages/component/Tabs/Invites.jsx b/src/ui/pages/component/Tabs/Invites.jsx index 1d3feb4ad..182367c41 100644 --- a/src/ui/pages/component/Tabs/Invites.jsx +++ b/src/ui/pages/component/Tabs/Invites.jsx @@ -7,28 +7,132 @@ import APITransport from '../../../../redux/actions/apitransport/apitransport'; import UserMappedByRole from "../../../../utils/UserMappedByRole/UserMappedByRole"; import MembersTable from "../Project/MembersTable"; import GetOragnizationUsersAPI from "../../../../redux/actions/api/Organization/GetOragnizationUsers"; +import GetManagerSuggestionsAPI from "../../../../redux/actions/api/Organization/GetManagerSuggestions"; +import Tab from '@mui/material/Tab'; +import Tabs from '@mui/material/Tabs'; +import Box from '@mui/material/Box'; +import { FormControl } from "@mui/material"; + +const mockdata = [ + { + "id": 316, + "username": "Translator Gujarati - LSB", + "email": "translatorguj1@gmail.com", + "first_name": "", + "last_name": "", + "role": 2, + "invited_by": "Vasudev Aital", + "has_accepted_invite": true + }, + { + "id": 320, + "username": "rmanojvarma", + "email": "rmanojvarma@gmail.com", + "first_name": "Manoj", + "last_name": "varma", + "role": 3, + "invited_by": "Vasudev Aital", + "has_accepted_invite": true + }, + { + "id": 344, + "username": "sandesh", + "email": "sandeshprabhudesai@gmail.com", + "first_name": "Sandesh", + "last_name": "Prabhudesai", + "role": 3, + "invited_by": "Vasudev Aital", + "has_accepted_invite": true + }, + { + "id": 383, + "username": "Vasudev Aital", + "email": "vasu.aital@gmail.com", + "first_name": "Vasudev", + "last_name": "Aital", + "role": 2, + "invited_by": "Vasudev Aital", + "has_accepted_invite": true + }, + { + "id": 148, + "username": "dhakaram.kafle@gmail.com", + "email": "dhakaram.kafle@gmail.com", + "first_name": "Dhaka Ram", + "last_name": "Kafle", + "role": 2, + "invited_by": "Vasudev Aital", + "has_accepted_invite": true + }, +] const Invites = (props) => { + const userDetails = useSelector((state) => state.fetchLoggedInUserData.data); const {hideButton,reSendButton} = props; const dispatch = useDispatch(); const { orgId } = useParams(); const OrganizationUserData = useSelector(state => state.getOrganizationUsers.data); + const ManagerSuggestions = useSelector(state => state.getManagerSuggestions.data); + const [tabValue, setTabValue] = useState(0); const getOrganizationMembersData = () => { const organizationUsersObj = new GetOragnizationUsersAPI(orgId); dispatch(APITransport(organizationUsersObj)); + console.log(OrganizationUserData) + } + + const getManagerSuggestions = () => { + const managerSuggestionsObj = new GetManagerSuggestionsAPI(orgId); + dispatch(APITransport(managerSuggestionsObj)); + } + const handleTabChange = (e, v) => { + setTabValue(v); } useEffect(() => { + if(userDetails) + { + console.log(userDetails) + } getOrganizationMembersData(); + getManagerSuggestions(); }, []); return ( - + + + + {userDetails && userDetails.role === 6 && + + + + + } + + + + {tabValue === 0 ? + 0 && OrganizationUserData.filter((el, i) => { return !el.has_accepted_invite })} - /> + /> + : <> + + + } + + ) } diff --git a/src/ui/pages/component/common/DetailsViewPage.jsx b/src/ui/pages/component/common/DetailsViewPage.jsx index 9fc3e09c3..513ef762d 100644 --- a/src/ui/pages/component/common/DetailsViewPage.jsx +++ b/src/ui/pages/component/common/DetailsViewPage.jsx @@ -49,7 +49,10 @@ import MetaAnalytics from "../../container/Progress/Workspace/MetaAnalytics"; import ProgressAnalytics from "../../container/Progress/Workspace/ProgressAnalytics"; import { DriveEta } from "@material-ui/icons"; import PerformanceAnalytics from "../../container/Progress/Workspace/PerformanceAnalytics"; - +import InviteUsersDialog from "./InviteUsersDialog"; +import UserRolesList from "../../../../utils/UserMappedByRole/UserRolesList"; +import GetOragnizationUsersAPI from "../../../../redux/actions/api/Organization/GetOragnizationUsers" +import InviteUsersToOrgAPI from "../../../../redux/actions/api/Organization/InviteUsersToOrgAPI" function TabPanel(props) { const { children, value, index, ...other } = props; @@ -90,7 +93,17 @@ const DetailsViewPage = (props) => { setAnchorEl(event.currentTarget); }; - + const [addUserDialogOpen, setAddUserDialogOpen] = useState(false); + const [csvFile, setCsvFile] = useState(null); + const [selectedUsers, setSelectedUsers] = useState([]); + const [userType, setUserType] = useState(Object.keys(UserRolesList)[0]); + const [selectedEmails, setSelectedEmails] = useState([]); + const [btn,setbtn] = useState(null); + const [snackbar, setSnackbarInfo] = useState({ + open: false, + message: "", + variant: "success", + }); const handleMenuClose = () => { setAnchorEl(null); }; @@ -145,6 +158,50 @@ const DetailsViewPage = (props) => { setAddWorkspacesDialogOpen(true); }; + const handleUserDialogOpen = () => { + setAddUserDialogOpen(true); + }; + const handleUserDialogClose = () => { + setAddUserDialogOpen(false); + }; + + const addBtnClickHandler = async () => { + setLoading(true); + const addMembersObj = new InviteUsersToOrgAPI( + orgId, + selectedUsers, + userType + ); + const res = await fetch(addMembersObj.apiEndPoint(), { + method: "POST", + body: JSON.stringify(addMembersObj.getBody()), + headers: addMembersObj.getHeaders().headers, + }); + const resp = await res.json(); + if (res.ok) { + setSnackbarInfo({ + open: true, + message: resp?.message, + variant: "success", + }); + const orgObj = new GetOragnizationUsersAPI(id); + dispatch(APITransport(orgObj)); + }else { + setSnackbarInfo({ + open: true, + message: resp?.message, + variant: "error", + }); + } + handleUserDialogClose(); + setLoading(false); + setSelectedUsers([ ]); + setSelectedEmails([]); + setCsvFile(null); + setbtn(null) + setUserType(Object.keys(UserRolesList)[0]) + console.log('todo') + }; useEffect(() => { setLoading(apiLoading); }, [apiLoading]); @@ -352,12 +409,52 @@ const DetailsViewPage = (props) => { {pageType === componentType.Type_Workspace && ( <> -