2016-10-03 17:54:22 +00:00
|
|
|
import React, { Component, PropTypes } from 'react';
|
|
|
|
|
import { connect } from 'react-redux';
|
2016-11-10 20:29:50 +00:00
|
|
|
import { first, isEqual, size } from 'lodash';
|
2016-10-19 20:22:18 +00:00
|
|
|
|
2016-12-16 15:54:49 +00:00
|
|
|
import entityGetter from 'redux/utilities/entityGetter';
|
|
|
|
|
import Button from 'components/buttons/Button';
|
|
|
|
|
import inviteActions from 'redux/nodes/entities/invites/actions';
|
|
|
|
|
import inviteInterface from 'interfaces/invite';
|
|
|
|
|
import InviteUserForm from 'components/forms/InviteUserForm';
|
|
|
|
|
import Modal from 'components/modals/Modal';
|
|
|
|
|
import userActions from 'redux/nodes/entities/users/actions';
|
|
|
|
|
import userInterface from 'interfaces/user';
|
|
|
|
|
import { renderFlash } from 'redux/nodes/notifications/actions';
|
2016-10-03 17:54:22 +00:00
|
|
|
import UserBlock from './UserBlock';
|
|
|
|
|
|
|
|
|
|
class UserManagementPage extends Component {
|
|
|
|
|
static propTypes = {
|
2016-10-21 23:13:41 +00:00
|
|
|
currentUser: userInterface,
|
2016-10-03 17:54:22 +00:00
|
|
|
dispatch: PropTypes.func,
|
2017-01-06 00:01:17 +00:00
|
|
|
inviteErrors: PropTypes.shape({
|
|
|
|
|
base: PropTypes.string,
|
|
|
|
|
email: PropTypes.string,
|
|
|
|
|
}),
|
2016-10-21 23:13:41 +00:00
|
|
|
invites: PropTypes.arrayOf(inviteInterface),
|
2017-01-06 00:01:17 +00:00
|
|
|
userErrors: PropTypes.shape({
|
|
|
|
|
base: PropTypes.string,
|
|
|
|
|
name: PropTypes.string,
|
|
|
|
|
username: PropTypes.string,
|
|
|
|
|
}),
|
2016-10-21 23:13:41 +00:00
|
|
|
users: PropTypes.arrayOf(userInterface),
|
2016-10-03 17:54:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
constructor (props) {
|
|
|
|
|
super(props);
|
|
|
|
|
|
|
|
|
|
this.state = {
|
|
|
|
|
showInviteUserModal: false,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
componentWillMount () {
|
2016-11-10 20:29:50 +00:00
|
|
|
const { currentUser, dispatch, invites, users } = this.props;
|
2016-10-03 17:54:22 +00:00
|
|
|
|
2016-11-10 20:29:50 +00:00
|
|
|
if (!size(users) ||
|
|
|
|
|
(size(users) === 1 && isEqual(first(users), currentUser))) {
|
2016-10-19 20:22:18 +00:00
|
|
|
dispatch(userActions.loadAll());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!invites.length) {
|
|
|
|
|
dispatch(inviteActions.loadAll());
|
|
|
|
|
}
|
2016-10-03 17:54:22 +00:00
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-04 22:24:39 +00:00
|
|
|
onUserActionSelect = (user, action) => {
|
2016-10-07 17:07:02 +00:00
|
|
|
const { currentUser, dispatch } = this.props;
|
2017-01-06 22:38:39 +00:00
|
|
|
const { update, requirePasswordReset } = userActions;
|
2016-10-03 17:54:22 +00:00
|
|
|
|
2016-10-04 22:24:39 +00:00
|
|
|
if (action) {
|
|
|
|
|
switch (action) {
|
2016-10-07 17:07:02 +00:00
|
|
|
case 'demote_user': {
|
|
|
|
|
if (currentUser.id === user.id) {
|
|
|
|
|
return dispatch(renderFlash('error', 'You cannot demote yourself'));
|
|
|
|
|
}
|
2016-10-03 17:54:22 +00:00
|
|
|
return dispatch(update(user, { admin: false }))
|
|
|
|
|
.then(() => {
|
|
|
|
|
return dispatch(renderFlash('success', 'User demoted', update(user, { admin: true })));
|
|
|
|
|
});
|
2016-10-07 17:07:02 +00:00
|
|
|
}
|
|
|
|
|
case 'disable_account': {
|
|
|
|
|
if (currentUser.id === user.id) {
|
|
|
|
|
return dispatch(renderFlash('error', 'You cannot disable your own account'));
|
|
|
|
|
}
|
2016-10-03 17:54:22 +00:00
|
|
|
return dispatch(userActions.update(user, { enabled: false }))
|
|
|
|
|
.then(() => {
|
|
|
|
|
return dispatch(renderFlash('success', 'User account disabled', update(user, { enabled: true })));
|
|
|
|
|
});
|
2016-10-07 17:07:02 +00:00
|
|
|
}
|
2016-10-03 17:54:22 +00:00
|
|
|
case 'enable_account':
|
|
|
|
|
return dispatch(update(user, { enabled: true }))
|
|
|
|
|
.then(() => {
|
|
|
|
|
return dispatch(renderFlash('success', 'User account enabled', update(user, { enabled: false })));
|
|
|
|
|
});
|
|
|
|
|
case 'promote_user':
|
|
|
|
|
return dispatch(update(user, { admin: true }))
|
|
|
|
|
.then(() => {
|
|
|
|
|
return dispatch(renderFlash('success', 'User promoted to admin', update(user, { admin: false })));
|
|
|
|
|
});
|
|
|
|
|
case 'reset_password':
|
2017-01-06 22:38:39 +00:00
|
|
|
return dispatch(requirePasswordReset(user, { require: true }))
|
2016-10-03 17:54:22 +00:00
|
|
|
.then(() => {
|
2017-01-06 22:38:39 +00:00
|
|
|
return dispatch(renderFlash('success', 'User required to reset password', requirePasswordReset(user, { require: false })));
|
2016-10-03 17:54:22 +00:00
|
|
|
});
|
2016-10-14 21:08:57 +00:00
|
|
|
case 'revert_invitation':
|
2016-12-21 17:07:13 +00:00
|
|
|
return dispatch(inviteActions.destroy(user))
|
2016-12-29 19:04:03 +00:00
|
|
|
.then(() => dispatch(renderFlash('success', 'Invite revoked')))
|
|
|
|
|
.catch(() => dispatch(renderFlash('error', 'Invite could not be revoked')));
|
2016-10-03 17:54:22 +00:00
|
|
|
default:
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-04 22:24:39 +00:00
|
|
|
onEditUser = (user, updatedUser) => {
|
|
|
|
|
const { dispatch } = this.props;
|
|
|
|
|
const { update } = userActions;
|
|
|
|
|
|
|
|
|
|
return dispatch(update(user, updatedUser))
|
|
|
|
|
.then(() => {
|
2017-01-06 00:01:17 +00:00
|
|
|
dispatch(renderFlash('success', 'User updated', update(user, user)));
|
|
|
|
|
|
|
|
|
|
return Promise.resolve();
|
2016-10-04 22:24:39 +00:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-03 17:54:22 +00:00
|
|
|
onInviteUserSubmit = (formData) => {
|
2016-10-11 15:34:52 +00:00
|
|
|
const { dispatch } = this.props;
|
|
|
|
|
|
|
|
|
|
dispatch(inviteActions.create(formData))
|
|
|
|
|
.then(() => {
|
|
|
|
|
dispatch(renderFlash('success', 'User invited'));
|
|
|
|
|
return this.toggleInviteUserModal();
|
|
|
|
|
});
|
2016-10-03 17:54:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onInviteCancel = (evt) => {
|
|
|
|
|
evt.preventDefault();
|
|
|
|
|
|
|
|
|
|
return this.toggleInviteUserModal();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toggleInviteUserModal = () => {
|
|
|
|
|
const { showInviteUserModal } = this.state;
|
|
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
|
showInviteUserModal: !showInviteUserModal,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-06 00:01:17 +00:00
|
|
|
renderUserBlock = (user, idx, options = { invite: false }) => {
|
|
|
|
|
const { currentUser, userErrors } = this.props;
|
2016-10-14 21:08:57 +00:00
|
|
|
const { invite } = options;
|
2016-10-04 22:24:39 +00:00
|
|
|
const { onEditUser, onUserActionSelect } = this;
|
2016-10-03 17:54:22 +00:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<UserBlock
|
2016-10-07 17:07:02 +00:00
|
|
|
currentUser={currentUser}
|
2016-10-14 21:08:57 +00:00
|
|
|
invite={invite}
|
2017-01-06 00:01:17 +00:00
|
|
|
key={`${user.email}-${idx}-${invite ? 'invite' : 'user'}`}
|
2016-10-04 22:24:39 +00:00
|
|
|
onEditUser={onEditUser}
|
2016-10-03 17:54:22 +00:00
|
|
|
onSelect={onUserActionSelect}
|
|
|
|
|
user={user}
|
2017-01-06 00:01:17 +00:00
|
|
|
userErrors={userErrors}
|
2016-10-03 17:54:22 +00:00
|
|
|
/>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
renderModal = () => {
|
2017-01-06 00:01:17 +00:00
|
|
|
const { currentUser, inviteErrors } = this.props;
|
|
|
|
|
const { showInviteUserModal } = this.state;
|
2016-10-03 17:54:22 +00:00
|
|
|
const { onInviteCancel, onInviteUserSubmit, toggleInviteUserModal } = this;
|
|
|
|
|
|
2016-10-19 20:22:18 +00:00
|
|
|
if (!showInviteUserModal) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2016-10-03 17:54:22 +00:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Modal
|
|
|
|
|
title="Invite new user"
|
|
|
|
|
onExit={toggleInviteUserModal}
|
|
|
|
|
>
|
|
|
|
|
<InviteUserForm
|
2017-01-06 00:01:17 +00:00
|
|
|
serverErrors={inviteErrors}
|
2016-10-11 15:34:52 +00:00
|
|
|
invitedBy={currentUser}
|
2016-10-03 17:54:22 +00:00
|
|
|
onCancel={onInviteCancel}
|
|
|
|
|
onSubmit={onInviteUserSubmit}
|
|
|
|
|
/>
|
|
|
|
|
</Modal>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
render () {
|
|
|
|
|
const { toggleInviteUserModal } = this;
|
2016-10-14 21:08:57 +00:00
|
|
|
const { invites, users } = this.props;
|
|
|
|
|
const resourcesCount = users.length + invites.length;
|
2016-10-03 17:54:22 +00:00
|
|
|
|
2016-11-03 19:40:54 +00:00
|
|
|
const baseClass = 'user-management';
|
|
|
|
|
|
2016-10-03 17:54:22 +00:00
|
|
|
return (
|
2016-12-06 16:55:48 +00:00
|
|
|
<div className={`${baseClass} body-wrap`}>
|
2016-12-15 15:45:20 +00:00
|
|
|
<h1 className={`${baseClass}__user-count`}>Listing {resourcesCount} users</h1>
|
2016-11-03 19:40:54 +00:00
|
|
|
<div className={`${baseClass}__add-user-wrap`}>
|
2016-12-28 15:24:52 +00:00
|
|
|
<Button onClick={toggleInviteUserModal} className={`${baseClass}__add-user-btn`}>
|
|
|
|
|
Add User
|
|
|
|
|
</Button>
|
2016-10-03 17:54:22 +00:00
|
|
|
</div>
|
2016-11-03 19:40:54 +00:00
|
|
|
<div className={`${baseClass}__users`}>
|
2017-01-06 00:01:17 +00:00
|
|
|
{users.map((user, idx) => {
|
|
|
|
|
return this.renderUserBlock(user, idx);
|
2016-10-03 17:54:22 +00:00
|
|
|
})}
|
2017-01-06 00:01:17 +00:00
|
|
|
{invites.map((user, idx) => {
|
|
|
|
|
return this.renderUserBlock(user, idx, { invite: true });
|
2016-10-14 21:08:57 +00:00
|
|
|
})}
|
2016-10-03 17:54:22 +00:00
|
|
|
</div>
|
|
|
|
|
{this.renderModal()}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const mapStateToProps = (state) => {
|
2016-10-14 21:08:57 +00:00
|
|
|
const stateEntityGetter = entityGetter(state);
|
2016-10-07 17:07:02 +00:00
|
|
|
const { user: currentUser } = state.auth;
|
2016-10-14 21:08:57 +00:00
|
|
|
const { entities: users } = stateEntityGetter.get('users');
|
|
|
|
|
const { entities: invites } = stateEntityGetter.get('invites');
|
2017-01-06 00:01:17 +00:00
|
|
|
const { errors: inviteErrors } = state.entities.invites;
|
|
|
|
|
const { errors: userErrors } = state.entities.users;
|
2016-10-03 17:54:22 +00:00
|
|
|
|
2017-01-06 00:01:17 +00:00
|
|
|
return { currentUser, inviteErrors, invites, userErrors, users };
|
2016-10-03 17:54:22 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default connect(mapStateToProps)(UserManagementPage);
|