Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usergroups #440

Merged
merged 12 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 22 additions & 6 deletions src/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
******************************************************************************/

import React from 'react';
import { useRef, useEffect } from 'react';
import { DndProvider } from 'react-dnd';
import { HTML5Backend }from 'react-dnd-html5-backend';
import NotificationSystem from 'react-notification-system';
Expand All @@ -37,6 +38,7 @@ import './styles/login.css';
import branding from './branding/branding';
import Logout from './pages/login/logout';
import Infrastructure from './pages/infrastructure/infrastructure';
import Usergroup from './pages/usergroups/usergroup';
import { useSelector } from 'react-redux';

const App = () => {
Expand All @@ -47,19 +49,28 @@ const App = () => {
return decodedToken.exp < timeNow;
}

const notificationSystem = useRef(null);

useEffect(() => {
NotificationsDataManager.setSystem(notificationSystem.current);

return () => {
NotificationsDataManager.setSystem(null);
};
}, []);

const { isAuthenticated, token, user } = useSelector((state) => state.auth);

if (!isAuthenticated || isTokenExpired(token)) {
console.log("APP redirecting to logout/login");
return (<Redirect to="/logout" />);
} else {

console.log("APP rendering app");
const pages = branding.values.pages;

return (<DndProvider backend={HTML5Backend} >
<div className="app">
<NotificationSystem />
<NotificationSystem ref={notificationSystem} />
<Header />

<div className='app-body app-body-spacing'>
Expand Down Expand Up @@ -96,10 +107,15 @@ const App = () => {
</>
: '' }
{ pages.account ? <Route path="/account"><Account /></Route> : '' }
{ user.role === "Admin" ?
<Route path="/users">
<Users />
</Route>
{ user.role === "Admin" ?
<>
<Route path="/users">
<Users />
</Route>
<Route path="/usergroup/:usergroup">
<Usergroup />
</Route>
</>
: '' }
{ user.role === "Admin" || pages.api ?
<Route path="/api" component={APIBrowser} />
Expand Down
1 change: 1 addition & 0 deletions src/branding/villasweb/villasweb-values.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const villasweb_values = {
home: true,
scenarios: true,
infrastructure: true,
usergroups: true,
account: true,
api: true,
},
Expand Down
1 change: 0 additions & 1 deletion src/pages/infrastructure/ic-action-board.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ const ICActionBoard = (props) => {

const onDelete = () => {
checkedICsIds.forEach((id) => {
console.log(id)
dispatch(deleteIC({token: sessionToken, id: id}));
});

Expand Down
8 changes: 8 additions & 0 deletions src/pages/scenarios/tables/results-table.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {Button} from "react-bootstrap";
import NotificationsFactory from "../../../common/data-managers/notifications-factory";
import notificationsDataManager from "../../../common/data-managers/notifications-data-manager";
import FileSaver from "file-saver";
import moment from "moment";
import {
useGetResultsQuery,
useAddResultMutation,
Expand Down Expand Up @@ -142,6 +143,11 @@ const ResultsTable = (props) => {
setIsDeleteModalOpened(false);
setResultToDelete({});
}

const stateUpdateModifier = (dateString) => {
const date = moment(dateString);
return `${date.fromNow()}`;
};

return (
<div>
Expand Down Expand Up @@ -175,11 +181,13 @@ const ResultsTable = (props) => {
<DataColumn
title='Created at'
dataKey='createdAt'
modifier={(createdAt) => stateUpdateModifier(createdAt)}
width={200}
/>
<DataColumn
title='Last update'
dataKey='updatedAt'
modifier={(updatedAt) => stateUpdateModifier(updatedAt)}
width={200}
/>
<LinkbuttonColumn
Expand Down
96 changes: 96 additions & 0 deletions src/pages/usergroups/dialogs/addScenarioMappingDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/**
* 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 { useState } from 'react';
import { Form, Col, Button} from 'react-bootstrap';
import Dialog from '../../../common/dialogs/dialog';
import { useGetScenariosQuery } from '../../../store/apiSlice';

const AddScenarioMappingDialog = ({isDialogOpened, onClose, mappings}) => {

const [isValid, setIsValid] = useState(false);
const [selectedOption, setSelectedOption] = useState('addUsersToScenario');
const [selectedScenarioID, setSelectedScenarioID] = useState('');

const {data: {scenarios} = {}, isLoading: isLoadingScenarios} = useGetScenariosQuery();

const handleRadioChange = (e) => {
setSelectedOption(e.target.value);
}

const handleClose = (canceled) => {
if(canceled) {
onClose(null);
} else {
onClose({scenarioID: Number(selectedScenarioID), duplicate: selectedOption === 'duplicateScenarioForUsers'});
}
}

const handleSelectChange = (e) => {
setSelectedScenarioID(e.target.value);
setIsValid(e.target.value !== '');
};

return (<Dialog
show={isDialogOpened}
title="New User Group"
buttonTitle="Add"
onClose={handleClose}
onReset={() => {}}
valid={isValid}>
<Form>
<Form.Group as={Col} controlId="radioGroup" style={{ marginBottom: '15px' }}>
<div>
<Form.Check
type="radio"
id="addUsersToScenario"
name="options"
label="Add users to scenario"
value="addUsersToScenario"
checked={selectedOption === 'addUsersToScenario'}
onChange={handleRadioChange}
/>
<Form.Check
type="radio"
id="duplicateScenarioForUsers"
name="options"
label="Duplicate scenario for each user"
value="duplicateScenarioForUsers"
checked={selectedOption === 'duplicateScenarioForUsers'}
onChange={handleRadioChange}
/>
</div>
</Form.Group>

<Form.Group controlId="scenario">
<Form.Label>Select Option</Form.Label>
{isLoadingScenarios ? <div>Loading...</div> : (
<Form.Control as="select" value={selectedScenarioID} onChange={handleSelectChange}>
<option value="">-- Select scenario --</option>
{scenarios.map(scenario => {
//check if existing mappings are already added to the usergroup
if(!mappings.some(mapping => mapping.scenarioID === scenario.id)) return <option key={scenario.id} value={scenario.id}>{scenario.name}</option>;
})}
</Form.Control>
)}
</Form.Group>
</Form>
</Dialog>);
}

export default AddScenarioMappingDialog;
81 changes: 81 additions & 0 deletions src/pages/usergroups/dialogs/addUserToUsergroupDialog.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* 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 { useState, useEffect } from 'react';
import { Form, Col, Dropdown, Badge } from 'react-bootstrap';
import Dialog from '../../../common/dialogs/dialog';
import { useGetUsersQuery } from '../../../store/apiSlice';

const AddUserToUsergroupDialog = ({isModalOpened, onClose, currentUsers}) => {

const [selectedUsers, setSelectedusers] = useState([]);
const [isValid, setIsValid] = useState(false);

const {data: {users} = {}, isLoading: isLoadingUsers} = useGetUsersQuery();

const toggleUser = (event, option) => {
event.preventDefault();
if (selectedUsers.includes(option)) {
setSelectedusers(prevState => ([...prevState.filter((item) => item !== option)]));
} else {
setSelectedusers(prevState => ([...prevState, option]));
}
}

useEffect(() => {
setIsValid(selectedUsers.length > 0);
}, [selectedUsers]);

const handleClose = (canceled) => {
if(!canceled){
onClose(selectedUsers);
} else {
onClose([]);
}
}

const handleReset = () => {
setSelectedusers([]);
}

return (
<Dialog show={isModalOpened} title="Add Users" buttonTitle="Add" onClose={handleClose} onReset={handleReset} valid={isValid}>
<Dropdown autoClose="outside">
<Dropdown.Toggle variant="success" id="dropdown-basic">Select Options</Dropdown.Toggle>
<Dropdown.Menu>
{isLoadingUsers ? <div>Loading...</div> : [...users].filter(user => !currentUsers.some(currentUser => currentUser.id == user.id)).map(user =>
<Dropdown.Item
key={user.id}
onClick={(event) => toggleUser(event, user)}
style={{
backgroundColor: selectedUsers.includes(user) ? '#d3d3d3' : ''
}}
>
{user.username}
</Dropdown.Item>)}
</Dropdown.Menu>
</Dropdown>
<div className='mt-4'>
{selectedUsers.length > 0 ? selectedUsers.map(user =>
<Badge className="fs-6 me-2 mb-2" key={user.id} bg="primary">{user.username}</Badge>) : <div className="fst-italic">No users selected</div>}
</div>
</Dialog>
);
}

export default AddUserToUsergroupDialog;
Loading