Upgrade to material ui 4

Add user management and roles - TBA
Menu Label Renames - TBA
This commit is contained in:
Rick Watson
2019-05-24 12:19:27 +01:00
parent 685420aaed
commit 0c630f0f93
16 changed files with 799 additions and 469 deletions

View File

@ -1,23 +1,26 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ValidatorForm } from 'react-material-ui-form-validator';
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import LinearProgress from '@material-ui/core/LinearProgress';
import Typography from '@material-ui/core/Typography';
import Table from '@material-ui/core/Table';
import TableBody from '@material-ui/core/TableBody';
import TableCell from '@material-ui/core/TableCell';
import TableFooter from '@material-ui/core/TableFooter';
import TableHead from '@material-ui/core/TableHead';
import TableRow from '@material-ui/core/TableRow';
import IconButton from '@material-ui/core/IconButton';
import Chip from '@material-ui/core/Chip';
import EditIcon from '@material-ui/icons/Edit';
import DeleteIcon from '@material-ui/icons/Delete';
import Chip from '@material-ui/core/Chip';
import IconButton from '@material-ui/core/IconButton';
import SectionContent from '../components/SectionContent';
import UserForm from './UserForm';
const styles = theme => ({
loadingSettings: {
@ -43,95 +46,198 @@ const styles = theme => ({
margin: theme.spacing.unit,
},
table: {
'& td, & th': {padding: theme.spacing.unit}
}
'& td, & th': { padding: theme.spacing.unit }
},
actions: {
color: theme.palette.text.secondary,
}
});
function compareUsers(a, b) {
if (a.username < b.username) {
return -1;
}
if (a.username > b.username) {
return 1;
}
return 0;
}
class ManageUsersForm extends React.Component {
render() {
const { classes, users, usersFetched, errorMessage, onSubmit, onReset } = this.props;
return (
<div>
{
!usersFetched ?
constructor(props) {
super(props);
this.state = {};
}
createUser = () => {
this.setState({
creating: true,
user: {
username: "",
password: "",
roles: []
}
});
};
uniqueUsername = username => {
return !this.props.userData.users.find(u => u.username === username);
}
usersValid = username => {
return !!this.props.userData.users.find(u => u.roles.includes("admin"));
}
startEditingUser = user => {
this.setState({
creating: false,
user
});
};
cancelEditingUser = () => {
this.setState({
user: undefined
});
}
sortedUsers(users) {
return users.sort(compareUsers);
}
doneEditingUser = () => {
const { user } = this.state;
const { userData } = this.props;
let { users } = userData;
users = users.filter(u => u.username !== user.username);
users.push(user);
this.props.setData({ ...userData, users });
this.setState({
user: undefined
});
};
handleUserValueChange = name => event => {
const { user } = this.state;
if (user) {
this.setState({
user: {
...user, [name]: event.target.value
}
});
}
};
render() {
const { classes, userData, userDataFetched, errorMessage, onSubmit, onReset, handleValueChange } = this.props;
const { user, creating } = this.state;
return (
<SectionContent title="Manage Users">
{
!userDataFetched ?
<div className={classes.loadingSettings}>
<LinearProgress className={classes.loadingSettingsDetails} />
<Typography variant="h4" className={classes.loadingSettingsDetails}>
Loading...
</Typography>
</div>
: users ?
<ValidatorForm onSubmit={onSubmit}>
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>Username</TableCell>
<TableCell>Password</TableCell>
<TableCell align="center">Role(s)</TableCell>
<TableCell align="center">Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{users.users.map(user => (
<TableRow key={user.username}>
<TableCell component="th" scope="row">
{user.username}
</TableCell>
<TableCell>{user.password}</TableCell>
<TableCell align="center">
<Chip label={user.role} className={classes.chip} />
:
userData ?
user ?
<UserForm
user={user}
creating={creating}
roles={userData.roles}
onDoneEditing={this.doneEditingUser}
onCancelEditing={this.cancelEditingUser}
handleValueChange={this.handleUserValueChange}
uniqueUsername={this.uniqueUsername}
/>
:
<ValidatorForm onSubmit={onSubmit}>
<Table className={classes.table}>
<TableHead>
<TableRow>
<TableCell>Username</TableCell>
<TableCell align="center">Role(s)</TableCell>
<TableCell align="center">Action</TableCell>
</TableRow>
</TableHead>
<TableBody>
{this.sortedUsers(userData.users).map(user => (
<TableRow key={user.username}>
<TableCell component="th" scope="row">
{user.username}
</TableCell>
<TableCell align="center">
{
user.roles.map(role => (
<Chip label={role} className={classes.chip} />
))
}
</TableCell>
<TableCell align="center">
<IconButton aria-label="Delete">
<DeleteIcon />
</IconButton>
<IconButton aria-label="Edit" onClick={() => this.startEditingUser(user)}>
<EditIcon />
</IconButton>
</TableCell>
</TableRow>
))}
</TableBody>
<TableFooter>
<TableRow>
<TableCell colSpan={2}>
{
!this.usersValid() &&
<Typography variant="body1" color="error">
You must have at least one admin user configured.
</Typography>
}
</TableCell>
<TableCell align="center">
<IconButton aria-label="Delete">
<DeleteIcon />
</IconButton>
<IconButton aria-label="Edit">
<EditIcon />
</IconButton>
<Button variant="contained" color="secondary" className={classes.button} onClick={this.createUser}>
Add User
</Button>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
<Button variant="contained" color="primary" className={classes.button} type="submit">
Save
</Button>
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>
Reset
</Button>
</ValidatorForm>
</TableFooter>
</Table>
<Button variant="contained" color="primary" className={classes.button} type="submit">
Save
</Button>
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>
Reset
</Button>
</ValidatorForm>
:
<div className={classes.loadingSettings}>
<SectionContent title="Manage Users">
<Typography variant="h4" className={classes.loadingSettingsDetails}>
{errorMessage}
</Typography>
<Button variant="contained" color="secondary" className={classes.button} onClick={onReset}>
Reset
</Button>
</div>
</Button>
</SectionContent>
}
</div>
</SectionContent>
);
}
}
ManageUsersForm.propTypes = {
classes: PropTypes.object.isRequired,
users: PropTypes.object,
usersFetched: PropTypes.bool.isRequired,
userData: PropTypes.object,
userDataFetched: PropTypes.bool.isRequired,
errorMessage: PropTypes.string,
onSubmit: PropTypes.func.isRequired,
onReset: PropTypes.func.isRequired,
handleValueChange: PropTypes.func.isRequired,
handleCheckboxChange: PropTypes.func.isRequired,
setData: PropTypes.func.isRequired,
handleValueChange: PropTypes.func.isRequired
};
export default withStyles(styles)(ManageUsersForm);

View File

@ -0,0 +1,112 @@
import React from 'react';
import PropTypes from 'prop-types';
import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import { TextValidator, ValidatorForm } from 'react-material-ui-form-validator';
import PasswordValidator from '../components/PasswordValidator';
import Input from '@material-ui/core/Input';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import FormControl from '@material-ui/core/FormControl';
import Select from '@material-ui/core/Select';
import Chip from '@material-ui/core/Chip';
const styles = theme => ({
textField: {
width: "100%"
},
checkboxControl: {
width: "100%"
},
chips: {
display: 'flex',
flexWrap: 'wrap',
},
chip: {
marginRight: theme.spacing.unit,
},
button: {
marginRight: theme.spacing.unit * 2,
marginTop: theme.spacing.unit * 2,
}
});
class UserForm extends React.Component {
componentWillMount() {
ValidatorForm.addValidationRule('uniqueUsername', this.props.uniqueUsername);
}
render() {
const { classes, user, roles, creating, handleValueChange, onDoneEditing, onCancelEditing } = this.props;
return (
<ValidatorForm onSubmit={onDoneEditing}>
<TextValidator
validators={creating ? ['required', 'uniqueUsername', 'matchRegexp:^[a-zA-Z0-9_\\.]{1,24}$'] : []}
errorMessages={creating ? ['Username is required', "That username already exists", "Must be 1-24 characters: alpha numberic, '_' or '.'"] : []}
name="username"
label="Username"
className={classes.textField}
value={user.username}
disabled={!creating}
onChange={handleValueChange('username')}
margin="normal"
/>
<PasswordValidator
validators={['required', 'matchRegexp:^.{0,64}$']}
errorMessages={['Password is required', 'Password must be 64 characters or less']}
name="password"
label="Password"
className={classes.textField}
value={user.password}
onChange={handleValueChange('password')}
margin="normal"
/>
<FormControl className={classes.textField}>
<InputLabel htmlFor="roles">Roles</InputLabel>
<Select
multiple
value={user.roles}
onChange={handleValueChange('roles')}
input={<Input id="roles" />}
renderValue={selected => (
<div className={classes.chips}>
{selected.map(value => (
<Chip key={value} label={value} className={classes.chip} />
))}
</div>
)}
>
{roles.map(name => (
<MenuItem key={name} value={name}>
{name}
</MenuItem>
))}
</Select>
</FormControl>
<Button variant="contained" color="primary" className={classes.button} type="submit">
Save
</Button>
<Button variant="contained" color="secondary" className={classes.button} onClick={onCancelEditing}>
Back
</Button>
</ValidatorForm>
);
}
}
UserForm.propTypes = {
classes: PropTypes.object.isRequired,
user: PropTypes.object.isRequired,
creating: PropTypes.bool.isRequired,
roles: PropTypes.array.isRequired,
onDoneEditing: PropTypes.func.isRequired,
onCancelEditing: PropTypes.func.isRequired,
uniqueUsername: PropTypes.func.isRequired,
handleValueChange: PropTypes.func.isRequired
};
export default withStyles(styles)(UserForm);