Adding new Admin UI. Updating Fredy to V3.0.0 as it has been a large rewrite. Thanks for all contributions and help on the way!
This commit is contained in:
Christian Kellner
2021-01-21 16:09:23 +01:00
committed by GitHub
parent 8185bfe818
commit b2847f6834
124 changed files with 9768 additions and 1495 deletions

View File

@@ -0,0 +1,23 @@
import React from 'react';
import { Modal, Header, Icon, Button } from 'semantic-ui-react';
const UserRemovalModal = function UserRemovalModal({ onOk, onCancel }) {
return (
<Modal open={true}>
<Header icon="warning sign" content="Warning" />
<Modal.Content>
<p>Removing this user will also remove all associated jobs.</p>
</Modal.Content>
<Modal.Actions>
<Button color="red" onClick={() => onCancel()}>
<Icon name="remove" /> Cancel
</Button>
<Button color="green" onClick={() => onOk()}>
<Icon name="checkmark" /> Remove
</Button>
</Modal.Actions>
</Modal>
);
};
export default UserRemovalModal;

View File

@@ -0,0 +1,79 @@
import React from 'react';
import ToastContext from '../../components/toasts/ToastContext';
import UserTable from '../../components/table/UserTable';
import { useDispatch, useSelector } from 'react-redux';
import { Button, Icon } from 'semantic-ui-react';
import UserRemovalModal from './UserRemovalModal';
import { xhrDelete } from '../../services/xhr';
import { useHistory } from 'react-router';
import './Users.less';
const Users = function Users() {
const dispatch = useDispatch();
const [loading, setLoading] = React.useState(true);
const users = useSelector((state) => state.user.users);
const ctx = React.useContext(ToastContext);
const [userIdToBeRemoved, setUserIdToBeRemoved] = React.useState(null);
const history = useHistory();
React.useEffect(async () => {
await dispatch.user.getUsers();
setLoading(false);
}, []);
const onUserRemoval = async () => {
try {
await xhrDelete('/api/admin/users', { userId: userIdToBeRemoved });
ctx.showToast({
title: 'Success',
message: 'User successfully remove',
delay: 4000,
backgroundColor: '#87eb8f',
color: '#000',
});
setUserIdToBeRemoved(null);
await dispatch.jobs.getJobs();
await dispatch.user.getUsers();
} catch (error) {
ctx.showToast({
title: 'Error',
message: error,
delay: 8000,
backgroundColor: '#db2828',
color: '#fff',
});
setUserIdToBeRemoved(null);
}
};
return (
<div>
{!loading && (
<React.Fragment>
{userIdToBeRemoved && <UserRemovalModal onCancel={() => setUserIdToBeRemoved(null)} onOk={onUserRemoval} />}
<Button primary className="users__newButton" onClick={() => history.push('/users/new')}>
<Icon name="plus" />
Create new User
</Button>
<UserTable
user={users}
onUserEdit={(userId) => {
history.push(`/users/edit/${userId}`);
}}
onUserRemoval={(userId) => {
setUserIdToBeRemoved(userId);
//throw warning message that all jobs will be removed associated to this user
//check if at least 1 admin is available
}}
/>
</React.Fragment>
)}
</div>
);
};
export default Users;

View File

@@ -0,0 +1,7 @@
.users {
&__newButton{
margin-top:1rem !important;
float: right;
margin-bottom: 1rem !important;
}
}

View File

@@ -0,0 +1,116 @@
import React from 'react';
import ToastContext from '../../../components/toasts/ToastContext';
import { xhrGet, xhrPost } from '../../../services/xhr';
import { useHistory, useParams } from 'react-router';
import { Button, Form } from 'semantic-ui-react';
import { useDispatch } from 'react-redux';
import Switch from 'react-switch';
import './UserMutator.less';
const UserMutator = function UserMutator() {
const params = useParams();
const [username, setUsername] = React.useState('');
const [password, setPassword] = React.useState('');
const [password2, setPassword2] = React.useState('');
const [isAdmin, setIsAdmin] = React.useState(false);
const history = useHistory();
const ctx = React.useContext(ToastContext);
const dispatch = useDispatch();
React.useEffect(async () => {
if (params.userId != null) {
try {
const userJson = await xhrGet(`/api/admin/users/${params.userId}`);
const user = userJson.json;
const defaultName = user?.username || '';
const defaultIsAdmin = user?.isAdmin || false;
setUsername(defaultName);
setIsAdmin(defaultIsAdmin);
} catch (Exception) {
console.error(Exception);
}
}
}, [params.userId]);
const saveUser = async () => {
try {
await xhrPost('/api/admin/users', {
userId: params.userId || null,
username,
password,
password2,
isAdmin,
});
await dispatch.user.getUsers();
ctx.showToast({
title: 'Success',
message: 'User successfully saved...',
delay: 5000,
backgroundColor: '#87eb8f',
color: '#000',
});
history.push('/users');
} catch (Exception) {
console.error(Exception);
ctx.showToast({
title: 'Error',
message: Exception.json.message,
delay: 6000,
backgroundColor: '#db2828',
color: '#fff',
});
}
};
return (
<Form inverted className="userMutator">
<Form.Input
type="text"
label="Username"
maxLength={30}
placeholder="Username"
autoFocus
inverted
width={6}
defaultValue={username}
onChange={(e) => setUsername(e.target.value)}
/>
<Form.Input
type="password"
label="Password"
placeholder="Password"
inverted
width={6}
defaultValue={password}
onChange={(e) => setPassword(e.target.value)}
/>
<Form.Input
type="password"
label="Retype password"
placeholder="Retype password"
inverted
width={6}
defaultValue={password2}
onChange={(e) => setPassword2(e.target.value)}
/>
<Form.Field>
<label>Is user an admin?</label>
<Switch checked={isAdmin} onChange={(checked) => setIsAdmin(checked)} />
</Form.Field>
<Button color="red" onClick={() => history.push('/users')}>
Cancel
</Button>
<Button color="green" onClick={saveUser}>
Save
</Button>
</Form>
);
};
export default UserMutator;

View File

@@ -0,0 +1,3 @@
.userMutator {
margin-top: 2rem ;
}