Remove batch actions, user add/remove to/from org, & user selectability from Chronograf Admin Table

pull/10616/head
Jared Scheib 2017-11-09 23:07:56 -08:00
parent cdbec69280
commit 676c079d62
6 changed files with 19 additions and 322 deletions

View File

@ -1,59 +0,0 @@
import React, {PropTypes} from 'react'
import Dropdown from 'shared/components/Dropdown'
import {USER_ROLES} from 'src/admin/constants/dummyUsers'
const BatchActionsBar = ({
organizations,
onDeleteUsers,
onChangeRoles,
numUsersSelected,
onAddUserToOrg,
}) => {
const rolesDropdownItems = USER_ROLES.map(role => ({
...role,
text: role.name,
}))
return (
<div className="chronograf-admin-table--batch">
<p className="chronograf-admin-table--num-selected">
{numUsersSelected} User{numUsersSelected === 1 ? ' ' : 's '}Selected
</p>
{numUsersSelected > 0
? <div className="chronograf-admin-table--batch-actions">
<div className="btn btn-sm btn-danger" onClick={onDeleteUsers}>
Delete
</div>
<Dropdown
items={rolesDropdownItems}
selected={'Set New Role'}
onChoose={onChangeRoles}
buttonColor="btn-primary"
className="dropdown-140"
/>
<Dropdown
items={organizations.map(org => ({...org, text: org.name}))}
selected={'Add to Org'}
onChoose={onAddUserToOrg}
buttonColor="btn-primary"
className="dropdown-240"
/>
</div>
: null}
</div>
)
}
const {arrayOf, func, number, shape} = PropTypes
BatchActionsBar.propTypes = {
organizations: arrayOf(shape()),
onDeleteUsers: func.isRequired,
onChangeRoles: func.isRequired,
numUsersSelected: number.isRequired,
onAddUserToOrg: func.isRequired,
}
export default BatchActionsBar

View File

@ -74,7 +74,6 @@ class NewUserTableRow extends Component {
return (
<tr className="chronograf-admin-table--new-user">
<td className="chronograf-admin-table--check-col" />
<td>
<input
className="form-control input-xs"

View File

@ -11,9 +11,6 @@ import {USERS_TABLE} from 'src/admin/constants/chronografTableSizing'
const OrgTableRow = ({
user,
organization,
onToggleUserSelected,
selectedUsers,
isSameUser,
onChangeUserRole,
onChangeSuperAdmin,
}) => {
@ -25,30 +22,13 @@ const OrgTableRow = ({
colActions,
} = USERS_TABLE
const isSelected = selectedUsers.find(u => isSameUser(user, u))
const currentRole = user.roles.find(
role => role.organization === organization.id
)
return (
<tr
className={
isSelected
? 'chronograf-admin-table--user selected'
: 'chronograf-admin-table--user'
}
>
<td
onClick={onToggleUserSelected(user)}
className="chronograf-admin-table--check-col chronograf-admin-table--selectable"
>
<div className="user-checkbox" />
</td>
<td
onClick={onToggleUserSelected(user)}
className="chronograf-admin-table--selectable"
>
<tr className={'chronograf-admin-table--user'}>
<td>
<strong>
{user.name}
</strong>
@ -88,7 +68,7 @@ const OrgTableRow = ({
)
}
const {arrayOf, func, shape, string} = PropTypes
const {func, shape, string} = PropTypes
OrgTableRow.propTypes = {
user: shape(),
@ -96,9 +76,6 @@ OrgTableRow.propTypes = {
name: string.isRequired,
id: string.isRequired,
}),
onToggleUserSelected: func.isRequired,
selectedUsers: arrayOf(shape()),
isSameUser: func.isRequired,
onChangeUserRole: func.isRequired,
onChangeSuperAdmin: func.isRequired,
}

View File

@ -1,13 +1,10 @@
import React, {Component, PropTypes} from 'react'
import _ from 'lodash'
import Authorized, {SUPERADMIN_ROLE} from 'src/auth/Authorized'
import UsersTableHeader from 'src/admin/components/chronograf/UsersTableHeader'
import OrgTableRow from 'src/admin/components/chronograf/OrgTableRow'
import NewUserTableRow from 'src/admin/components/chronograf/NewUserTableRow'
import BatchActionsBar from 'src/admin/components/chronograf/BatchActionsBar'
import {USERS_TABLE} from 'src/admin/constants/chronografTableSizing'
@ -36,28 +33,8 @@ class UsersTable extends Component {
this.setState({isCreatingUser: false})
}
areSameUsers = (usersA, usersB) => {
if (usersA.length === 0 && usersB.length === 0) {
return false
}
const {isSameUser} = this.props
return !_.differenceWith(usersA, usersB, isSameUser).length
}
render() {
const {
organization,
users,
organizations,
onToggleAllUsersSelected,
onToggleUserSelected,
selectedUsers,
isSameUser,
onCreateUser,
onDeleteUsers,
onChangeRoles,
onAddUserToOrg,
} = this.props
const {organization, users, onCreateUser} = this.props
const {isCreatingUser} = this.state
const {
@ -68,35 +45,16 @@ class UsersTable extends Component {
colActions,
} = USERS_TABLE
const areAllSelected = this.areSameUsers(users, selectedUsers)
return (
<div className="panel panel-minimal">
<UsersTableHeader
numUsers={users.length}
onCreateUserRow={this.handleClickCreateUserRow}
/>
<BatchActionsBar
organizations={organizations}
numUsersSelected={selectedUsers.length}
onDeleteUsers={onDeleteUsers}
onChangeRoles={onChangeRoles}
onAddUserToOrg={onAddUserToOrg}
/>
<div className="panel-body">
<table className="table table-highlight v-center chronograf-admin-table">
<thead>
<tr>
<th className="chronograf-admin-table--check-col">
<div
className={
areAllSelected
? 'user-checkbox selected'
: 'user-checkbox'
}
onClick={onToggleAllUsersSelected(areAllSelected)}
/>
</th>
<th>Username</th>
<th style={{width: colRole}} className="align-with-col-text">
Role
@ -124,9 +82,6 @@ class UsersTable extends Component {
<OrgTableRow
user={user}
key={i}
onToggleUserSelected={onToggleUserSelected}
selectedUsers={selectedUsers}
isSameUser={isSameUser}
organization={organization}
onChangeUserRole={this.handleChangeUserRole}
onChangeSuperAdmin={this.handleChangeSuperAdmin}
@ -158,20 +113,12 @@ const {arrayOf, func, shape, string} = PropTypes
UsersTable.propTypes = {
users: arrayOf(shape()),
organizations: arrayOf(shape()),
selectedUsers: arrayOf(shape()),
onToggleUserSelected: func.isRequired,
onToggleAllUsersSelected: func.isRequired,
isSameUser: func.isRequired,
organization: shape({
name: string.isRequired,
id: string.isRequired,
}),
onUpdateUserRole: func.isRequired,
onCreateUser: func.isRequired,
onUpdateUserRole: func.isRequired,
onUpdateUserSuperAdmin: func.isRequired,
onDeleteUsers: func.isRequired,
onChangeRoles: func.isRequired,
onAddUserToOrg: func.isRequired,
}
export default UsersTable

View File

@ -12,19 +12,9 @@ import UsersTable from 'src/admin/components/chronograf/UsersTable'
import FancyScrollbar from 'shared/components/FancyScrollbar'
import {isSameUser} from 'shared/reducers/helpers/auth'
import {DEFAULT_ORG_ID} from 'src/admin/constants/dummyUsers'
class AdminChronografPage extends Component {
constructor(props) {
super(props)
this.state = {
selectedUsers: [],
}
}
// TODO: revisit this, possibly don't call setState if both are deep equal
componentWillReceiveProps(nextProps) {
const {currentOrganization} = nextProps
@ -42,36 +32,9 @@ class AdminChronografPage extends Component {
}
loadUsers = () => {
const {
links,
actions: {loadUsersAsync, loadOrganizationsAsync},
} = this.props
const {links, actions: {loadUsersAsync}} = this.props
loadUsersAsync(links.users)
loadOrganizationsAsync(links.organizations)
}
handleToggleUserSelected = user => e => {
e.preventDefault()
const {selectedUsers} = this.state
const isUserSelected = selectedUsers.find(u => isSameUser(user, u))
const newSelectedUsers = isUserSelected
? selectedUsers.filter(u => !isSameUser(user, u))
: [...selectedUsers, user]
this.setState({selectedUsers: newSelectedUsers})
}
handleToggleAllUsersSelected = areAllSelected => () => {
const {users} = this.props
if (areAllSelected) {
this.setState({selectedUsers: []})
} else {
this.setState({selectedUsers: users})
}
}
// SINGLE USER ACTIONS
@ -93,33 +56,7 @@ class AdminChronografPage extends Component {
}
createUserAsync(links.users, newUser)
}
// handleAddUserToOrg will add a user to an organization as a 'member'. if
// the user already has a role in that organization, it will do nothing.
handleAddUserToOrg = (user, organization) => {
const {actions: {updateUserAsync}} = this.props
updateUserAsync(user, {
...user,
roles: [
...user.roles,
{
name: MEMBER_ROLE, // TODO: remove this to let server decide when default org role is implemented
organization: organization.id,
},
],
})
}
handleRemoveUserFromOrg = (user, organization) => {
const {actions: {updateUserAsync}} = this.props
let newRoles = user.roles.filter(r => r.organization !== organization.id)
if (newRoles.length === 0) {
newRoles = [{organization: DEFAULT_ORG_ID, name: MEMBER_ROLE}]
}
updateUserAsync(user, {...user, roles: newRoles})
}
handleUpdateUserRole = () => (user, currentRole, {name}) => {
const {actions: {updateUserAsync}} = this.props
@ -142,54 +79,8 @@ class AdminChronografPage extends Component {
deleteUserAsync(user)
}
// BATCH USER ACTIONS
// TODO: make batch actions work for batch. currently only work for one user
// since batch actions have not been implemented in the API.
handleBatchChangeUsersRole = () => {}
handleBatchAddUsersToOrg = organization => {
const {notify} = this.props
const {selectedUsers} = this.state
if (selectedUsers.length > 1) {
notify(
'error',
'Batch actions for more than 1 user not currently supported'
)
} else {
this.handleAddUserToOrg(selectedUsers[0], organization)
}
}
handleBatchRemoveUsersFromOrg = organization => {
const {notify} = this.props
const {selectedUsers} = this.state
if (selectedUsers.length > 1) {
notify(
'error',
'Batch actions for more than 1 user not currently supported'
)
} else {
this.handleRemoveUserFromOrg(selectedUsers[0], organization)
}
}
handleBatchDeleteUsers = () => {
const {notify} = this.props
// const {selectedUsers} = this.state
if (this.state.selectedUsers.length > 1) {
notify(
'error',
'Batch actions for more than 1 user not currently supported'
)
} else {
this.handleDeleteUser(this.state.selectedUsers[0])
this.setState({selectedUsers: []})
}
}
render() {
const {users, organizations, currentOrganization} = this.props
const {selectedUsers} = this.state
const {users, currentOrganization} = this.props
return (
<div className="page">
@ -200,21 +91,12 @@ class AdminChronografPage extends Component {
<div className="row">
<div className="col-xs-12">
<UsersTable
onAddUserToOrg={this.handleBatchAddUsersToOrg}
users={users}
organizations={organizations}
selectedUsers={selectedUsers}
onToggleUserSelected={this.handleToggleUserSelected}
onToggleAllUsersSelected={
this.handleToggleAllUsersSelected
}
isSameUser={isSameUser}
organization={currentOrganization}
onUpdateUserRole={this.handleUpdateUserRole()}
onCreateUser={this.handleCreateUser}
onUpdateUserRole={this.handleUpdateUserRole()}
onUpdateUserSuperAdmin={this.handleUpdateUserSuperAdmin()}
onDeleteUsers={this.handleBatchDeleteUsers}
onChangeRoles={this.handleBatchChangeUsersRole}
onDeleteUser={this.handleDeleteUser}
/>
</div>
</div>
@ -231,17 +113,14 @@ const {arrayOf, func, shape, string} = PropTypes
AdminChronografPage.propTypes = {
links: shape({
users: string.isRequired,
organizations: string.isRequired,
}),
users: arrayOf(shape),
organizations: arrayOf(shape),
currentOrganization: shape({
id: string.isRequired,
name: string.isRequired,
}).isRequired,
actions: shape({
loadUsersAsync: func.isRequired,
loadOrganizationsAsync: func.isRequired,
createUserAsync: func.isRequired,
updateUserAsync: func.isRequired,
deleteUserAsync: func.isRequired,
@ -251,12 +130,11 @@ AdminChronografPage.propTypes = {
const mapStateToProps = ({
links,
adminChronograf: {users, organizations},
adminChronograf: {users},
auth: {me: {currentOrganization}},
}) => ({
links,
users,
organizations,
currentOrganization,
})

View File

@ -25,57 +25,6 @@ table.table.chronograf-admin-table .dropdown {
table.table.chronograf-admin-table thead tr th.align-with-col-text {
padding-left: 15px;
}
.user-checkbox {
display: inline-block;
vertical-align: middle;
width: 16px;
height: 16px;
border-radius: 3px;
background-color: $g2-kevlar;
position: relative;
&:after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%) scale(1, 1);
width: 20px;
height: 20px;
opacity: 0;
background-color: $c-pool;
border-radius: 50%;
transition: transform 0.25s ease, opacity 0.25s ease;
}
&:hover {
cursor: pointer;
}
}
tr.selected .user-checkbox:after,
.user-checkbox.selected:after {
opacity: 1;
transform: translate(-50%, -50%) scale(0.4, 0.4);
}
table.table.chronograf-admin-table tbody tr.selected {
background-color: $g5-pepper;
}
table.table.chronograf-admin-table tbody tr.selected td {
color: $g18-cloud;
font-weight: 700;
}
table.table.chronograf-admin-table thead tr th.chronograf-admin-table--check-col,
table.table.chronograf-admin-table tbody tr td.chronograf-admin-table--check-col {
width: 28px;
padding-right: 0;
}
table.table.chronograf-admin-table thead tr th.chronograf-admin-table--selectable,
table.table.chronograf-admin-table tbody tr td.chronograf-admin-table--selectable {
&:hover {cursor: pointer;}
}
.dropdown-label {
margin: 0 8px 0 0;
font-weight: 700;
@ -123,7 +72,9 @@ table.table.chronograf-admin-table tbody tr.chronograf-admin-table--user td div.
background-color: $g3-castle;
color: $g13-mist;
> .caret {opacity: 0;}
> .caret {
opacity: 0;
}
}
table.table.chronograf-admin-table tbody tr.chronograf-admin-table--user.selected td div.dropdown div.btn.dropdown-toggle {
background-color: $g5-pepper;
@ -132,7 +83,9 @@ table.table.chronograf-admin-table tbody tr.chronograf-admin-table--user:hover t
background-color: $c-pool;
color: $g20-white;
> .caret {opacity: 1;}
> .caret {
opacity: 1;
}
&:hover {
background-color: $c-laser;
@ -143,7 +96,9 @@ table.table.chronograf-admin-table tbody tr.chronograf-admin-table--user td div.
background-color: $c-hydrogen;
color: $g20-white;
> .caret {opacity: 1;}
> .caret {
opacity: 1;
}
}
/* Styles for new user row */