Skip to content

Commit

Permalink
BE-793 Admin User Panel lists all users, allows admin to delete users (
Browse files Browse the repository at this point in the history
…#188)

* BE-793 Admin User Panel lists all users, allows admin to delete users

Signed-off-by: MekiaE <dev.edwardsm@gmail.com>

* BE-793 prevent user from deleting themselves, code coverage fix

Signed-off-by: Mekia E <mekiae@Mekias-MacBook-Pro.local>
Signed-off-by: MekiaE <dev.edwardsm@gmail.com>

Co-authored-by: Mekia E <mekiae@Mekias-MacBook-Pro.local>
  • Loading branch information
MekiaE and Mekia E authored Oct 1, 2020
1 parent c7258ac commit e164633
Show file tree
Hide file tree
Showing 12 changed files with 657 additions and 134 deletions.
9 changes: 4 additions & 5 deletions client/src/components/Header/HeaderView.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import AdminPanel from '../Panels/AdminPanel';
import { chartOperations, chartSelectors } from '../../state/redux/charts';
import { tableOperations, tableSelectors } from '../../state/redux/tables';
import { themeSelectors } from '../../state/redux/theme';

import UsersPanal from '../UsersPanal/UsersPanal';
import { authOperations } from '../../state/redux/auth';

import Register from '../Register';
Expand All @@ -48,6 +48,7 @@ import {
getTransactionPerMinType,
refreshType
} from '../types';
import getUserList from '../../state/redux/charts/actions';

const {
blockPerHour,
Expand All @@ -71,7 +72,6 @@ const {

const { currentChannelSelector } = chartSelectors;
const { channelsSelector } = tableSelectors;

/* istanbul ignore next */
const styles = theme => {
const { type } = theme.palette;
Expand Down Expand Up @@ -244,7 +244,6 @@ export class HeaderView extends Component {
label: element.channelname
});
});

this.setState({
currentChannel: currentChannel,
channels: arr,
Expand Down Expand Up @@ -480,7 +479,6 @@ export class HeaderView extends Component {
registerOpen,
notifications
} = this.state;

const links = [
{ to: '/', label: 'DASHBOARD', exact: true },
{ to: '/network', label: 'NETWORK' },
Expand Down Expand Up @@ -610,7 +608,8 @@ export class HeaderView extends Component {
fullWidth={false}
maxWidth="md"
>
<Register onClose={this.registerClose} onRegister={this.onRegister} />
<UsersPanal onClose={this.registerClose} onRegister={this.onRegister} />
{/* <Register onClose={this.registerClose} onRegister={this.onRegister} /> */}
</Dialog>
<Dialog
open={modalOpen}
Expand Down
296 changes: 296 additions & 0 deletions client/src/components/Lists/Users.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,296 @@
import React, { Component } from 'react';

import compose from 'recompose/compose';
import { connect } from 'react-redux';

import { withStyles } from '@material-ui/core/styles';

import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import FormHelperText from '@material-ui/core/FormHelperText';
import Paper from '@material-ui/core/Paper';
import MuiDialogTitle from '@material-ui/core/DialogTitle';
import CloseIcon from '@material-ui/icons/Close';
import IconButton from '@material-ui/core/IconButton';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import SupervisorAccountIcon from '@material-ui/icons/SupervisorAccount';
import PersonIcon from '@material-ui/icons/Person';
import RemoveIcon from '@material-ui/icons/Remove';
import Avatar from '@material-ui/core/Avatar';
import { shape, string } from 'prop-types';
//import userListSelector,{chartOperations} from '../../state/redux/charts';
import { authSelectors, authOperations } from '../../state/redux/auth';
import { userListType, getUserListType } from '../types';
const styles = theme => {
const { type } = theme.palette;
const dark = type === 'dark';
return {
container: {
width: 'auto',
display: 'block', // Fix IE 11 issue.
marginLeft: theme.spacing.unit * 3,
marginRight: theme.spacing.unit * 3,
[theme.breakpoints.up(400 + theme.spacing.unit * 3 * 2)]: {
width: 400,
marginLeft: 'auto',
marginRight: 'auto'
}
},
paper: {
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 3}px ${theme
.spacing.unit * 3}px`
},
form: {
width: '100%', // Fix IE 11 issue.
marginTop: theme.spacing.unit
},
root: {
width: '100%',
maxWidth: 360,
backgroundColor: theme.palette.background.paper
},
title: {
marginTop: theme.spacing.unit * 2
},
actions: {
marginTop: theme.spacing.unit * 3
},
errortext: {
fontSize: 16,
font: 'bold',
color: 'red'
},
successtext: {
fontSize: 16,
font: 'bold',
color: 'green'
},
closeButton: {
position: 'absolute',
right: theme.spacing.unit,
top: theme.spacing.unit,
color: theme.palette.grey[500]
},
adminIcon: {
color: dark ? '#1f1a33' : '#004d6b',
backgroundColor: dark ? '#6a9cf8' : '#b9d6e1'
},
userIcon: {
color: dark ? '#79536d' : '#407b20',
backgroundColor: dark ? '#f7cdea' : '#d0ecda'
},
deleteButton: {
color: '#e33e19'
}
};
};
const { userlist, unregister } = authOperations;

const { userlistSelector, unregisteredSelector } = authSelectors;

export class Users extends Component {
static propTypes = {
classes: shape({
form: string,
container: string,
paper: string,
actions: string
}).isRequired
};

constructor(props) {
super(props);
const { registered, userlists } = props;
this.state = {
rolesList: ['admin', 'user'],
error: '',
registered,
isLoading: false,
allValid: false,
prompt: false,
selectedUser: '',
userlists: userlists,
info: {
status: '',
message: ''
}
};
}

componentWillReceiveProps(nextProps) {
const { registered = [], error, userlists } = nextProps;
this.setState(() => ({
registered,
error,
userlists
}));
}

handleChange = event => {
const { target } = event;
const value = target.type === 'checkbox' ? target.checked : target.value;
const { name } = target;
this.setState({
[name]: { value }
});
};
deletePrompt = username => {
this.setState({ prompt: true });
this.setState({ selectedUser: username });
};
async reloadUsers() {
const { userlists } = this.props;
var index = userlists.findIndex(x => x.username === this.state.selectedUser);
var newUsers = userlists;
newUsers.splice(index, 1);
this.setState({ userlists: newUsers });
}
async removeUser() {
const { unregister } = this.props;
await Promise.all([unregister({ user: this.state.selectedUser })]).then(
message => {
if (message[0].status === 'success') {
this.reloadUsers();
} else {
this.setState({ info: message[0] });
}
}
);
}
render() {
const { classes, onClose } = this.props;
/* const [open, setOpen] = React.useState(false);
*/
const { info } = this.state;
const handleClose = () => {
this.setState({
prompt: false,
info: {
status: '',
message: ''
}
});
};
const handleDelete = () => {
this.setState({ info: { status: '', message: '' } });
this.removeUser();
this.setState({ selectedUser: '' });

handleClose();
};
return (
<div className={classes.container}>
<Paper className={classes.paper}>
{/* <Typography className={classes.title} component="h5" variant="headline">
User List
</Typography> */}
<MuiDialogTitle>
{onClose ? (
<IconButton
aria-label="Close"
className={classes.closeButton}
onClick={onClose}
>
<CloseIcon />
</IconButton>
) : null}
</MuiDialogTitle>
<List className={classes.root}>
{this.state.userlists.map(user => (
<ListItem key={user.username}>
{user.roles === 'admin' ? (
<ListItemAvatar className={classes.adminIcon}>
<Avatar>
<SupervisorAccountIcon />
</Avatar>
</ListItemAvatar>
) : (
<ListItemAvatar className={classes.userIcon}>
<Avatar>
<PersonIcon />
</Avatar>
</ListItemAvatar>
)}
{user.firstName ? (
<ListItemText
primary={user.firstName + ' ' + user.lastName}
secondary={user.username}
/>
) : (
<ListItemText primary="No Name Listed" secondary={user.username} />
)}
<IconButton
className={classes.deleteButton}
color="primary"
aria-label="upload picture"
component="span"
>
<RemoveIcon onClick={() => this.deletePrompt(user.username)} />
</IconButton>
</ListItem>
))}
</List>
{info.status !== 'success' && (
<FormHelperText id="component-error-text" error>
{info.message}
</FormHelperText>
)}
<Dialog
open={this.state.prompt}
keepMounted
onClose={handleClose}
aria-labelledby="alert-dialog-slide-title"
aria-describedby="alert-dialog-slide-description"
>
<DialogTitle id="alert-dialog-slide-title">
{'Delete a user?'}
</DialogTitle>
<DialogContent>
<DialogContentText id="alert-dialog-slide-description">
Are you sure you want to remove this user?
</DialogContentText>
</DialogContent>
<DialogActions>
<Button onClick={handleClose} color="primary">
Cancel
</Button>
<Button onClick={handleDelete} color="primary">
Delete
</Button>
</DialogActions>
</Dialog>
</Paper>
</div>
);
}
}

const { errorSelector } = authSelectors;
Users.propTypes = {
userlists: userListType.isRequired,
userlist: getUserListType.isRequired
};
export default compose(
withStyles(styles),
connect(
state => ({
error: errorSelector(state),
userlists: userlistSelector(state),
unregistered: unregisteredSelector(state)
}),
{
userlist: userlist,
unregister: unregister
}
)
)(Users);
Loading

0 comments on commit e164633

Please sign in to comment.