PLT-6098 Added Manage Teams modal to System Console users list (#5914)

* Added Manage Teams modal to System Console users list

* Localized ManageTeamsModal

* Fixed borders between Manage Teams list items

* Updated appearance of ManageTeamsModal

* Fixed admin being redirected from system console when removing self from a team

* Sorted teams in ManageTeamsModal

* Updated Manage Teams styling
This commit is contained in:
Harrison Healey
2017-04-04 00:18:04 -04:00
committed by Corey Hulen
parent 4c9019b9eb
commit 348374fba5
10 changed files with 584 additions and 3 deletions

View File

@@ -0,0 +1,137 @@
// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React from 'react';
import {Dropdown, MenuItem} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
import {updateTeamMemberRoles, removeUserFromTeam} from 'actions/team_actions.jsx';
import * as Utils from 'utils/utils.jsx';
export default class ManageTeamsDropdown extends React.Component {
static propTypes = {
user: React.PropTypes.object.isRequired,
teamMember: React.PropTypes.object.isRequired,
onError: React.PropTypes.func.isRequired,
onMemberChange: React.PropTypes.func.isRequired,
onMemberRemove: React.PropTypes.func.isRequired
};
constructor(props) {
super(props);
this.toggleDropdown = this.toggleDropdown.bind(this);
this.makeTeamAdmin = this.makeTeamAdmin.bind(this);
this.makeMember = this.makeMember.bind(this);
this.removeFromTeam = this.removeFromTeam.bind(this);
this.handleMemberChange = this.handleMemberChange.bind(this);
this.handleMemberRemove = this.handleMemberRemove.bind(this);
this.state = {
show: false
};
}
toggleDropdown() {
this.setState({
show: !this.state.show
});
}
makeTeamAdmin() {
updateTeamMemberRoles(
this.props.teamMember.team_id,
this.props.user.id,
'team_user team_admin',
this.handleMemberChange,
this.props.onError
);
}
makeMember() {
updateTeamMemberRoles(
this.props.teamMember.team_id,
this.props.user.id,
'team_user',
this.handleMemberChange,
this.props.onError
);
}
removeFromTeam() {
removeUserFromTeam(
this.props.teamMember.team_id,
this.props.user.id,
this.handleMemberRemove,
this.props.onError
);
}
handleMemberChange() {
this.props.onMemberChange(this.props.teamMember.team_id);
}
handleMemberRemove() {
this.props.onMemberRemove(this.props.teamMember.team_id);
}
render() {
const isTeamAdmin = Utils.isAdmin(this.props.teamMember.roles);
let title;
if (isTeamAdmin) {
title = Utils.localizeMessage('admin.user_item.teamAdmin', 'Team Admin');
} else {
title = Utils.localizeMessage('admin.user_item.teamMember', 'Team Member');
}
let makeTeamAdmin = null;
if (!isTeamAdmin) {
makeTeamAdmin = (
<MenuItem onSelect={this.makeTeamAdmin}>
<FormattedMessage
id='admin.user_item.makeTeamAdmin'
defaultMessage='Make Team Admin'
/>
</MenuItem>
);
}
let makeMember = null;
if (isTeamAdmin) {
makeMember = (
<MenuItem onSelect={this.makeMember}>
<FormattedMessage
id='admin.user_item.makeMember'
defaultMessage='Make Member'
/>
</MenuItem>
);
}
return (
<Dropdown
id={`manage-teams-${this.props.user.id}-${this.props.teamMember.team_id}`}
open={this.state.show}
onToggle={this.toggleDropdown}
>
<Dropdown.Toggle useAnchor={true}>
{title}
</Dropdown.Toggle>
<Dropdown.Menu>
{makeTeamAdmin}
{makeMember}
<MenuItem onSelect={this.removeFromTeam}>
<FormattedMessage
id='team_members_dropdown.leave_team'
defaultMessage='Remove from Team'
/>
</MenuItem>
</Dropdown.Menu>
</Dropdown>
);
}
}

View File

@@ -0,0 +1,225 @@
// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React from 'react';
import {Modal} from 'react-bootstrap';
import {FormattedMessage} from 'react-intl';
import * as TeamActions from 'actions/team_actions.jsx';
import Client from 'client/web_client.jsx';
import LoadingScreen from 'components/loading_screen.jsx';
import {sortTeamsByDisplayName} from 'utils/team_utils.jsx';
import * as Utils from 'utils/utils.jsx';
import ManageTeamsDropdown from './manage_teams_dropdown.jsx';
import RemoveFromTeamButton from './remove_from_team_button.jsx';
export default class ManageTeamsModal extends React.Component {
static propTypes = {
onModalDismissed: React.PropTypes.func.isRequired,
show: React.PropTypes.bool.isRequired,
user: React.PropTypes.object
};
constructor(props) {
super(props);
this.loadTeamsAndTeamMembers = this.loadTeamsAndTeamMembers.bind(this);
this.handleError = this.handleError.bind(this);
this.handleMemberChange = this.handleMemberChange.bind(this);
this.handleMemberRemove = this.handleMemberRemove.bind(this);
this.renderContents = this.renderContents.bind(this);
this.state = {
error: null,
teams: null,
teamMembers: null
};
}
componentDidMount() {
if (this.props.user) {
this.loadTeamsAndTeamMembers();
}
}
componentWillReceiveProps(nextProps) {
const userId = this.props.user ? this.props.user.id : '';
const nextUserId = nextProps.user ? nextProps.user.id : '';
if (userId !== nextUserId) {
this.setState({
teams: null,
teamMembers: null
});
if (nextProps.user) {
this.loadTeamsAndTeamMembers(nextProps.user);
}
}
}
loadTeamsAndTeamMembers(user = this.props.user) {
TeamActions.getTeamsForUser(user.id, (teams) => {
this.setState({
teams: teams.sort(sortTeamsByDisplayName)
});
});
TeamActions.getTeamMembersForUser(user.id, (teamMembers) => {
this.setState({
teamMembers
});
});
}
handleError(error) {
this.setState({
error
});
}
handleMemberChange() {
TeamActions.getTeamMembersForUser(this.props.user.id, (teamMembers) => {
this.setState({
teamMembers
});
});
}
handleMemberRemove(teamId) {
this.setState({
teams: this.state.teams.filter((team) => team.id !== teamId),
teamMembers: this.state.teamMembers.filter((teamMember) => teamMember.team_id !== teamId)
});
}
renderContents() {
const {user} = this.props;
const {teams, teamMembers} = this.state;
if (!user) {
return <LoadingScreen/>;
}
const isSystemAdmin = Utils.isAdmin(user.roles);
let name = Utils.getFullName(user);
if (name) {
name += ` (@${user.username})`;
} else {
name = `@${user.username}`;
}
let teamList;
if (teams && teamMembers) {
teamList = teams.map((team) => {
const teamMember = teamMembers.find((member) => member.team_id === team.id);
if (!teamMember) {
return null;
}
let action;
if (isSystemAdmin) {
action = (
<RemoveFromTeamButton
user={user}
team={team}
onError={this.handleError}
onMemberRemove={this.handleMemberRemove}
/>
);
} else {
action = (
<ManageTeamsDropdown
user={user}
team={team}
teamMember={teamMember}
onError={this.handleError}
onMemberChange={this.handleMemberChange}
onMemberRemove={this.handleMemberRemove}
/>
);
}
return (
<div
key={team.id}
className='manage-teams__team'
>
<div className='manage-teams__team-name'>
{team.display_name}
</div>
<div className='manage-teams__team-actions'>
{action}
</div>
</div>
);
});
} else {
teamList = <LoadingScreen/>;
}
let systemAdminIndicator = null;
if (isSystemAdmin) {
systemAdminIndicator = (
<div className='manage-teams__system-admin'>
<FormattedMessage
id='admin.user_item.sysAdmin'
defaultMessage='System Admin'
/>
</div>
);
}
return (
<div>
<div className='manage-teams__user'>
<img
className='manage-teams__profile-picture'
src={Client.getProfilePictureUrl(user.id, user.last_picture_update)}
/>
<div className='manage-teams__info'>
<div className='manage-teams__name'>
{name}
</div>
<div className='manage-teams__email'>
{user.email}
</div>
</div>
{systemAdminIndicator}
</div>
<div className='manage-teams__teams'>
{teamList}
</div>
</div>
);
}
render() {
return (
<Modal
show={this.props.show}
onHide={this.props.onModalDismissed}
dialogClassName='manage-teams'
>
<Modal.Header closeButton={true}>
<Modal.Title>
<FormattedMessage
id='admin.user_item.manageTeams'
defaultMessage='Manage Teams'
/>
</Modal.Title>
</Modal.Header>
<Modal.Body>
{this.renderContents()}
</Modal.Body>
</Modal>
);
}
}

View File

@@ -0,0 +1,52 @@
// Copyright (c) 2017 Mattermost, Inc. All Rights Reserved.
// See License.txt for license information.
import React from 'react';
import {FormattedMessage} from 'react-intl';
import {removeUserFromTeam} from 'actions/team_actions.jsx';
export default class RemoveFromTeamButton extends React.PureComponent {
static propTypes = {
onError: React.PropTypes.func.isRequired,
onMemberRemove: React.PropTypes.func.isRequired,
team: React.PropTypes.object.isRequired,
user: React.PropTypes.object.isRequired
};
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
this.handleMemberRemove = this.handleMemberRemove.bind(this);
}
handleClick(e) {
e.preventDefault();
removeUserFromTeam(
this.props.team.id,
this.props.user.id,
this.handleMemberRemove,
this.props.onError
);
}
handleMemberRemove() {
this.props.onMemberRemove(this.props.team.id);
}
render() {
return (
<button
className='btn btn-default'
onClick={this.handleClick}
>
<FormattedMessage
id='team_members_dropdown.leave_team'
defaultMessage='Remove from Team'
/>
</button>
);
}
}

View File

@@ -18,7 +18,8 @@ import React from 'react';
export default class SystemUsersDropdown extends React.Component {
static propTypes = {
user: React.PropTypes.object.isRequired,
doPasswordReset: React.PropTypes.func.isRequired
doPasswordReset: React.PropTypes.func.isRequired,
doManageTeams: React.PropTypes.func.isRequired
};
constructor(props) {
@@ -28,6 +29,7 @@ export default class SystemUsersDropdown extends React.Component {
this.handleMakeActive = this.handleMakeActive.bind(this);
this.handleMakeNotActive = this.handleMakeNotActive.bind(this);
this.handleMakeSystemAdmin = this.handleMakeSystemAdmin.bind(this);
this.handleManageTeams = this.handleManageTeams.bind(this);
this.handleResetPassword = this.handleResetPassword.bind(this);
this.handleResetMfa = this.handleResetMfa.bind(this);
this.handleDemoteSystemAdmin = this.handleDemoteSystemAdmin.bind(this);
@@ -94,6 +96,12 @@ export default class SystemUsersDropdown extends React.Component {
);
}
handleManageTeams(e) {
e.preventDefault();
this.props.doManageTeams(this.props.user);
}
handleResetPassword(e) {
e.preventDefault();
this.props.doPasswordReset(this.props.user);
@@ -177,6 +185,7 @@ export default class SystemUsersDropdown extends React.Component {
let showMakeSystemAdmin = !Utils.isSystemAdmin(user.roles);
let showMakeActive = false;
let showMakeNotActive = !Utils.isSystemAdmin(user.roles);
let showManageTeams = true;
const mfaEnabled = global.window.mm_license.IsLicensed === 'true' && global.window.mm_license.MFA === 'true' && global.window.mm_config.EnableMultifactorAuthentication === 'true';
const showMfaReset = mfaEnabled && user.mfa_active;
@@ -191,6 +200,7 @@ export default class SystemUsersDropdown extends React.Component {
showMakeSystemAdmin = false;
showMakeActive = true;
showMakeNotActive = false;
showManageTeams = false;
}
let disableActivationToggle = false;
@@ -281,6 +291,24 @@ export default class SystemUsersDropdown extends React.Component {
);
}
let manageTeams = null;
if (showManageTeams) {
manageTeams = (
<li role='presentation'>
<a
role='menuitem'
href='#'
onClick={this.handleManageTeams}
>
<FormattedMessage
id='admin.user_item.manageTeams'
defaultMessage='Manage Teams'
/>
</a>
</li>
);
}
let mfaReset = null;
if (showMfaReset) {
mfaReset = (
@@ -404,6 +432,7 @@ export default class SystemUsersDropdown extends React.Component {
{makeActive}
{makeNotActive}
{makeSystemAdmin}
{manageTeams}
{mfaReset}
{passwordReset}
</ul>

View File

@@ -4,6 +4,7 @@
import React from 'react';
import {FormattedMessage, FormattedHTMLMessage} from 'react-intl';
import ManageTeamsModal from 'components/admin_console/manage_teams_modal/manage_teams_modal.jsx';
import ResetPasswordModal from 'components/admin_console/reset_password_modal.jsx';
import SearchableUserList from 'components/searchable_user_list/searchable_user_list.jsx';
@@ -35,6 +36,9 @@ export default class SystemUsersList extends React.Component {
this.previousPage = this.previousPage.bind(this);
this.search = this.search.bind(this);
this.doManageTeams = this.doManageTeams.bind(this);
this.doManageTeamsDismiss = this.doManageTeamsDismiss.bind(this);
this.doPasswordReset = this.doPasswordReset.bind(this);
this.doPasswordResetDismiss = this.doPasswordResetDismiss.bind(this);
this.doPasswordResetSubmit = this.doPasswordResetSubmit.bind(this);
@@ -42,6 +46,7 @@ export default class SystemUsersList extends React.Component {
this.state = {
page: 0,
showManageTeamsModal: false,
showPasswordModal: false,
user: null
};
@@ -71,6 +76,20 @@ export default class SystemUsersList extends React.Component {
}
}
doManageTeams(user) {
this.setState({
showManageTeamsModal: true,
user
});
}
doManageTeamsDismiss() {
this.setState({
showManageTeamsModal: false,
user: null
});
}
doPasswordReset(user) {
this.setState({
showPasswordModal: true,
@@ -211,7 +230,8 @@ export default class SystemUsersList extends React.Component {
extraInfo={extraInfo}
actions={[SystemUsersDropdown]}
actionProps={{
doPasswordReset: this.doPasswordReset
doPasswordReset: this.doPasswordReset,
doManageTeams: this.doManageTeams
}}
nextPage={this.nextPage}
previousPage={this.previousPage}
@@ -220,6 +240,11 @@ export default class SystemUsersList extends React.Component {
term={this.props.term}
onTermChange={this.props.onTermChange}
/>
<ManageTeamsModal
user={this.state.user}
show={this.state.showManageTeamsModal}
onModalDismissed={this.doManageTeamsDismiss}
/>
<ResetPasswordModal
user={this.state.user}
show={this.state.showPasswordModal}