1
0
Fork 0
mirror of https://git.rwth-aachen.de/acs/public/villas/web/ synced 2025-03-09 00:00:01 +01:00

updated users page

Signed-off-by: Andrii Podriez <andrey5577990@gmail.com>
This commit is contained in:
Andrii Podriez 2024-07-25 13:53:51 +02:00 committed by al3xa23
parent 4284fc1876
commit 3ca56016a4
5 changed files with 570 additions and 0 deletions

View file

@ -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 <http://www.gnu.org/licenses/>.
******************************************************************************/
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 (
<Dialog show={this.props.show} title="Edit user" buttonTitle="Save" onClose={(c) => this.onClose(c)} onReset={() => this.resetState()} valid={true}>
<Form>
<Form.Group as={Col} controlId="username" style={{marginBottom: '15px'}}>
<Form.Label>Username</Form.Label>
<Form.Control type="text" placeholder={this.props.user.username} value={this.state.username} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group as={Col} controlId="mail" style={{marginBottom: '15px'}}>
<Form.Label>E-mail</Form.Label>
<Form.Control type="text" placeholder={this.props.user.mail} value={this.state.mail} onChange={(e) => this.handleChange(e)} />
</Form.Group>
<Form.Group as={Col} controlId="oldPassword" style={{marginBottom: '15px'}}>
<Form.Label>Admin Password</Form.Label>
<Form.Control type="password" placeholder="Enter admin password" value={this.state.oldPassword} onChange={(e) => this.handleChange(e)} />
</Form.Group>
<Form.Group as={Col} controlId="password" style={{marginBottom: '15px'}}>
<Form.Label>New User Password</Form.Label>
<Form.Control type="password" placeholder="Enter password" value={this.state.password} onChange={(e) => this.handleChange(e)} />
</Form.Group>
<Form.Group as={Col} controlId="confirmPassword" style={{marginBottom: '15px'}}>
<Form.Label>Confirm new Password</Form.Label>
<Form.Control type="password" placeholder="Enter password" value={this.state.confirmPassword} onChange={(e) => this.handleChange(e)} />
</Form.Group>
<Form.Group as={Col} controlId="role" style={{marginBottom: '15px'}}>
<Form.Label>Role</Form.Label>
<Form.Control as="select" placeholder={this.props.user.role} value={this.state.role} onChange={(e) => this.handleChange(e)}>
<option key='1' value='Admin'>Administrator</option>
<option key='2' value='User'>User</option>
<option key='3' value='Guest'>Guest</option>
</Form.Control>
</Form.Group>
<Form.Group as={Col} controlId="active">
<Form.Label>Active</Form.Label>
<Form.Control as="select" placeholder="Select Active state" value={this.state.active} onChange={(e) => this.handleChange(e)}>
<option key='1' value='yes'>Yes</option>
<option key='2' value='no'>No</option>
</Form.Control>
</Form.Group>
</Form>
</Dialog>
);
}
}
export default EditUserDialog;

View file

@ -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 <http://www.gnu.org/licenses/>.
******************************************************************************/
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 (
<Dialog
show={this.props.show}
title="New User"
buttonTitle="Add"
onClose={(c) => this.onClose(c)}
onReset={() => this.resetState()}
valid={this.valid}
>
<Form>
<Form.Group as={Col} controlId="username" style={{marginBottom: '15px'}}>
<Form.Label>Username</Form.Label>
<Form.Control type="text" placeholder="Enter username" value={this.state.name} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
<Form.Text>Min 3 characters.</Form.Text>
</Form.Group>
<Form.Group as={Col} controlId="mail" style={{marginBottom: '15px'}}>
<Form.Label>E-mail</Form.Label>
<Form.Control type="text" placeholder="Enter e-mail" value={this.state.mail} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group as={Col} controlId="password" style={{marginBottom: '15px'}}>
<Form.Label>Password</Form.Label>
<Form.Control type="text" placeholder="Enter password" value={this.state.password} onChange={(e) => this.handleChange(e)} />
<Form.Control.Feedback />
</Form.Group>
<Form.Group as={Col} controlId="role">
<Form.Label>Role</Form.Label>
<Form.Control as="select" placeholder="Select role" value={this.state.role} onChange={(e) => this.handleChange(e)}>
<option key='1' value='Admin'>Administrator</option>
<option key='2' value='User'>User</option>
<option key='3' value='Guest'>Guest</option>
</Form.Control>
</Form.Group>
</Form>
</Dialog>
);
}
}
export default NewUserDialog;

View file

@ -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 <http://www.gnu.org/licenses/>.
******************************************************************************/
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 ( <tr>
<td>{key}</td>
<td>{value}</td>
</tr> );
}
renderData() {
let arr = [];
this.props.users.forEach(user => {
arr.push(this.renderRow(user.username, user.id))
})
return arr;
}
render() {
return <Dialog
size='md'
show={this.props.show}
title={'Add to \'' + this.props.scenario + '\'?'}
buttonTitle='Confirm'
onClose={(c) => this.onClose(c)}
valid={true}
>
<Table size='sm' striped>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
{ this.renderData() }
</tbody>
</Table>
</Dialog>;
}
}
export default UsersToScenarioDialog;

View file

@ -0,0 +1,8 @@
export const buttonStyle = {
marginLeft: '10px',
}
export const iconStyle = {
height: '30px',
width: '30px'
}

248
src/pages/users/users.js Normal file
View file

@ -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 (
<div>
<h1>Users
<span className='icon-button'>
<IconButton
childKey={0}
tooltip='Add User'
onClick={() => setIsNewModalOpened(true)}
icon='plus'
buttonStyle={buttonStyle}
iconStyle={iconStyle}
/>
</span>
</h1>
<Table
data={users}
allRowsChecked={areAllUsersChecked}
>
{currentUser.role === "Admin" ?
<DataColumn
title='ID'
dataKey='id'
/>
: <></>
}
{currentUser.role === "Admin" ?
<CheckboxColumn
enableCheckAll
onCheckAll={() => toggleCheckAllUsers()}
allChecked={areAllUsersChecked}
checked={(user) => isUserChecked(user)}
onChecked={(user, event) => handleUserCheck(user, event)}
width='30'
/>
: <></>
}
<DataColumn title='Username' width='150' dataKey='username' />
<DataColumn title='E-mail' dataKey='mail'/>
<DataColumn title='Role' dataKey='role'/>
<DataColumn title='Active' dataKey='active' modifier={(active) => {}}/>
<ButtonColumn
width='200'
align='right'
editButton
deleteButton
onEdit={index => {
setIsEditModalOpened(true);
setUserToEdit(users[index]);
}}
onDelete={index => {
setIsDeleteModalOpened(true);
setUserToDelete(users[index]);
}}
/>
</Table>
<span className='solid-button'>
<DropdownButton
title='Add to Scenario'
onSelect={(id) => {
let scenario;
if(scenarios.length > 0) {
scenario = scenarios.find(s => s.id == id);
}
setScenario(scenario);
setUsersToScenarioModalOpened(true);
}}
>
{scenarios.map(scenario => (
<Dropdown.Item key={scenario.id} eventKey={scenario.id}>{scenario.name}</Dropdown.Item>
))}
</DropdownButton>
</span>
<UsersToScenarioDialog
show={isUsersToScenarioModalOpened}
users={users.filter(user => checkedUsersIDs.includes(user.id))}
scenario={scenario.name}
onClose={(canceled) => handleAddUserToScenario(canceled)}
/>
<NewUserDialog
show={isNewModalOpened}
onClose={(data) => handleAddNewUser(data)}
/>
<EditUserDialog
show={isEditModalOpened}
onClose={(data) => handleEditUser(data)}
user={userToEdit}
/>
<DeleteDialog
title="user"
name={userToDelete.username}
show={isDeleteModalOpened}
onClose={(e) => handleDeleteUser(e)}
/>
</div>
)
}
export default Users;