From 3ca56016a4fbe2406a041e542c547183630c2441 Mon Sep 17 00:00:00 2001 From: Andrii Podriez Date: Thu, 25 Jul 2024 13:53:51 +0200 Subject: [PATCH] updated users page Signed-off-by: Andrii Podriez --- src/pages/users/dialogs/edit-user.js | 136 ++++++++++ src/pages/users/dialogs/new-user.js | 108 ++++++++ src/pages/users/dialogs/users-to-scenario.js | 70 ++++++ src/pages/users/styles.js | 8 + src/pages/users/users.js | 248 +++++++++++++++++++ 5 files changed, 570 insertions(+) create mode 100644 src/pages/users/dialogs/edit-user.js create mode 100644 src/pages/users/dialogs/new-user.js create mode 100644 src/pages/users/dialogs/users-to-scenario.js create mode 100644 src/pages/users/styles.js create mode 100644 src/pages/users/users.js diff --git a/src/pages/users/dialogs/edit-user.js b/src/pages/users/dialogs/edit-user.js new file mode 100644 index 0000000..97fdc20 --- /dev/null +++ b/src/pages/users/dialogs/edit-user.js @@ -0,0 +1,136 @@ +/** + * 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 React from 'react'; +import { Form, Col } from 'react-bootstrap'; +import Dialog from '../../../common/dialogs/dialog'; +import NotificationsDataManager from "../../../common/data-managers/notifications-data-manager"; +import NotificationsFactory from "../../../common/data-managers/notifications-factory"; + +class EditUserDialog extends React.Component { + constructor(props) { + super(props); + + this.state = { + username: '', + mail: '', + role: '', + active: '', + password: "", + confirmPassword: "", + oldPassword: "", + } + } + + onClose(canceled) { + if (canceled === false) { + + let user = {} + user.id = this.props.user.id; + + if (this.state.username != null && this.state.username !== this.props.user.username){ + user.username = this.state.username + } + + if (this.state.mail != null && this.state.mail !== this.props.user.mail){ + user.mail = this.state.mail + } + + if (this.state.role != null && this.state.role !== this.props.user.role){ + user.role = this.state.role + } + + if (this.state.active != null && this.state.active !== this.props.user.active){ + user.active = this.state.active + } + + if (this.state.password !== '' && this.state.oldPassword !== '' && this.state.password === this.state.confirmPassword) { + user.password = this.state.password; + user.oldpassword = this.state.oldPassword + } else if (this.state.password !== '' && this.state.password !== this.state.confirmPassword){ + NotificationsDataManager.addNotification(NotificationsFactory.UPDATE_ERROR("New password not correctly confirmed")) + } + + this.props.onClose(user); + + } else { + this.props.onClose(); + } + } + + handleChange(e) { + this.setState({ [e.target.id]: e.target.value }); + } + + resetState() { + this.setState({ + username: this.props.user.username, + mail: this.props.user.mail, + role: this.props.user.role, + password: "", + confirmPassword: "", + oldPassword: "", + }); + } + + render() { + return ( + this.onClose(c)} onReset={() => this.resetState()} valid={true}> +
+ + Username + this.handleChange(e)} /> + + + + E-mail + this.handleChange(e)} /> + + + Admin Password + this.handleChange(e)} /> + + + New User Password + this.handleChange(e)} /> + + + Confirm new Password + this.handleChange(e)} /> + + + Role + this.handleChange(e)}> + + + + + + + Active + this.handleChange(e)}> + + + + +
+
+ ); + } +} + +export default EditUserDialog; diff --git a/src/pages/users/dialogs/new-user.js b/src/pages/users/dialogs/new-user.js new file mode 100644 index 0000000..4addc41 --- /dev/null +++ b/src/pages/users/dialogs/new-user.js @@ -0,0 +1,108 @@ +/** + * 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 React from 'react'; +import { Form, Col } from 'react-bootstrap'; +import Dialog from '../../../common/dialogs/dialog'; + +class NewUserDialog extends React.Component { + valid = false; + + constructor(props) { + super(props); + + this.state = { + username: '', + mail: '', + role: 'User', + password: '', + }; + } + + onClose(canceled) { + if (canceled === false) { + if (this.valid) { + this.props.onClose(this.state); + } + } else { + this.props.onClose(); + } + } + + handleChange(e) { + this.setState({ [e.target.id]: e.target.value }); + + // check all controls + let username = this.state.username !== '' && this.state.username.length >= 3; + let password = this.state.password !== ''; + let role = this.state.role !== ''; + let mail = this.state.mail !== ''; + + this.valid = username && password && role && mail; + } + + resetState() { + this.setState({ + username: '', + mail: '', + role: 'User', + password: '', + }); + } + + render() { + return ( + this.onClose(c)} + onReset={() => this.resetState()} + valid={this.valid} + > +
+ + Username + this.handleChange(e)} /> + + Min 3 characters. + + + E-mail + this.handleChange(e)} /> + + + + Password + this.handleChange(e)} /> + + + + Role + this.handleChange(e)}> + + + + + +
+
+ ); + } +} + +export default NewUserDialog; diff --git a/src/pages/users/dialogs/users-to-scenario.js b/src/pages/users/dialogs/users-to-scenario.js new file mode 100644 index 0000000..cd19e3d --- /dev/null +++ b/src/pages/users/dialogs/users-to-scenario.js @@ -0,0 +1,70 @@ +/** + * 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 React from 'react'; +import { Table } from 'react-bootstrap'; +import Dialog from '../../../common/dialogs/dialog'; + +class UsersToScenarioDialog extends React.Component { + valid = true; + + onClose(canceled) { + if (this.props.onClose != null) { + this.props.onClose(canceled); + } + }; + + renderRow(value, key) { + return ( + {key} + {value} + ); + } + + renderData() { + let arr = []; + this.props.users.forEach(user => { + arr.push(this.renderRow(user.username, user.id)) + }) + return arr; + } + + render() { + return this.onClose(c)} + valid={true} + > + + + + + + + + + { this.renderData() } + +
IDName
+
; + } +} + +export default UsersToScenarioDialog; diff --git a/src/pages/users/styles.js b/src/pages/users/styles.js new file mode 100644 index 0000000..a075bf2 --- /dev/null +++ b/src/pages/users/styles.js @@ -0,0 +1,8 @@ +export const buttonStyle = { + marginLeft: '10px', + } + +export const iconStyle = { + height: '30px', + width: '30px' +} diff --git a/src/pages/users/users.js b/src/pages/users/users.js new file mode 100644 index 0000000..8d98374 --- /dev/null +++ b/src/pages/users/users.js @@ -0,0 +1,248 @@ +import { useState } from "react"; +import { Dropdown, DropdownButton } 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"; +import EditUserDialog from "./dialogs/edit-user"; +import UsersToScenarioDialog from "./dialogs/users-to-scenario"; +import DeleteDialog from "../../common/dialogs/delete-dialog"; +import { buttonStyle, iconStyle } from "./styles"; +import {currentUser} from '../../localStorage'; +import NotificationsFactory from "../../common/data-managers/notifications-factory"; +import notificationsDataManager from "../../common/data-managers/notifications-data-manager"; +import { + useGetUsersQuery, + useAddUserMutation, + useUpdateUserMutation, + useDeleteUserMutation, + useGetScenariosQuery, + useAddUserToScenarioMutation, +} from "../../store/apiSlice"; + +const Users = ({}) => { + + const {data: fetchedUsers, refetch: refetchUsers} = useGetUsersQuery(); + const users = fetchedUsers ? fetchedUsers.users : []; + const { data: fetchedScenarios } = useGetScenariosQuery(); + const scenarios = fetchedScenarios ? fetchedScenarios.scenarios : []; + const [checkedUsersIDs, setCheckedUsersIDs] = useState([]); + const [addUserMutation] = useAddUserMutation(); + const [updateUserMutation] = useUpdateUserMutation(); + const [deleteUserMutation] = useDeleteUserMutation(); + const [addUserToScenarioMutation] = useAddUserToScenarioMutation(); + const [isNewModalOpened, setIsNewModalOpened] = useState(false); + const [isEditModalOpened, setIsEditModalOpened] = useState(false); + const [isDeleteModalOpened, setIsDeleteModalOpened] = useState(false); + const [scenario, setScenario] = useState({name: ''}); + const [isUsersToScenarioModalOpened, setUsersToScenarioModalOpened] = 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")); + } + } + } + + setIsNewModalOpened(false); + } + + 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({}); + } + + 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")); + } + } + } + + 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) { + console.log('ERROR', 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 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); + } + } + + 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' + /> + : <> + } + + + + {}}/> + { + setIsEditModalOpened(true); + setUserToEdit(users[index]); + }} + onDelete={index => { + setIsDeleteModalOpened(true); + setUserToDelete(users[index]); + }} + /> +
+ + { + 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)} + /> + handleAddNewUser(data)} + /> + handleEditUser(data)} + user={userToEdit} + /> + handleDeleteUser(e)} + /> +
+ ) +} + +export default Users;