mirror of
https://git.rwth-aachen.de/acs/public/villas/web/
synced 2025-03-09 00:00:01 +01:00
Merge branch 'usergroups' into fix-requests
This commit is contained in:
commit
6098e87077
4 changed files with 695 additions and 490 deletions
|
@ -15,103 +15,210 @@
|
|||
* 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';
|
||||
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 (<Dialog
|
||||
return (
|
||||
<Dialog
|
||||
show={isModalOpened}
|
||||
title="New User Group"
|
||||
buttonTitle="Add"
|
||||
onClose={handleClose}
|
||||
onReset={handleReset}
|
||||
valid={isValid}>
|
||||
valid={isValid}
|
||||
>
|
||||
<Form>
|
||||
<Form.Group as={Col} controlId="name" style={{marginBottom: '15px'}}>
|
||||
<Form.Group as={Col} controlId="name" className="mt-2">
|
||||
<Form.Label>Name</Form.Label>
|
||||
<Form.Control type="text" placeholder="Enter name" value={name} onChange={handleNameChange} />
|
||||
<Form.Control.Feedback />
|
||||
</Form.Group>
|
||||
<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.Control
|
||||
type="text"
|
||||
placeholder="Enter name"
|
||||
isInvalid={!isValid && name.length < 3}
|
||||
value={name}
|
||||
onChange={handleNameChange}
|
||||
/>
|
||||
<Form.Control.Feedback type="invalid">
|
||||
Name should not at least 3 characters long
|
||||
</Form.Control.Feedback>
|
||||
</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 => <option key={scenario.id} value={scenario.id}>{scenario.name}</option>)}
|
||||
</Form.Control>
|
||||
)}
|
||||
</Form.Group>
|
||||
<Button
|
||||
onClick={addCard}
|
||||
className="mt-2 btn-secondary"
|
||||
disabled={scenarioMappings?.length >= scenarios?.length}
|
||||
>
|
||||
Add Scenario Mapping
|
||||
</Button>
|
||||
<div className="scrollable-list">
|
||||
{scenarioMappings.map((mapping, index) => (
|
||||
<Card key={index} className="mt-2">
|
||||
<Card.Body>
|
||||
<div className="d-flex justify-content-between align-items-start">
|
||||
<Form.Group
|
||||
as={Col}
|
||||
controlId={`radioGroup-${index}`}
|
||||
style={{ marginBottom: "15px", flexGrow: 1 }}
|
||||
>
|
||||
<div>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
id={`addUsersToScenario-${index}`}
|
||||
name={`options-${index}`}
|
||||
label="Add users to scenario"
|
||||
value="addUsersToScenario"
|
||||
checked={!mapping.duplicate}
|
||||
onChange={(e) =>
|
||||
handleRadioChange(index, e.target.value)
|
||||
}
|
||||
/>
|
||||
<Form.Check
|
||||
type="radio"
|
||||
id={`duplicateScenarioForUsers-${index}`}
|
||||
name={`options-${index}`}
|
||||
label="Duplicate scenario for each user"
|
||||
value="duplicateScenarioForUsers"
|
||||
checked={mapping.duplicate}
|
||||
onChange={(e) =>
|
||||
handleRadioChange(index, e.target.value)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</Form.Group>
|
||||
|
||||
<Button
|
||||
variant="danger"
|
||||
onClick={() => removeCard(index)}
|
||||
className="ms-auto"
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<Form.Group controlId={`scenario-${index}`}>
|
||||
<Form.Label>Select Option</Form.Label>
|
||||
{isLoadingScenarios ? (
|
||||
<div>Loading...</div>
|
||||
) : (
|
||||
<Form.Control
|
||||
as="select"
|
||||
isInvalid={!isValid && mapping.scenarioID == ""}
|
||||
value={mapping.scenarioID}
|
||||
onChange={(e) =>
|
||||
handleSelectChange(index, e.target.value)
|
||||
}
|
||||
>
|
||||
<option value="">-- Select scenario --</option>
|
||||
{scenarios.map((scenario) => {
|
||||
//is there already a mapping with this scenarioID?
|
||||
const isOptionUnavailable = scenarioMappings.find(
|
||||
(m) => m.scenarioID == scenario.id
|
||||
);
|
||||
return (
|
||||
<option
|
||||
key={scenario.id}
|
||||
value={scenario.id}
|
||||
style={
|
||||
isOptionUnavailable
|
||||
? { backgroundColor: "lightgray" }
|
||||
: {}
|
||||
}
|
||||
disabled={isOptionUnavailable}
|
||||
>
|
||||
{scenario.name}
|
||||
</option>
|
||||
);
|
||||
})}
|
||||
</Form.Control>
|
||||
)}
|
||||
</Form.Group>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
</Form>
|
||||
</Dialog>);
|
||||
}
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default AddUsergroupDialog;
|
||||
|
|
|
@ -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 <Spinner />;
|
||||
if (isLoading) return <Spinner />;
|
||||
|
||||
if(usergroups){
|
||||
return (<div className="section">
|
||||
if (usergroups) {
|
||||
return (
|
||||
<div className="section">
|
||||
<h1>
|
||||
User Groups
|
||||
<span className="icon-button">
|
||||
<IconButton
|
||||
childKey={0}
|
||||
tooltip="Add Usergroup"
|
||||
onClick={() => setIsAddDialogOpen(true)}
|
||||
icon="plus"
|
||||
buttonStyle={buttonStyle}
|
||||
iconStyle={iconStyle}
|
||||
/>
|
||||
<IconButton
|
||||
childKey={0}
|
||||
tooltip="Add Usergroup"
|
||||
onClick={() => setIsAddDialogOpen(true)}
|
||||
icon="plus"
|
||||
buttonStyle={buttonStyle}
|
||||
iconStyle={iconStyle}
|
||||
/>
|
||||
</span>
|
||||
</h1>
|
||||
<Table data={usergroups}>
|
||||
<DataColumn
|
||||
title='ID'
|
||||
dataKey='id'
|
||||
width={70}
|
||||
/>
|
||||
<LinkColumn
|
||||
title="Name"
|
||||
dataKey="name"
|
||||
link="/usergroup/"
|
||||
linkKey="id"
|
||||
/>
|
||||
<DataColumn
|
||||
title='Last Update'
|
||||
dataKey='updatedAt'
|
||||
modifier={(updatedAt) => stateUpdateModifier(updatedAt)}
|
||||
/>
|
||||
<ButtonColumn
|
||||
width="200"
|
||||
align="right"
|
||||
deleteButton
|
||||
onDelete={(index) => {
|
||||
setDialogUsergroup(usergroups[index]);
|
||||
setIsDeleteDialogOpen(true);
|
||||
}}
|
||||
/>
|
||||
<DataColumn title="ID" dataKey="id" width={70} />
|
||||
<LinkColumn
|
||||
title="Name"
|
||||
dataKey="name"
|
||||
link="/usergroup/"
|
||||
linkKey="id"
|
||||
/>
|
||||
<DataColumn
|
||||
title="Last Update"
|
||||
dataKey="updatedAt"
|
||||
modifier={(updatedAt) => stateUpdateModifier(updatedAt)}
|
||||
/>
|
||||
<ButtonColumn
|
||||
width="200"
|
||||
align="right"
|
||||
deleteButton
|
||||
onDelete={(index) => {
|
||||
setDialogUsergroup(usergroups[index]);
|
||||
setIsDeleteDialogOpen(true);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
|
||||
<AddUsergroupDialog isModalOpened={isAddDialogOpen} onClose={handleAddNewGroup} />
|
||||
<AddUsergroupDialog
|
||||
isModalOpened={isAddDialogOpen}
|
||||
onClose={handleAddNewGroup}
|
||||
/>
|
||||
<DeleteDialog
|
||||
title="scenario"
|
||||
name={dialogUsegroup.name}
|
||||
show={isDeleteDialogOpen}
|
||||
onClose={(isConfirmed) => handleDeleteUsergroup(isConfirmed)}
|
||||
/>
|
||||
</div>);
|
||||
}
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default Usergroups;
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
******************************************************************************/
|
||||
|
||||
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 <Icon icon={active ? 'check' : 'times'} />
|
||||
}
|
||||
|
||||
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 <Icon icon={active ? "check" : "times"} />;
|
||||
};
|
||||
|
||||
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 (
|
||||
<div>
|
||||
<h1>Users
|
||||
<span className='icon-button'>
|
||||
<IconButton
|
||||
childKey={0}
|
||||
tooltip='Add User'
|
||||
onClick={() => setIsNewModalOpened(true)}
|
||||
icon='plus'
|
||||
buttonStyle={buttonStyle}
|
||||
iconStyle={iconStyle}
|
||||
/>
|
||||
</span>
|
||||
</h1>
|
||||
refetchUsers();
|
||||
setIsDeleteModalOpened(false);
|
||||
setUserToDelete({});
|
||||
};
|
||||
|
||||
<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) => getIconForActiveColumn(active)}/>
|
||||
<ButtonColumn
|
||||
width='200'
|
||||
align='right'
|
||||
editButton
|
||||
deleteButton
|
||||
onEdit={index => {
|
||||
setIsEditModalOpened(true);
|
||||
setUserToEdit(users[index]);
|
||||
}}
|
||||
onDelete={index => {
|
||||
setIsDeleteModalOpened(true);
|
||||
setUserToDelete(users[index]);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
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")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<Row>
|
||||
<Col md="auto">
|
||||
{isLoadingScenarios? <Spinner /> : <>
|
||||
<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>
|
||||
|
||||
setUsersToScenarioModalOpened(false);
|
||||
setCheckedUsersIDs([]);
|
||||
setScenario({ name: "" });
|
||||
setAreAllUsersChecked(false);
|
||||
};
|
||||
|
||||
<UsersToScenarioDialog
|
||||
show={isUsersToScenarioModalOpened}
|
||||
users={users.filter(user => checkedUsersIDs.includes(user.id))}
|
||||
scenario={scenario.name}
|
||||
onClose={(canceled) => handleAddUserToScenario(canceled)}
|
||||
/>
|
||||
</>}
|
||||
</Col>
|
||||
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")
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
<Col md="auto">
|
||||
{isLoadingUsergroups? <Spinner /> : <>
|
||||
<span className='solid-button'>
|
||||
<DropdownButton
|
||||
title='Add to Usegroup'
|
||||
onSelect={(id) => {
|
||||
let usergroup;
|
||||
if(usergroups.length > 0) {
|
||||
usergroup = usergroups.find(s => s.id == id);
|
||||
}
|
||||
setUsergroup(usergroup);
|
||||
setUsersToUsegroupModalOpened(true);
|
||||
}}
|
||||
>
|
||||
{usergroups.map(usergroup => (
|
||||
<Dropdown.Item key={usergroup.id} eventKey={usergroup.id}>{usergroup.name}</Dropdown.Item>
|
||||
))}
|
||||
</DropdownButton>
|
||||
</span>
|
||||
setUsersToUsegroupModalOpened(false);
|
||||
setCheckedUsersIDs([]);
|
||||
setUsergroup({ name: "" });
|
||||
setAreAllUsersChecked(false);
|
||||
};
|
||||
|
||||
{/* re-using same modal to implement adding suers to usergroup */}
|
||||
<UsersToScenarioDialog
|
||||
show={isUsersToUsegroupModalOpened}
|
||||
users={users.filter(user => checkedUsersIDs.includes(user.id))}
|
||||
scenario={usergroup.name}
|
||||
onClose={(canceled) => handleAddUsersToUsergroup(canceled)}
|
||||
/>
|
||||
</>}
|
||||
</Col>
|
||||
</Row>
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
<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)}
|
||||
/>
|
||||
const isUserChecked = (user) => {
|
||||
return checkedUsersIDs.includes(user.id);
|
||||
};
|
||||
|
||||
<div className="mt-4">
|
||||
<Usergroups />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
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) => getIconForActiveColumn(active)}
|
||||
/>
|
||||
<ButtonColumn
|
||||
width="200"
|
||||
align="right"
|
||||
editButton
|
||||
deleteButton
|
||||
onEdit={(index) => {
|
||||
setIsEditModalOpened(true);
|
||||
setUserToEdit(users[index]);
|
||||
}}
|
||||
onDelete={(index) => {
|
||||
setIsDeleteModalOpened(true);
|
||||
setUserToDelete(users[index]);
|
||||
}}
|
||||
/>
|
||||
</Table>
|
||||
|
||||
<Row>
|
||||
<Col md="auto">
|
||||
{isLoadingScenarios ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<>
|
||||
<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)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
|
||||
<Col md="auto">
|
||||
{isLoadingUsergroups ? (
|
||||
<Spinner />
|
||||
) : (
|
||||
<>
|
||||
<span className="solid-button">
|
||||
<DropdownButton
|
||||
title="Add to Usegroup"
|
||||
onSelect={(id) => {
|
||||
let usergroup;
|
||||
if (usergroups.length > 0) {
|
||||
usergroup = usergroups.find((s) => s.id == id);
|
||||
}
|
||||
setUsergroup(usergroup);
|
||||
setUsersToUsegroupModalOpened(true);
|
||||
}}
|
||||
>
|
||||
{usergroups.map((usergroup) => (
|
||||
<Dropdown.Item key={usergroup.id} eventKey={usergroup.id}>
|
||||
{usergroup.name}
|
||||
</Dropdown.Item>
|
||||
))}
|
||||
</DropdownButton>
|
||||
</span>
|
||||
|
||||
{/* re-using same modal to implement adding suers to usergroup */}
|
||||
<UsersToScenarioDialog
|
||||
show={isUsersToUsegroupModalOpened}
|
||||
users={users.filter((user) =>
|
||||
checkedUsersIDs.includes(user.id)
|
||||
)}
|
||||
scenario={usergroup.name}
|
||||
onClose={(canceled) => handleAddUsersToUsergroup(canceled)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<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 className="mt-4">
|
||||
<Usergroups />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Users;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue