Reorient AllUsersTable around organizations, not roles

Use new links.rawUsers route to get users for AllUsersPage.
Introduce '*' role name to tell server to assign org defaultRole.
Update table header to display number of users & num orgs.
Remove concept of roles from AllUsers UI.

Note: The update & create AJAX are both broken or disabled until
the server is modified to accept '*' for role.
pull/10616/head
Jared Scheib 2018-01-10 15:46:34 -08:00
parent 4c33cf7846
commit 6728ec5d82
8 changed files with 116 additions and 110 deletions

View File

@ -36,12 +36,7 @@ const AdminTabs = ({
{
requiredRole: SUPERADMIN_ROLE,
type: ALL_USERS_TAB_NAME,
component: (
<AllUsersPage
meID={meID}
meCurrentOrganization={meCurrentOrganization}
/>
),
component: <AllUsersPage meID={meID} />,
},
].filter(t => isUserAuthorized(meRole, t.requiredRole))

View File

@ -17,8 +17,10 @@ class AllUsersTable extends Component {
}
}
handleChangeUserRole = (user, currentRole) => newRole => {
this.props.onUpdateUserRole(user, currentRole, newRole)
handleAddUserToOrganization = user => newOrganization => {
console.log('handleAddUserToOrganization', user.name, newOrganization.id)
// const newOrganizationRole = newOrganization + newOrganizationDefaultRole -- need to get this fresh from server or have server determine it, which requires a change to ValidUpdate
// this.props.onUpdateUserRole(user, newOrganizationRole)
}
handleChangeSuperAdmin = user => newStatus => {
@ -38,7 +40,7 @@ class AllUsersTable extends Component {
}
render() {
const {organization, users, onCreateUser, meID, notify} = this.props
const {users, organizations, onCreateUser, meID, notify} = this.props
const {isCreatingUser} = this.state
const {
@ -53,9 +55,9 @@ class AllUsersTable extends Component {
<div className="panel panel-default">
<AllUsersTableHeader
numUsers={users.length}
numOrganizations={organizations.length}
onClickCreateUser={this.handleClickCreateUser}
isCreatingUser={isCreatingUser}
organization={organization}
/>
<div className="panel-body">
<table className="table table-highlight v-center chronograf-admin-table">
@ -76,7 +78,7 @@ class AllUsersTable extends Component {
<tbody>
{isCreatingUser
? <AllUsersTableRowNew
organization={organization}
organizations={organizations}
onBlur={this.handleBlurCreateUserRow}
onCreateUser={onCreateUser}
notify={notify}
@ -87,8 +89,8 @@ class AllUsersTable extends Component {
<AllUsersTableRow
user={user}
key={uuid.v4()}
organization={organization}
onChangeUserRole={this.handleChangeUserRole}
organizations={organizations}
onAddUserToOrganization={this.handleAddUserToOrganization}
onChangeSuperAdmin={this.handleChangeSuperAdmin}
onDelete={this.handleDeleteUser}
meID={meID}
@ -128,10 +130,12 @@ AllUsersTable.propTypes = {
superAdmin: bool,
})
).isRequired,
organization: shape({
name: string.isRequired,
id: string.isRequired,
}),
organizations: arrayOf(
shape({
name: string.isRequired,
id: string.isRequired,
})
),
onCreateUser: func.isRequired,
onUpdateUserRole: func.isRequired,
onUpdateUserSuperAdmin: func.isRequired,

View File

@ -1,55 +1,47 @@
import React, {Component, PropTypes} from 'react'
import React, {PropTypes} from 'react'
class AllUsersTableHeader extends Component {
constructor(props) {
super(props)
}
const AllUsersTableHeader = ({
numUsers,
numOrganizations,
onClickCreateUser,
isCreatingUser,
}) => {
const numUsersString = `${numUsers} User${numUsers === 1 ? '' : 's'}`
const numOrganizationsString = `${numOrganizations} Org${numOrganizations ===
1
? ''
: 's'}`
render() {
const {
onClickCreateUser,
numUsers,
isCreatingUser,
organization,
} = this.props
const panelTitle = numUsers === 1 ? `${numUsers} User` : `${numUsers} Users`
return (
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
<h2 className="panel-title">
{panelTitle} in <em>{organization.name}</em>
</h2>
<button
className="btn btn-primary btn-sm"
onClick={onClickCreateUser}
disabled={isCreatingUser || !onClickCreateUser}
>
<span className="icon plus" />
Create User
</button>
</div>
)
}
return (
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
<h2 className="panel-title">
{numUsersString} in {numOrganizationsString}
</h2>
<button
className="btn btn-primary btn-sm"
onClick={onClickCreateUser}
disabled={isCreatingUser || !onClickCreateUser}
>
<span className="icon plus" />
Create User
</button>
</div>
)
}
const {bool, func, shape, string, number} = PropTypes
const {bool, func, number} = PropTypes
AllUsersTableHeader.defaultProps = {
numUsers: 0,
organization: {
name: '',
},
numOrganizations: 0,
isCreatingUser: false,
}
AllUsersTableHeader.propTypes = {
numUsers: number.isRequired,
numOrganizations: number.isRequired,
onClickCreateUser: func,
isCreatingUser: bool.isRequired,
organization: shape({
name: string.isRequired,
}),
}
export default AllUsersTableHeader

View File

@ -4,26 +4,22 @@ import Dropdown from 'shared/components/Dropdown'
import SlideToggle from 'shared/components/SlideToggle'
import DeleteConfirmTableCell from 'shared/components/DeleteConfirmTableCell'
import {USER_ROLES} from 'src/admin/constants/chronografAdmin'
import {USERS_TABLE} from 'src/admin/constants/chronografTableSizing'
const AllUsersTableRow = ({
organizations,
user,
organization,
onChangeUserRole,
onAddUserToOrganization,
onChangeSuperAdmin,
onDelete,
meID,
}) => {
const {colRole, colSuperAdmin, colProvider, colScheme} = USERS_TABLE
const dropdownRolesItems = USER_ROLES.map(r => ({
const dropdownOrganizationsItems = organizations.map(r => ({
...r,
text: r.name,
}))
const currentRole = user.roles.find(
role => role.organization === organization.id
)
const userIsMe = user.id === meID
@ -42,9 +38,9 @@ const AllUsersTableRow = ({
<td style={{width: colRole}}>
<span className="chronograf-user--role">
<Dropdown
items={dropdownRolesItems}
selected={currentRole.name}
onChoose={onChangeUserRole(user, currentRole)}
items={dropdownOrganizationsItems}
selected={'Add to Organization'}
onChoose={onAddUserToOrganization(user)}
buttonColor="btn-primary"
buttonSize="btn-xs"
className="dropdown-stretch"
@ -76,7 +72,7 @@ const AllUsersTableRow = ({
)
}
const {func, shape, string} = PropTypes
const {arrayOf, func, shape, string} = PropTypes
AllUsersTableRow.propTypes = {
user: shape(),
@ -84,10 +80,16 @@ AllUsersTableRow.propTypes = {
name: string.isRequired,
id: string.isRequired,
}),
onChangeUserRole: func.isRequired,
onAddUserToOrganization: func.isRequired,
onChangeSuperAdmin: func.isRequired,
onDelete: func.isRequired,
meID: string.isRequired,
organizations: arrayOf(
shape({
id: string.isRequired,
name: string.isRequired,
})
),
}
export default AllUsersTableRow

View File

@ -3,9 +3,10 @@ import React, {Component, PropTypes} from 'react'
import Dropdown from 'shared/components/Dropdown'
import {USERS_TABLE} from 'src/admin/constants/chronografTableSizing'
import {USER_ROLES} from 'src/admin/constants/chronografAdmin'
class UsersTableRowNew extends Component {
const nullOrganization = {id: null, name: 'None'}
class AllUsersTableRowNew extends Component {
constructor(props) {
super(props)
@ -13,7 +14,11 @@ class UsersTableRowNew extends Component {
name: '',
provider: '',
scheme: 'oauth2',
role: this.props.organization.defaultRole,
roles: [
{
...nullOrganization,
},
],
}
}
@ -22,20 +27,15 @@ class UsersTableRowNew extends Component {
}
handleConfirmCreateUser = () => {
const {onBlur, onCreateUser, organization} = this.props
const {name, provider, scheme, role, superAdmin} = this.state
const {onBlur, onCreateUser} = this.props
const {name, provider, scheme, roles, superAdmin} = this.state
const newUser = {
name,
provider,
scheme,
superAdmin,
roles: [
{
name: role,
organization: organization.id,
},
],
roles: roles[0].id === null ? [] : roles,
}
onCreateUser(newUser)
@ -46,8 +46,18 @@ class UsersTableRowNew extends Component {
e.target.select()
}
handleSelectRole = newRole => {
this.setState({role: newRole.text})
handleSelectOrganization = newOrganization => {
const newRoles = [
newOrganization.id === null
? {
...nullOrganization,
}
: {
id: newOrganization.id,
name: '*', // '*' causes the server to determine the current defaultRole of the selected organization
},
]
this.setState({roles: newRoles})
}
handleKeyDown = e => {
@ -70,6 +80,9 @@ class UsersTableRowNew extends Component {
}
render() {
const {organizations, onBlur} = this.props
const {name, provider, scheme, roles} = this.state
const {
colRole,
colProvider,
@ -77,10 +90,18 @@ class UsersTableRowNew extends Component {
colSuperAdmin,
colActions,
} = USERS_TABLE
const {onBlur} = this.props
const {name, provider, scheme, role} = this.state
const dropdownRolesItems = USER_ROLES.map(r => ({...r, text: r.name}))
const dropdownOrganizationsItems = [
{...nullOrganization},
...organizations,
].map(o => ({
...o,
text: o.name,
}))
const selectedRole = dropdownOrganizationsItems.find(
o => roles[0].id === o.id
)
const preventCreate = !name || !provider
return (
@ -98,9 +119,9 @@ class UsersTableRowNew extends Component {
</td>
<td style={{width: colRole}}>
<Dropdown
items={dropdownRolesItems}
selected={role}
onChoose={this.handleSelectRole}
items={dropdownOrganizationsItems}
selected={selectedRole.text}
onChoose={this.handleSelectOrganization}
buttonColor="btn-primary"
buttonSize="btn-xs"
className="dropdown-stretch"
@ -145,16 +166,18 @@ class UsersTableRowNew extends Component {
}
}
const {func, shape, string} = PropTypes
const {arrayOf, func, shape, string} = PropTypes
UsersTableRowNew.propTypes = {
organization: shape({
id: string.isRequired,
name: string.isRequired,
}),
AllUsersTableRowNew.propTypes = {
organizations: arrayOf(
shape({
id: string.isRequired,
name: string.isRequired,
})
),
onBlur: func.isRequired,
onCreateUser: func.isRequired,
notify: func.isRequired,
}
export default UsersTableRowNew
export default AllUsersTableRowNew

View File

@ -52,35 +52,25 @@ class AllUsersPage extends Component {
await Promise.all([
loadOrganizationsAsync(links.organizations),
loadUsersAsync(links.users),
loadUsersAsync(links.rawUsers),
])
this.setState({isLoading: false})
}
render() {
const {
meCurrentOrganization,
organizations,
meID,
users,
notify,
} = this.props
const {organizations, meID, users, notify} = this.props
const {isLoading} = this.state
if (isLoading) {
return <AllUsersTableEmpty />
}
const organization = organizations.find(
o => o.id === meCurrentOrganization.id
)
return (
<AllUsersTable
meID={meID}
users={users}
organization={organization}
organizations={organizations}
onCreateUser={this.handleCreateUser}
onUpdateUserRole={this.handleUpdateUserRole}
onUpdateUserSuperAdmin={this.handleUpdateUserSuperAdmin}
@ -98,10 +88,6 @@ AllUsersPage.propTypes = {
users: string.isRequired,
}),
meID: string.isRequired,
meCurrentOrganization: shape({
id: string.isRequired,
name: string.isRequired,
}).isRequired,
users: arrayOf(shape),
organizations: arrayOf(shape),
actions: shape({

View File

@ -62,6 +62,7 @@ export const getMeAsync = ({shouldResetMe = false} = {}) => async dispatch => {
data: me,
auth,
users,
rawUsers,
meLink,
config,
external,
@ -82,6 +83,7 @@ export const getMeAsync = ({shouldResetMe = false} = {}) => async dispatch => {
linksReceived({
external,
users,
rawUsers,
organizations,
me: meLink,
config,

View File

@ -15,6 +15,7 @@ const generateResponseWithLinks = (response, newLinks) => {
logout,
external,
users,
rawUsers,
organizations,
me: meLink,
config,
@ -27,6 +28,7 @@ const generateResponseWithLinks = (response, newLinks) => {
logoutLink: logout,
external,
users,
rawUsers,
organizations,
meLink,
config,