summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHarrison Healey <harrisonmhealey@gmail.com>2017-04-04 00:18:04 -0400
committerCorey Hulen <corey@hulen.com>2017-04-03 21:18:04 -0700
commit348374fba5db8415d37d5cd8b897048b1300f415 (patch)
tree8277c9be4b8032b488e2fe87ceee58a283569a3e
parent4c9019b9eb789152439ad6a56e93c7f0fb7832c5 (diff)
downloadchat-348374fba5db8415d37d5cd8b897048b1300f415.tar.gz
chat-348374fba5db8415d37d5cd8b897048b1300f415.tar.bz2
chat-348374fba5db8415d37d5cd8b897048b1300f415.zip
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
-rw-r--r--webapp/actions/team_actions.jsx8
-rw-r--r--webapp/actions/websocket_actions.jsx5
-rw-r--r--webapp/client/client.jsx30
-rw-r--r--webapp/components/admin_console/manage_teams_modal/manage_teams_dropdown.jsx137
-rw-r--r--webapp/components/admin_console/manage_teams_modal/manage_teams_modal.jsx225
-rw-r--r--webapp/components/admin_console/manage_teams_modal/remove_from_team_button.jsx52
-rw-r--r--webapp/components/admin_console/system_users/system_users_dropdown.jsx31
-rw-r--r--webapp/components/admin_console/system_users/system_users_list.jsx27
-rwxr-xr-xwebapp/i18n/en.json2
-rw-r--r--webapp/sass/routes/_admin-console.scss70
10 files changed, 584 insertions, 3 deletions
diff --git a/webapp/actions/team_actions.jsx b/webapp/actions/team_actions.jsx
index 478d3dffc..4cb57961b 100644
--- a/webapp/actions/team_actions.jsx
+++ b/webapp/actions/team_actions.jsx
@@ -150,3 +150,11 @@ export function switchTeams(url) {
AsyncClient.viewChannel();
browserHistory.push(url);
}
+
+export function getTeamsForUser(userId, success, error) {
+ Client.getTeamsForUser(userId, success, error);
+}
+
+export function getTeamMembersForUser(userId, success, error) {
+ Client.getTeamMembersForUser(userId, success, error);
+}
diff --git a/webapp/actions/websocket_actions.jsx b/webapp/actions/websocket_actions.jsx
index e07e3e217..ab798df28 100644
--- a/webapp/actions/websocket_actions.jsx
+++ b/webapp/actions/websocket_actions.jsx
@@ -251,7 +251,10 @@ function handleLeaveTeamEvent(msg) {
Client.setTeamId('');
BrowserStore.removeGlobalItem('team');
BrowserStore.removeGlobalItem(msg.data.team_id);
- GlobalActions.redirectUserToDefaultTeam();
+
+ if (!global.location.pathname.startsWith('/admin_console')) {
+ GlobalActions.redirectUserToDefaultTeam();
+ }
}
} else {
UserStore.removeProfileFromTeam(msg.data.team_id, msg.data.user_id);
diff --git a/webapp/client/client.jsx b/webapp/client/client.jsx
index 1f2e5517f..1f70300e8 100644
--- a/webapp/client/client.jsx
+++ b/webapp/client/client.jsx
@@ -550,6 +550,16 @@ export default class Client {
end(this.handleResponse.bind(this, 'getAllTeamListings', success, error));
}
+ getTeamsForUser(userId, success, error) {
+ // Call out to API v4 since this call doesn't exist in v3
+ request.
+ get(`${this.url}/api/v4/users/${userId}/teams`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getTeamsForUser', success, error));
+ }
+
getMyTeam(success, error) {
request.
get(`${this.getTeamNeededRoute()}/me`).
@@ -586,6 +596,16 @@ export default class Client {
end(this.handleResponse.bind(this, 'getMyTeamMembers', success, error));
}
+ getTeamMembersForUser(userId, success, error) {
+ // Call out to API v4 since this call doesn't exist in v3
+ request.
+ get(`${this.url}/api/v4/users/${userId}/teams/members`).
+ set(this.defaultHeaders).
+ type('application/json').
+ accept('application/json').
+ end(this.handleResponse.bind(this, 'getTeamsForUser', success, error));
+ }
+
getMyTeamsUnread(teamId, success, error) {
let url = `${this.getTeamsRoute()}/unread`;
@@ -1275,6 +1295,16 @@ export default class Client {
this.trackEvent('api', 'api_users_update_profile_picture');
}
+ getProfilePictureUrl(id, lastPictureUpdate) {
+ let url = `${this.getUsersRoute()}/${id}/image`;
+
+ if (lastPictureUpdate) {
+ url += `?time=${lastPictureUpdate}`;
+ }
+
+ return url;
+ }
+
// Channel Routes Section
createChannel(channel, success, error) {
diff --git a/webapp/components/admin_console/manage_teams_modal/manage_teams_dropdown.jsx b/webapp/components/admin_console/manage_teams_modal/manage_teams_dropdown.jsx
new file mode 100644
index 000000000..81e6460af
--- /dev/null
+++ b/webapp/components/admin_console/manage_teams_modal/manage_teams_dropdown.jsx
@@ -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>
+ );
+ }
+}
diff --git a/webapp/components/admin_console/manage_teams_modal/manage_teams_modal.jsx b/webapp/components/admin_console/manage_teams_modal/manage_teams_modal.jsx
new file mode 100644
index 000000000..e3eae6310
--- /dev/null
+++ b/webapp/components/admin_console/manage_teams_modal/manage_teams_modal.jsx
@@ -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>
+ );
+ }
+}
diff --git a/webapp/components/admin_console/manage_teams_modal/remove_from_team_button.jsx b/webapp/components/admin_console/manage_teams_modal/remove_from_team_button.jsx
new file mode 100644
index 000000000..d733135f4
--- /dev/null
+++ b/webapp/components/admin_console/manage_teams_modal/remove_from_team_button.jsx
@@ -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>
+ );
+ }
+}
diff --git a/webapp/components/admin_console/system_users/system_users_dropdown.jsx b/webapp/components/admin_console/system_users/system_users_dropdown.jsx
index 6f18754a1..534017cf8 100644
--- a/webapp/components/admin_console/system_users/system_users_dropdown.jsx
+++ b/webapp/components/admin_console/system_users/system_users_dropdown.jsx
@@ -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>
diff --git a/webapp/components/admin_console/system_users/system_users_list.jsx b/webapp/components/admin_console/system_users/system_users_list.jsx
index 5d8837164..ccb1a39d4 100644
--- a/webapp/components/admin_console/system_users/system_users_list.jsx
+++ b/webapp/components/admin_console/system_users/system_users_list.jsx
@@ -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}
diff --git a/webapp/i18n/en.json b/webapp/i18n/en.json
index 13660e12a..4c28126e5 100755
--- a/webapp/i18n/en.json
+++ b/webapp/i18n/en.json
@@ -896,6 +896,7 @@
"admin.user_item.makeMember": "Make Member",
"admin.user_item.makeSysAdmin": "Make System Admin",
"admin.user_item.makeTeamAdmin": "Make Team Admin",
+ "admin.user_item.manageTeams": "Manage Teams",
"admin.user_item.member": "Member",
"admin.user_item.mfaNo": "<strong>MFA</strong>: No",
"admin.user_item.mfaYes": "<strong>MFA</strong>: Yes",
@@ -903,6 +904,7 @@
"admin.user_item.resetPwd": "Reset Password",
"admin.user_item.switchToEmail": "Switch to Email/Password",
"admin.user_item.teamAdmin": "Team Admin",
+ "admin.user_item.sysAdmin": "System Admin",
"admin.webrtc.enableDescription": "When true, Mattermost allows making <strong>one-on-one</strong> video calls. WebRTC calls are available on Chrome, Firefox and Mattermost Desktop Apps.",
"admin.webrtc.enableTitle": "Enable Mattermost WebRTC: ",
"admin.webrtc.gatewayAdminSecretDescription": "Enter your admin secret password to access the Gateway Admin URL.",
diff --git a/webapp/sass/routes/_admin-console.scss b/webapp/sass/routes/_admin-console.scss
index bf8aba6cc..a39e1c20a 100644
--- a/webapp/sass/routes/_admin-console.scss
+++ b/webapp/sass/routes/_admin-console.scss
@@ -501,3 +501,73 @@
width: 200px
}
}
+
+.manage-teams {
+ .manage-teams__user {
+ align-items: center;
+ border-bottom-color: lightgray;
+ border-bottom-style: solid;
+ border-bottom-width: 1px;
+ display: flex;
+ padding-bottom: 15px;
+ }
+
+ .manage-teams__profile-picture {
+ border-radius: 20px;
+ height: 40px;
+ width: 40px;
+ }
+
+ .manage-teams__info {
+ flex: 1;
+ margin-left: 10px;
+ overflow: hidden;
+ white-space: nowrap;
+
+ .manage-teams__name {
+ font-weight: bold;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .manage-teams__email {
+ opacity: 0.5;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+ }
+
+ .manage-teams__system-admin {
+ margin-left: 10px;
+ opacity: 0.5;
+ padding-right: 10px;
+ }
+
+ .manage-teams__team {
+ align-items: center;
+ display: flex;
+ padding: 10px;
+ }
+
+ .manage-teams__team + .manage-teams__team {
+ border-top-color: lightgray;
+ border-top-style: solid;
+ border-top-width: 1px;
+ }
+
+ .manage-teams__team-name {
+ flex: 1;
+ font-weight: bold;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .manage-teams__team-actions {
+ margin-left: 10px;
+
+ // Override default react-bootstrap style
+ .dropdown-toggle {
+ @include box-shadow(none);
+ }
+ }
+}