From 540faad66a3b796f6b49401c5064312740d06bef Mon Sep 17 00:00:00 2001 From: Andrii Podriez Date: Wed, 13 Nov 2024 15:29:47 +0100 Subject: [PATCH] Updated modal for new usergroups Signed-off-by: Andrii Podriez --- .../usergroups/dialogs/addUsergroupDialog.js | 263 +++++--- src/pages/usergroups/usergroups.js | 177 ++--- src/pages/users/users.js | 638 ++++++++++-------- src/styles/app.css | 107 +-- 4 files changed, 695 insertions(+), 490 deletions(-) diff --git a/src/pages/usergroups/dialogs/addUsergroupDialog.js b/src/pages/usergroups/dialogs/addUsergroupDialog.js index cac9f9d..118aa84 100644 --- a/src/pages/usergroups/dialogs/addUsergroupDialog.js +++ b/src/pages/usergroups/dialogs/addUsergroupDialog.js @@ -15,103 +15,210 @@ * along with VILLASweb. If not, see . ******************************************************************************/ -import React from 'react'; -import { useState } from 'react'; -import { Form, Col, Button} from 'react-bootstrap'; -import Dialog from '../../../common/dialogs/dialog'; -import { useGetScenariosQuery } from '../../../store/apiSlice'; +import React, { useEffect } from "react"; +import { useState } from "react"; +import { Form, Col, Button, Card } from "react-bootstrap"; +import Dialog from "../../../common/dialogs/dialog"; +import { useGetScenariosQuery } from "../../../store/apiSlice"; -const AddUsergroupDialog = ({isModalOpened, onClose}) => { +const AddUsergroupDialog = ({ isModalOpened, onClose }) => { + const [name, setName] = useState(""); + const [isValid, setIsValid] = useState(true); + //each mapping is a 'scenarioID' and a bool value 'duplicate' that indicates wether this scenario is duplicated for each user in the group + const [scenarioMappings, setScenarioMappings] = useState([]); - const [name, setName] = useState(''); - const [isValid, setIsValid] = useState(false); - const [selectedOption, setSelectedOption] = useState('addUsersToScenario'); - const [selectedScenarioID, setSelectedScenarioID] = useState(''); + const { data: { scenarios } = {}, isLoading: isLoadingScenarios } = + useGetScenariosQuery(); - const {data: {scenarios} = {}, isLoading: isLoadingScenarios} = useGetScenariosQuery(); + const checkValidity = (newName, newMappings) => { + //group names have to be at lest 3 characters long and cannot start with spaces + const isNameValid = newName.length >= 3 && !/^\s/.test(newName); + //scenario ID is chosen from the dropdown therefore we just make sure that empty option is not selected + const areMappingsValid = newMappings.every( + (mapping) => mapping.scenarioID != "" + ); - const checkValidity = (name, scenarioID) => { - //group names have to be at lest 3 characters long and cannot start with spaces - const isNameValid = name.length >= 3 && !(/^\s/.test(name)); - //scenario ID is chosen from the dropdown therefore we just make sure that empty option is not selected - const isScenarioIDValid = scenarioID !== ''; - setIsValid(isNameValid && isScenarioIDValid); + setIsValid(isNameValid && areMappingsValid); + + //this is only for the check before the submition + return isNameValid && areMappingsValid; + }; + + const handleNameChange = (e) => { + const newName = e.target.value; + setName(newName); + if (!isValid) checkValidity(newName, scenarioMappings); + }; + + const handleRadioChange = (index, value) => { + const updatedMappings = [...scenarioMappings]; + updatedMappings[index].duplicate = value === "duplicateScenarioForUsers"; + setScenarioMappings(updatedMappings); + }; + + const handleClose = (canceled) => { + if (canceled) { + onClose(null); + } else { + if (checkValidity(name, scenarioMappings)) { + const scenarioMappingsModified = [...scenarioMappings].map( + (mapping) => ({ + ...mapping, + scenarioID: Number(mapping.scenarioID), + }) + ); + onClose({ + name: name.trim(), + scenarioMappings: scenarioMappingsModified, + }); + } } + }; - const handleNameChange = (e) => { - const newName = e.target.value; - setName(newName); - checkValidity(newName, selectedScenarioID); - } + const handleReset = () => { + setName(""); + setScenarioMappings([]); + }; - const handleRadioChange = (e) => { - setSelectedOption(e.target.value); - } + const handleSelectChange = (index, value) => { + const updatedMappings = [...scenarioMappings]; + updatedMappings[index].scenarioID = value; + setScenarioMappings(updatedMappings); + if (!isValid) checkValidity(name, updatedMappings); + }; - const handleClose = (canceled) => { - if(canceled) { - onClose(null); - } else { - onClose({name: name, scenarioID: selectedScenarioID, isDuplicateSelected: selectedOption === 'duplicateScenarioForUsers'}); - } - } + const addCard = () => + setScenarioMappings((prevState) => [ + ...prevState, + { scenarioID: "", duplicate: false }, + ]); - const handleReset = () => { - setName(''); - } + const removeCard = (index) => { + const updatedMappings = scenarioMappings.filter((_, i) => i !== index); + setScenarioMappings(updatedMappings); + if (!isValid) checkValidity(name, updatedMappings); + }; - const handleSelectChange = (e) => { - setSelectedScenarioID(e.target.value); - checkValidity(name, e.target.value); - }; - - return ( + valid={isValid} + >
- + Name - - - - -
- - -
+ + + Name should not at least 3 characters long +
- - Select Option - {isLoadingScenarios ?
Loading...
: ( - - - {scenarios.map(scenario => )} - - )} -
+ +
+ {scenarioMappings.map((mapping, index) => ( + + +
+ +
+ + handleRadioChange(index, e.target.value) + } + /> + + handleRadioChange(index, e.target.value) + } + /> +
+
+ + +
+ + + Select Option + {isLoadingScenarios ? ( +
Loading...
+ ) : ( + + handleSelectChange(index, e.target.value) + } + > + + {scenarios.map((scenario) => { + //is there already a mapping with this scenarioID? + const isOptionUnavailable = scenarioMappings.find( + (m) => m.scenarioID == scenario.id + ); + return ( + + ); + })} + + )} +
+
+
+ ))} +
-
); -} + + ); +}; export default AddUsergroupDialog; diff --git a/src/pages/usergroups/usergroups.js b/src/pages/usergroups/usergroups.js index 9e24bb4..c2b9564 100644 --- a/src/pages/usergroups/usergroups.js +++ b/src/pages/usergroups/usergroups.js @@ -16,8 +16,17 @@ ******************************************************************************/ import { useState } from "react"; -import { useGetUsergroupsQuery, useAddUsergroupMutation,useDeleteUsergroupMutation } from "../../store/apiSlice"; -import { Table, DataColumn, LinkColumn, ButtonColumn } from "../../common/table"; +import { + useGetUsergroupsQuery, + useAddUsergroupMutation, + useDeleteUsergroupMutation, +} from "../../store/apiSlice"; +import { + Table, + DataColumn, + LinkColumn, + ButtonColumn, +} from "../../common/table"; import IconButton from "../../common/buttons/icon-button"; import { buttonStyle, iconStyle } from "./styles"; import AddUsergroupDialog from "./dialogs/addUsergroupDialog"; @@ -28,102 +37,114 @@ import NotificationsFactory from "../../common/data-managers/notifications-facto import { Spinner } from "react-bootstrap"; const Usergroups = () => { - const {data: {usergroups} = {}, refetch: refetchUsergroups, isLoading} = useGetUsergroupsQuery(); - const [addUsergroup] = useAddUsergroupMutation(); - const [deleteUsergroup] = useDeleteUsergroupMutation(); - - const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); - const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); - const [dialogUsegroup, setDialogUsergroup] = useState({}); + const { + data: { usergroups } = {}, + refetch: refetchUsergroups, + isLoading, + } = useGetUsergroupsQuery(); + const [addUsergroup] = useAddUsergroupMutation(); + const [deleteUsergroup] = useDeleteUsergroupMutation(); - const handleAddNewGroup = async (response) => { - if(response){ - try { - //put data in correct structure - const usergroup = {Name: response.name, ScenarioMappings: [{Duplicate: response.isDuplicateSelected, ScenarioID: Number(response.scenarioID)}]}; - await addUsergroup(usergroup).unwrap(); - - refetchUsergroups(); - } catch (err) { - notificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR(err.data.message)); - } + const [isAddDialogOpen, setIsAddDialogOpen] = useState(false); + const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); + const [dialogUsegroup, setDialogUsergroup] = useState({}); + + const handleAddNewGroup = async (response) => { + if (response) { + try { + //put data in correct structure + const usergroup = { + name: response.name, + scenarioMappings: response.scenarioMappings, + }; + await addUsergroup(usergroup).unwrap(); + + refetchUsergroups(); + } catch (err) { + notificationsDataManager.addNotification( + NotificationsFactory.UPDATE_ERROR(err.data.message) + ); + } + } + setIsAddDialogOpen(false); + }; + + const handleDeleteUsergroup = async (isConfirmed) => { + if (isConfirmed) { + try { + await deleteUsergroup(dialogUsegroup.id); + refetchUsergroups(); + } catch (err) { + notificationsDataManager.addNotification( + NotificationsFactory.UPDATE_ERROR(err.data.message) + ); } - setIsAddDialogOpen(false); } - const handleDeleteUsergroup = async (isConfirmed) => { - if(isConfirmed){ - try{ - await deleteUsergroup(dialogUsegroup.id); - refetchUsergroups(); - } catch (err) { - notificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR(err.data.message)); - } - } + setDialogUsergroup({}); + setIsDeleteDialogOpen(false); + }; - setDialogUsergroup({}); - setIsDeleteDialogOpen(false); - } + const stateUpdateModifier = (dateString) => { + const date = moment(dateString); + return `${date.fromNow()}`; + }; - const stateUpdateModifier = (dateString) => { - const date = moment(dateString); - return `${date.fromNow()}`; - }; - - if(isLoading) return ; + if (isLoading) return ; - if(usergroups){ - return (
+ if (usergroups) { + return ( +

User Groups - setIsAddDialogOpen(true)} - icon="plus" - buttonStyle={buttonStyle} - iconStyle={iconStyle} - /> + setIsAddDialogOpen(true)} + icon="plus" + buttonStyle={buttonStyle} + iconStyle={iconStyle} + />

- - - stateUpdateModifier(updatedAt)} - /> - { - setDialogUsergroup(usergroups[index]); - setIsDeleteDialogOpen(true); - }} - /> + + + stateUpdateModifier(updatedAt)} + /> + { + setDialogUsergroup(usergroups[index]); + setIsDeleteDialogOpen(true); + }} + />
- + handleDeleteUsergroup(isConfirmed)} /> -
); - } -} +
+ ); + } +}; export default Usergroups; diff --git a/src/pages/users/users.js b/src/pages/users/users.js index 6e9c5e9..5d49d41 100644 --- a/src/pages/users/users.js +++ b/src/pages/users/users.js @@ -1,7 +1,29 @@ +/** + * This file is part of VILLASweb. + * + * VILLASweb is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * VILLASweb 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 VILLASweb. If not, see . + ******************************************************************************/ + import { useState } from "react"; import { useSelector } from "react-redux"; -import { Dropdown, DropdownButton, Spinner, Row, Col } from 'react-bootstrap'; -import { Table, ButtonColumn, CheckboxColumn, DataColumn } from "../../common/table"; +import { Dropdown, DropdownButton, Spinner, Row, Col } from "react-bootstrap"; +import { + Table, + ButtonColumn, + CheckboxColumn, + DataColumn, +} from "../../common/table"; import Icon from "../../common/icon"; import IconButton from "../../common/buttons/icon-button"; import NewUserDialog from "./dialogs/new-user"; @@ -12,313 +34,363 @@ import { buttonStyle, iconStyle } from "./styles"; import NotificationsFactory from "../../common/data-managers/notifications-factory"; import notificationsDataManager from "../../common/data-managers/notifications-data-manager"; import Usergroups from "../usergroups/usergroups"; -import { - useGetUsersQuery, - useAddUserMutation, - useUpdateUserMutation, - useDeleteUserMutation, - useGetScenariosQuery, - useAddUserToScenarioMutation, - useGetUsergroupsQuery, - useAddUserToUsergroupMutation +import { + useGetUsersQuery, + useAddUserMutation, + useUpdateUserMutation, + useDeleteUserMutation, + useGetScenariosQuery, + useAddUserToScenarioMutation, + useGetUsergroupsQuery, + useAddUserToUsergroupMutation, } from "../../store/apiSlice"; const Users = () => { + const { user: currentUser, token: sessionToken } = useSelector( + (state) => state.auth + ); - const { user: currentUser, token: sessionToken } = useSelector((state) => state.auth); + const { data: fetchedUsers, refetch: refetchUsers } = useGetUsersQuery(); + const users = fetchedUsers ? fetchedUsers.users : []; + const { data: { scenarios } = [], isLoading: isLoadingScenarios } = + useGetScenariosQuery(); + const { data: { usergroups } = [], isLoading: isLoadingUsergroups } = + useGetUsergroupsQuery(); + const [checkedUsersIDs, setCheckedUsersIDs] = useState([]); + const [addUserMutation] = useAddUserMutation(); + const [updateUserMutation] = useUpdateUserMutation(); + const [deleteUserMutation] = useDeleteUserMutation(); + const [addUserToScenarioMutation] = useAddUserToScenarioMutation(); + const [addUserToUsergroup] = useAddUserToUsergroupMutation(); + const [isNewModalOpened, setIsNewModalOpened] = useState(false); + const [isEditModalOpened, setIsEditModalOpened] = useState(false); + const [isDeleteModalOpened, setIsDeleteModalOpened] = useState(false); + const [scenario, setScenario] = useState({ name: "" }); + const [usergroup, setUsergroup] = useState({ name: "" }); + const [isUsersToScenarioModalOpened, setUsersToScenarioModalOpened] = + useState(false); + const [isUsersToUsegroupModalOpened, setUsersToUsegroupModalOpened] = + useState(false); + const [userToEdit, setUserToEdit] = useState({}); + const [userToDelete, setUserToDelete] = useState({}); + const [areAllUsersChecked, setAreAllUsersChecked] = useState(false); - const {data: fetchedUsers, refetch: refetchUsers} = useGetUsersQuery(); - const users = fetchedUsers ? fetchedUsers.users : []; - const { data: {scenarios} = [], isLoading: isLoadingScenarios } = useGetScenariosQuery(); - const {data: {usergroups} = [], isLoading: isLoadingUsergroups } = useGetUsergroupsQuery(); - const [checkedUsersIDs, setCheckedUsersIDs] = useState([]); - const [addUserMutation] = useAddUserMutation(); - const [updateUserMutation] = useUpdateUserMutation(); - const [deleteUserMutation] = useDeleteUserMutation(); - const [addUserToScenarioMutation] = useAddUserToScenarioMutation(); - const [addUserToUsergroup] = useAddUserToUsergroupMutation(); - const [isNewModalOpened, setIsNewModalOpened] = useState(false); - const [isEditModalOpened, setIsEditModalOpened] = useState(false); - const [isDeleteModalOpened, setIsDeleteModalOpened] = useState(false); - const [scenario, setScenario] = useState({name: ''}); - const [usergroup, setUsergroup] = useState({name: ''}); - const [isUsersToScenarioModalOpened, setUsersToScenarioModalOpened] = useState(false); - const [isUsersToUsegroupModalOpened, setUsersToUsegroupModalOpened] = useState(false); - const [userToEdit, setUserToEdit] = useState({}); - const [userToDelete, setUserToDelete] = useState({}); - const [areAllUsersChecked, setAreAllUsersChecked] = useState(false); - - const handleAddNewUser = async (data) => { - if(data){ - try { - const res = await addUserMutation({user: data}); - if(res.error){ - notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR(res.error.data.message)); - } - refetchUsers(); - } catch (error){ - if(error.data){ - notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR(error.data.message)); - } else { - notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR("Unknown error")); - } - } + const handleAddNewUser = async (data) => { + if (data) { + try { + const res = await addUserMutation({ user: data }); + if (res.error) { + notificationsDataManager.addNotification( + NotificationsFactory.LOAD_ERROR(res.error.data.message) + ); } - - setIsNewModalOpened(false); - } - - const getIconForActiveColumn = (active) => { - return - } - - const handleEditUser = async (data) => { - if(data){ - try { - await updateUserMutation(data); - refetchUsers(); - } catch (error) { - if(error.data){ - notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR(error.data.message)); - } else { - notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR("Unknown error")); - } - } - } - refetchUsers(); - setIsEditModalOpened(false); - setUserToEdit({}); + } catch (error) { + if (error.data) { + notificationsDataManager.addNotification( + NotificationsFactory.LOAD_ERROR(error.data.message) + ); + } else { + notificationsDataManager.addNotification( + NotificationsFactory.LOAD_ERROR("Unknown error") + ); + } + } } - const handleDeleteUser = async (isConfimed) => { - if(isConfimed){ - try { - await deleteUserMutation(userToDelete.id); - } catch(error) { - if(error.data){ - notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR(error.data.message)); - } else { - notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR("Unknown error")); - } - } - } + setIsNewModalOpened(false); + }; + const getIconForActiveColumn = (active) => { + return ; + }; + + const handleEditUser = async (data) => { + if (data) { + try { + await updateUserMutation(data); refetchUsers(); - setIsDeleteModalOpened(false); - setUserToDelete({}); - } - - const handleAddUserToScenario = async (isCanceled) => { - if(!isCanceled){ - try { - for(let i = 0; i < checkedUsersIDs.length; i++){ - await addUserToScenarioMutation({ scenarioID: scenario.id, username: users.find(u => u.id === checkedUsersIDs[i]).username }).unwrap(); - } - } catch (error) { - if(error.data){ - notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR(error.data.message)); - } else { - notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR("Unknown error")); - } - } - } - - setUsersToScenarioModalOpened(false); - setCheckedUsersIDs([]); - setScenario({name: ''}); - setAreAllUsersChecked(false); - } - - const handleAddUsersToUsergroup = async (isCanceled) => { - if(!isCanceled){ - try { - for(let i = 0; i < checkedUsersIDs.length; i++){ - await addUserToUsergroup({ usergroupID: usergroup.id, username: users.find(u => u.id === checkedUsersIDs[i]).username }).unwrap(); - } - } catch (error) { - if(error.data){ - notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR(error.data.message)); - } else { - console.log("error", error) - notificationsDataManager.addNotification(NotificationsFactory.LOAD_ERROR("Unknown error")); - } - } - } - - setUsersToUsegroupModalOpened(false); - setCheckedUsersIDs([]); - setUsergroup({name: ''}); - setAreAllUsersChecked(false); - } - - const toggleCheckAllUsers = () => { - if(checkedUsersIDs.length === users.length){ - setCheckedUsersIDs([]); - setAreAllUsersChecked(false); + } catch (error) { + if (error.data) { + notificationsDataManager.addNotification( + NotificationsFactory.LOAD_ERROR(error.data.message) + ); } else { - users.forEach(user => { - if(!checkedUsersIDs.includes(user.id)){ - setCheckedUsersIDs(prevState => ([...prevState, user.id])); - } - }) - setAreAllUsersChecked(true); + notificationsDataManager.addNotification( + NotificationsFactory.LOAD_ERROR("Unknown error") + ); } + } } - const isUserChecked = (user) => { - return checkedUsersIDs.includes(user.id); - } + refetchUsers(); + setIsEditModalOpened(false); + setUserToEdit({}); + }; - const handleUserCheck = (user, event) => { - if(!checkedUsersIDs.includes(user.id)){ - setCheckedUsersIDs(prevState => ([...prevState, user.id])); + const handleDeleteUser = async (isConfimed) => { + if (isConfimed) { + try { + await deleteUserMutation(userToDelete.id); + } catch (error) { + if (error.data) { + notificationsDataManager.addNotification( + NotificationsFactory.LOAD_ERROR(error.data.message) + ); } else { - const index = checkedUsersIDs.indexOf(user.id); - setCheckedUsersIDs(prevState => prevState.filter((_, i) => i !== index)); + notificationsDataManager.addNotification( + NotificationsFactory.LOAD_ERROR("Unknown error") + ); } + } } - return ( -
-

Users - - setIsNewModalOpened(true)} - icon='plus' - buttonStyle={buttonStyle} - iconStyle={iconStyle} - /> - -

+ refetchUsers(); + setIsDeleteModalOpened(false); + setUserToDelete({}); + }; - - {currentUser.role === "Admin" ? - - : <> - } - {currentUser.role === "Admin" ? - toggleCheckAllUsers()} - allChecked={areAllUsersChecked} - checked={(user) => isUserChecked(user)} - onChecked={(user, event) => handleUserCheck(user, event)} - width='30' - /> - : <> - } - - - - getIconForActiveColumn(active)}/> - { - setIsEditModalOpened(true); - setUserToEdit(users[index]); - }} - onDelete={index => { - setIsDeleteModalOpened(true); - setUserToDelete(users[index]); - }} - /> -
+ const handleAddUserToScenario = async (isCanceled) => { + if (!isCanceled) { + try { + for (let i = 0; i < checkedUsersIDs.length; i++) { + await addUserToScenarioMutation({ + scenarioID: scenario.id, + username: users.find((u) => u.id === checkedUsersIDs[i]).username, + }).unwrap(); + } + } catch (error) { + if (error.data) { + notificationsDataManager.addNotification( + NotificationsFactory.LOAD_ERROR(error.data.message) + ); + } else { + notificationsDataManager.addNotification( + NotificationsFactory.LOAD_ERROR("Unknown error") + ); + } + } + } - - - {isLoadingScenarios? : <> - - { - let scenario; - if(scenarios.length > 0) { - scenario = scenarios.find(s => s.id == id); - } - setScenario(scenario); - setUsersToScenarioModalOpened(true); - }} - > - {scenarios.map(scenario => ( - {scenario.name} - ))} - - - + setUsersToScenarioModalOpened(false); + setCheckedUsersIDs([]); + setScenario({ name: "" }); + setAreAllUsersChecked(false); + }; - checkedUsersIDs.includes(user.id))} - scenario={scenario.name} - onClose={(canceled) => handleAddUserToScenario(canceled)} - /> - } - + const handleAddUsersToUsergroup = async (isCanceled) => { + if (!isCanceled) { + try { + for (let i = 0; i < checkedUsersIDs.length; i++) { + await addUserToUsergroup({ + usergroupID: usergroup.id, + username: users.find((u) => u.id === checkedUsersIDs[i]).username, + }).unwrap(); + } + } catch (error) { + if (error.data) { + notificationsDataManager.addNotification( + NotificationsFactory.LOAD_ERROR(error.data.message) + ); + } else { + notificationsDataManager.addNotification( + NotificationsFactory.LOAD_ERROR("Unknown error") + ); + } + } + } - - {isLoadingUsergroups? : <> - - { - let usergroup; - if(usergroups.length > 0) { - usergroup = usergroups.find(s => s.id == id); - } - setUsergroup(usergroup); - setUsersToUsegroupModalOpened(true); - }} - > - {usergroups.map(usergroup => ( - {usergroup.name} - ))} - - + setUsersToUsegroupModalOpened(false); + setCheckedUsersIDs([]); + setUsergroup({ name: "" }); + setAreAllUsersChecked(false); + }; - {/* re-using same modal to implement adding suers to usergroup */} - checkedUsersIDs.includes(user.id))} - scenario={usergroup.name} - onClose={(canceled) => handleAddUsersToUsergroup(canceled)} - /> - } - - + const toggleCheckAllUsers = () => { + if (checkedUsersIDs.length === users.length) { + setCheckedUsersIDs([]); + setAreAllUsersChecked(false); + } else { + users.forEach((user) => { + if (!checkedUsersIDs.includes(user.id)) { + setCheckedUsersIDs((prevState) => [...prevState, user.id]); + } + }); + setAreAllUsersChecked(true); + } + }; - handleAddNewUser(data)} - /> - handleEditUser(data)} - user={userToEdit} - /> - handleDeleteUser(e)} - /> + const isUserChecked = (user) => { + return checkedUsersIDs.includes(user.id); + }; -
- -
-
- ) -} + const handleUserCheck = (user, event) => { + if (!checkedUsersIDs.includes(user.id)) { + setCheckedUsersIDs((prevState) => [...prevState, user.id]); + } else { + const index = checkedUsersIDs.indexOf(user.id); + setCheckedUsersIDs((prevState) => + prevState.filter((_, i) => i !== index) + ); + } + }; + + return ( +
+

+ Users + + setIsNewModalOpened(true)} + icon="plus" + buttonStyle={buttonStyle} + iconStyle={iconStyle} + /> + +

+ + + {currentUser.role === "Admin" ? ( + + ) : ( + <> + )} + {currentUser.role === "Admin" ? ( + toggleCheckAllUsers()} + allChecked={areAllUsersChecked} + checked={(user) => isUserChecked(user)} + onChecked={(user, event) => handleUserCheck(user, event)} + width="30" + /> + ) : ( + <> + )} + + + + getIconForActiveColumn(active)} + /> + { + setIsEditModalOpened(true); + setUserToEdit(users[index]); + }} + onDelete={(index) => { + setIsDeleteModalOpened(true); + setUserToDelete(users[index]); + }} + /> +
+ + + + {isLoadingScenarios ? ( + + ) : ( + <> + + { + let scenario; + if (scenarios.length > 0) { + scenario = scenarios.find((s) => s.id == id); + } + setScenario(scenario); + setUsersToScenarioModalOpened(true); + }} + > + {scenarios.map((scenario) => ( + + {scenario.name} + + ))} + + + + + checkedUsersIDs.includes(user.id) + )} + scenario={scenario.name} + onClose={(canceled) => handleAddUserToScenario(canceled)} + /> + + )} + + + + {isLoadingUsergroups ? ( + + ) : ( + <> + + { + let usergroup; + if (usergroups.length > 0) { + usergroup = usergroups.find((s) => s.id == id); + } + setUsergroup(usergroup); + setUsersToUsegroupModalOpened(true); + }} + > + {usergroups.map((usergroup) => ( + + {usergroup.name} + + ))} + + + + {/* re-using same modal to implement adding suers to usergroup */} + + checkedUsersIDs.includes(user.id) + )} + scenario={usergroup.name} + onClose={(canceled) => handleAddUsersToUsergroup(canceled)} + /> + + )} + + + + handleAddNewUser(data)} + /> + handleEditUser(data)} + user={userToEdit} + /> + handleDeleteUser(e)} + /> + +
+ +
+
+ ); +}; export default Users; diff --git a/src/styles/app.css b/src/styles/app.css index 8323809..d64075d 100644 --- a/src/styles/app.css +++ b/src/styles/app.css @@ -25,8 +25,8 @@ --highlights: #527984; --secondaryText: #818181; --borderRadius: 0px; - --fontFamily: 'Helvetica Neue', Helvetica, Arial, sans-serif; - --backgroundText:#fff; + --fontFamily: "Helvetica Neue", Helvetica, Arial, sans-serif; + --backgroundText: #fff; } * { @@ -43,7 +43,6 @@ body { color: var(--main); border-radius: var(--borderRadius); - font: 16px; hyphens: auto; } @@ -65,23 +64,22 @@ body { padding: 0 !important; } -a:link { - text-decoration: none; +a:link { + text-decoration: none; } -a:visited { - text-decoration: none; +a:visited { + text-decoration: none; } -a:hover { - text-decoration: none; +a:hover { + text-decoration: none; } -a:active { - text-decoration: none; +a:active { + text-decoration: none; } - .funding-logos img { height: 70px; margin: 0 10px; @@ -113,13 +111,11 @@ a:active { padding: 15px 20px 20px 20px; border-radius: var(--borderRadius); - width: auto; min-height: 300px; background-color: #fff; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 9px 18px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 9px 18px 0 rgba(0, 0, 0, 0.1); } .app-content-margin-left { @@ -148,10 +144,8 @@ a:active { width: 160px; border-radius: var(--borderRadius); - background-color: #fff; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 9px 18px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 9px 18px 0 rgba(0, 0, 0, 0.1); } .menulogo { @@ -163,17 +157,17 @@ a:active { border-radius: var(--borderRadius); background-color: #fff; - box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), - 0 9px 18px 0 rgba(0, 0, 0, 0.1); + box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 9px 18px 0 rgba(0, 0, 0, 0.1); } .menu a { color: var(--secondarytext); - text-decoration:none; + text-decoration: none; } -.menu a:hover, .menu a:focus { - text-decoration:none; +.menu a:hover, +.menu a:focus { + text-decoration: none; } .active { @@ -255,7 +249,7 @@ a:active { } .home-container > img { - margin: 20px; + margin: 20px; } /** @@ -269,18 +263,21 @@ a:active { hr { margin-top: 1rem; margin-bottom: 1rem; - border:0; + border: 0; border-top: 1px solid rgba(0, 0, 0, 0.1); } /** * Tables */ -.table thead,th,td { +.table thead, +th, +td { padding: 0.5em 0.5em !important; } -.table thead,th { +.table thead, +th { background-color: var(--highlights); color: #fff; } @@ -304,11 +301,11 @@ hr { .unselectable { -webkit-touch-callout: none !important; /* iOS Safari */ - -webkit-user-select: none !important; /* Safari */ - -khtml-user-select: none !important; /* Konqueror HTML */ - -moz-user-select: none !important; /* Firefox */ - -ms-user-select: none !important; /* Internet Explorer/Edge */ - user-select: none !important; /* Non-prefixed version, currently + -webkit-user-select: none !important; /* Safari */ + -khtml-user-select: none !important; /* Konqueror HTML */ + -moz-user-select: none !important; /* Firefox */ + -ms-user-select: none !important; /* Internet Explorer/Edge */ + user-select: none !important; /* Non-prefixed version, currently supported by Chrome and Opera */ } @@ -363,7 +360,7 @@ hr { display: -webkit-flex; display: flex; flex-direction: column; - background-color: #FFFFFF; + background-color: #ffffff; } .box-header { @@ -401,7 +398,8 @@ hr { padding-right: 3px; } -.section-header .btn-link:hover, .section-header .btn-link:focus { +.section-header .btn-link:hover, +.section-header .btn-link:focus { text-decoration: none; } @@ -421,22 +419,22 @@ hr { background-color: #ffffff; } -.section-buttons-group-center .btn{ +.section-buttons-group-center .btn { border-color: #ffffff; background-color: #ffffff; } -.section-buttons-group-center .btn:hover{ +.section-buttons-group-center .btn:hover { border-color: #e3e3e3; background-color: #e3e3e3; } -.section-buttons-group-right .btn{ +.section-buttons-group-right .btn { border-color: #ffffff; background-color: #ffffff; } -.section-buttons-group-right .btn:hover{ +.section-buttons-group-right .btn:hover { border-color: #e3e3e3; background-color: #e3e3e3; } @@ -447,52 +445,51 @@ hr { float: left; } -.section-buttons-group-left .btn{ +.section-buttons-group-left .btn { background-color: var(--highlights); border-color: var(--highlights); } -.section-buttons-group-left .btn:hover{ +.section-buttons-group-left .btn:hover { background-color: #31484f; border-color: #31484f; } -.drag-and-drop .btn{ +.drag-and-drop .btn { color: var(--highlights); border-color: var(--highlights); } -.drag-and-drop .btn:hover{ +.drag-and-drop .btn:hover { color: var(--highlights); border-color: var(--highlights); } - .section-buttons-group-right .rc-slider { margin-left: 12px; } -.solid-button .btn{ +.solid-button .btn { background-color: var(--highlights); border-color: var(--highlights); } -.solid-button .btn:hover{ - background-color: #31484f; +.solid-button .btn:hover { + background-color: #31484f; border-color: #31484f; } -.solid-button .btn:disabled{ +.solid-button .btn:disabled { background-color: var(--highlights); border-color: var(--highlights); } -.icon-button .btn{ +.icon-button .btn { border-color: #ffffff; background-color: #ffffff; } -.icon-button .btn:hover{ +.icon-button .btn:hover { border-color: #e3e3e3; background-color: #e3e3e3; } @@ -510,12 +507,11 @@ hr { opacity: 0.4; } - /** * Swagger UI */ .swagger-ui .wrapper { - padding: 0 !important; + padding: 0 !important; } .swagger-ui .info { @@ -526,3 +522,12 @@ hr { background-color: var(--highlights); color: #fff; } + +.scrollable-list { + max-height: 600px; + overflow-y: auto; +} + +.btn-secondary { + background-color: var(--highlights) !important; +}