Refactor AdminChronograf into UsersPage to ensure correct users via AJAX & guard
Move OrganizationsPage into /chronograf & cleanuppull/10616/head
parent
0f075d7b9d
commit
db91aca339
|
@ -7,44 +7,28 @@ import {
|
|||
} from 'src/auth/Authorized'
|
||||
|
||||
import {Tab, Tabs, TabPanel, TabPanels, TabList} from 'shared/components/Tabs'
|
||||
import OrganizationsPage from 'src/admin/containers/OrganizationsPage'
|
||||
import UsersTable from 'src/admin/components/chronograf/UsersTable'
|
||||
import OrganizationsPage from 'src/admin/containers/chronograf/OrganizationsPage'
|
||||
import UsersPage from 'src/admin/containers/chronograf/UsersPage'
|
||||
|
||||
const ORGANIZATIONS_TAB_NAME = 'Organizations'
|
||||
const USERS_TAB_NAME = 'Users'
|
||||
|
||||
const AdminTabs = ({
|
||||
meRole,
|
||||
// UsersTable
|
||||
users,
|
||||
organization,
|
||||
onCreateUser,
|
||||
onUpdateUserRole,
|
||||
onUpdateUserSuperAdmin,
|
||||
onDeleteUser,
|
||||
meID,
|
||||
notify,
|
||||
me: {currentOrganization: meCurrentOrganization, role: meRole, id: meID},
|
||||
}) => {
|
||||
const tabs = [
|
||||
{
|
||||
requiredRole: SUPERADMIN_ROLE,
|
||||
type: ORGANIZATIONS_TAB_NAME,
|
||||
component: <OrganizationsPage currentOrganization={organization} />,
|
||||
component: (
|
||||
<OrganizationsPage meCurrentOrganization={meCurrentOrganization} />
|
||||
),
|
||||
},
|
||||
{
|
||||
requiredRole: ADMIN_ROLE,
|
||||
type: USERS_TAB_NAME,
|
||||
component: (
|
||||
<UsersTable
|
||||
users={users}
|
||||
organization={organization}
|
||||
onCreateUser={onCreateUser}
|
||||
onUpdateUserRole={onUpdateUserRole}
|
||||
onUpdateUserSuperAdmin={onUpdateUserSuperAdmin}
|
||||
onDeleteUser={onDeleteUser}
|
||||
meID={meID}
|
||||
notify={notify}
|
||||
/>
|
||||
<UsersPage meID={meID} meCurrentOrganization={meCurrentOrganization} />
|
||||
),
|
||||
},
|
||||
].filter(t => isUserAuthorized(meRole, t.requiredRole))
|
||||
|
@ -69,46 +53,17 @@ const AdminTabs = ({
|
|||
)
|
||||
}
|
||||
|
||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||
|
||||
AdminTabs.defaultProps = {
|
||||
organization: {
|
||||
name: '',
|
||||
id: '0',
|
||||
},
|
||||
}
|
||||
const {shape, string} = PropTypes
|
||||
|
||||
AdminTabs.propTypes = {
|
||||
meRole: string.isRequired,
|
||||
meID: string.isRequired,
|
||||
// UsersTable
|
||||
users: arrayOf(
|
||||
shape({
|
||||
id: string,
|
||||
links: shape({
|
||||
self: string.isRequired,
|
||||
}),
|
||||
name: string.isRequired,
|
||||
provider: string.isRequired,
|
||||
roles: arrayOf(
|
||||
shape({
|
||||
name: string.isRequired,
|
||||
organization: string.isRequired,
|
||||
})
|
||||
),
|
||||
scheme: string.isRequired,
|
||||
superAdmin: bool,
|
||||
})
|
||||
).isRequired,
|
||||
organization: shape({
|
||||
name: string.isRequired,
|
||||
me: shape({
|
||||
id: string.isRequired,
|
||||
role: string.isRequired,
|
||||
currentOrganization: shape({
|
||||
name: string.isRequired,
|
||||
id: string.isRequired,
|
||||
}),
|
||||
}).isRequired,
|
||||
onCreateUser: func.isRequired,
|
||||
onUpdateUserRole: func.isRequired,
|
||||
onUpdateUserSuperAdmin: func.isRequired,
|
||||
onDeleteUser: func.isRequired,
|
||||
notify: func.isRequired,
|
||||
}
|
||||
|
||||
export default AdminTabs
|
||||
|
|
|
@ -120,10 +120,27 @@ class UsersTable extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
const {arrayOf, bool, func, shape, string} = PropTypes
|
||||
|
||||
UsersTable.propTypes = {
|
||||
users: arrayOf(shape()),
|
||||
users: arrayOf(
|
||||
shape({
|
||||
id: string,
|
||||
links: shape({
|
||||
self: string.isRequired,
|
||||
}),
|
||||
name: string.isRequired,
|
||||
provider: string.isRequired,
|
||||
roles: arrayOf(
|
||||
shape({
|
||||
name: string.isRequired,
|
||||
organization: string.isRequired,
|
||||
})
|
||||
),
|
||||
scheme: string.isRequired,
|
||||
superAdmin: bool,
|
||||
})
|
||||
).isRequired,
|
||||
organization: shape({
|
||||
name: string.isRequired,
|
||||
id: string.isRequired,
|
||||
|
|
|
@ -1,170 +1,42 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import React, {PropTypes} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import * as adminChronografActionCreators from 'src/admin/actions/chronograf'
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
|
||||
import AdminTabs from 'src/admin/components/chronograf/AdminTabs'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
|
||||
class AdminChronografPage extends Component {
|
||||
// TODO: revisit this, possibly don't call setState if both are deep equal
|
||||
componentWillReceiveProps(nextProps) {
|
||||
const {meCurrentOrganization} = nextProps
|
||||
|
||||
const hasChangedCurrentOrganization =
|
||||
meCurrentOrganization.id !== this.props.meCurrentOrganization.id
|
||||
|
||||
if (hasChangedCurrentOrganization) {
|
||||
this.loadUsers()
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.loadOrganizations()
|
||||
this.loadUsers()
|
||||
}
|
||||
|
||||
loadUsers = () => {
|
||||
const {links, actions: {loadUsersAsync}} = this.props
|
||||
|
||||
loadUsersAsync(links.users)
|
||||
}
|
||||
|
||||
loadOrganizations = () => {
|
||||
const {links, actions: {loadOrganizationsAsync}} = this.props
|
||||
loadOrganizationsAsync(links.organizations)
|
||||
}
|
||||
|
||||
// SINGLE USER ACTIONS
|
||||
handleCreateUser = user => {
|
||||
const {links, actions: {createUserAsync}} = this.props
|
||||
|
||||
createUserAsync(links.users, user)
|
||||
}
|
||||
|
||||
handleUpdateUserRole = (user, currentRole, {name}) => {
|
||||
const {actions: {updateUserAsync}} = this.props
|
||||
|
||||
const updatedRole = {...currentRole, name}
|
||||
const newRoles = user.roles.map(
|
||||
r => (r.organization === currentRole.organization ? updatedRole : r)
|
||||
)
|
||||
|
||||
updateUserAsync(user, {...user, roles: newRoles})
|
||||
}
|
||||
|
||||
handleUpdateUserSuperAdmin = (user, superAdmin) => {
|
||||
const {actions: {updateUserAsync}} = this.props
|
||||
|
||||
const updatedUser = {...user, superAdmin}
|
||||
|
||||
updateUserAsync(user, updatedUser)
|
||||
}
|
||||
|
||||
handleDeleteUser = user => {
|
||||
const {actions: {deleteUserAsync}} = this.props
|
||||
|
||||
deleteUserAsync(user)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
users,
|
||||
meRole,
|
||||
meID,
|
||||
notify,
|
||||
organizations,
|
||||
meCurrentOrganization,
|
||||
} = this.props
|
||||
|
||||
const organization = organizations.find(
|
||||
o => o.id === meCurrentOrganization.id
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1 className="page-header__title">Chronograf Admin</h1>
|
||||
</div>
|
||||
</div>
|
||||
const AdminChronografPage = ({me}) =>
|
||||
<div className="page">
|
||||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
||||
<h1 className="page-header__title">Chronograf Admin</h1>
|
||||
</div>
|
||||
<FancyScrollbar className="page-contents">
|
||||
{users.length && organizations.length
|
||||
? <div className="container-fluid">
|
||||
<div className="row">
|
||||
<AdminTabs
|
||||
meRole={meRole}
|
||||
meID={meID}
|
||||
// UsersTable
|
||||
users={users}
|
||||
organization={organization}
|
||||
onCreateUser={this.handleCreateUser}
|
||||
onUpdateUserRole={this.handleUpdateUserRole}
|
||||
onUpdateUserSuperAdmin={this.handleUpdateUserSuperAdmin}
|
||||
onDeleteUser={this.handleDeleteUser}
|
||||
notify={notify}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
: <div className="page-spinner" />}
|
||||
</FancyScrollbar>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
</div>
|
||||
<FancyScrollbar className="page-contents">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<AdminTabs me={me} />
|
||||
</div>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
</div>
|
||||
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
const {shape, string} = PropTypes
|
||||
|
||||
AdminChronografPage.propTypes = {
|
||||
links: shape({
|
||||
users: string.isRequired,
|
||||
}),
|
||||
users: arrayOf(shape),
|
||||
organizations: arrayOf(shape),
|
||||
meCurrentOrganization: shape({
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
}).isRequired,
|
||||
meRole: string.isRequired,
|
||||
me: shape({
|
||||
name: string.isRequired,
|
||||
id: string.isRequired,
|
||||
role: string.isRequired,
|
||||
currentOrganization: shape({
|
||||
name: string.isRequired,
|
||||
id: string.isRequired,
|
||||
}),
|
||||
}).isRequired,
|
||||
meID: string.isRequired,
|
||||
actions: shape({
|
||||
loadUsersAsync: func.isRequired,
|
||||
loadOrganizationsAsync: func.isRequired,
|
||||
createUserAsync: func.isRequired,
|
||||
updateUserAsync: func.isRequired,
|
||||
deleteUserAsync: func.isRequired,
|
||||
}),
|
||||
notify: func.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({
|
||||
links,
|
||||
adminChronograf: {users, organizations},
|
||||
auth: {
|
||||
me,
|
||||
me: {currentOrganization: meCurrentOrganization, role: meRole, id: meID},
|
||||
},
|
||||
}) => ({
|
||||
const mapStateToProps = ({auth: {me}}) => ({
|
||||
me,
|
||||
meID,
|
||||
links,
|
||||
users,
|
||||
meRole,
|
||||
organizations,
|
||||
meCurrentOrganization,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
actions: bindActionCreators(adminChronografActionCreators, dispatch),
|
||||
notify: bindActionCreators(publishAutoDismissingNotification, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AdminChronografPage)
|
||||
export default connect(mapStateToProps, null)(AdminChronografPage)
|
||||
|
|
|
@ -71,21 +71,25 @@ class OrganizationsPage extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {organizations, currentOrganization, authConfig} = this.props
|
||||
const {meCurrentOrganization, organizations, authConfig} = this.props
|
||||
|
||||
return (
|
||||
<OrganizationsTable
|
||||
organizations={organizations}
|
||||
currentOrganization={currentOrganization}
|
||||
onCreateOrg={this.handleCreateOrganization}
|
||||
onDeleteOrg={this.handleDeleteOrganization}
|
||||
onRenameOrg={this.handleRenameOrganization}
|
||||
onTogglePublic={this.handleTogglePublic}
|
||||
onChooseDefaultRole={this.handleChooseDefaultRole}
|
||||
authConfig={authConfig}
|
||||
onChangeAuthConfig={this.handleUpdateAuthConfig}
|
||||
/>
|
||||
const organization = organizations.find(
|
||||
o => o.id === meCurrentOrganization.id
|
||||
)
|
||||
|
||||
return organizations.length
|
||||
? <OrganizationsTable
|
||||
organizations={organizations}
|
||||
currentOrganization={organization}
|
||||
onCreateOrg={this.handleCreateOrganization}
|
||||
onDeleteOrg={this.handleDeleteOrganization}
|
||||
onRenameOrg={this.handleRenameOrganization}
|
||||
onTogglePublic={this.handleTogglePublic}
|
||||
onChooseDefaultRole={this.handleChooseDefaultRole}
|
||||
authConfig={authConfig}
|
||||
onChangeAuthConfig={this.handleUpdateAuthConfig}
|
||||
/>
|
||||
: <div className="page-spinner" />
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,7 +120,7 @@ OrganizationsPage.propTypes = {
|
|||
updateAuthConfigAsync: func.isRequired,
|
||||
}),
|
||||
getMe: func.isRequired,
|
||||
currentOrganization: shape({
|
||||
meCurrentOrganization: shape({
|
||||
name: string.isRequired,
|
||||
id: string.isRequired,
|
||||
}),
|
|
@ -0,0 +1,143 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import * as adminChronografActionCreators from 'src/admin/actions/chronograf'
|
||||
import {publishAutoDismissingNotification} from 'shared/dispatchers'
|
||||
|
||||
import UsersTable from 'src/admin/components/chronograf/UsersTable'
|
||||
|
||||
class UsersPage extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
isLoading: true,
|
||||
}
|
||||
}
|
||||
|
||||
// // TODO: revisit this, possibly don't call setState if both are deep equal
|
||||
// componentWillReceiveProps(nextProps) {
|
||||
// const {meCurrentOrganization} = nextProps
|
||||
//
|
||||
// const hasChangedCurrentOrganization =
|
||||
// meCurrentOrganization.id !== this.props.meCurrentOrganization.id
|
||||
//
|
||||
// if (hasChangedCurrentOrganization) {
|
||||
// this.setState({isLoading: true})
|
||||
// this.loadUsers()
|
||||
// }
|
||||
// }
|
||||
|
||||
// loadUsers = () => {
|
||||
// // this.setState({isLoading: true})
|
||||
// return loadUsersAsync(links.users)
|
||||
// }
|
||||
|
||||
handleCreateUser = user => {
|
||||
const {links, actions: {createUserAsync}} = this.props
|
||||
createUserAsync(links.users, user)
|
||||
}
|
||||
|
||||
handleUpdateUserRole = (user, currentRole, {name}) => {
|
||||
const {actions: {updateUserAsync}} = this.props
|
||||
const updatedRole = {...currentRole, name}
|
||||
const newRoles = user.roles.map(
|
||||
r => (r.organization === currentRole.organization ? updatedRole : r)
|
||||
)
|
||||
updateUserAsync(user, {...user, roles: newRoles})
|
||||
}
|
||||
|
||||
handleUpdateUserSuperAdmin = (user, superAdmin) => {
|
||||
const {actions: {updateUserAsync}} = this.props
|
||||
const updatedUser = {...user, superAdmin}
|
||||
updateUserAsync(user, updatedUser)
|
||||
}
|
||||
|
||||
handleDeleteUser = user => {
|
||||
const {actions: {deleteUserAsync}} = this.props
|
||||
deleteUserAsync(user)
|
||||
}
|
||||
|
||||
async componentWillMount() {
|
||||
const {
|
||||
links,
|
||||
actions: {loadOrganizationsAsync, loadUsersAsync},
|
||||
} = this.props
|
||||
|
||||
this.setState({isLoading: true})
|
||||
|
||||
loadOrganizationsAsync(links.organizations)
|
||||
await loadUsersAsync(links.users)
|
||||
|
||||
this.setState({isLoading: false})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
meCurrentOrganization,
|
||||
organizations,
|
||||
meID,
|
||||
users,
|
||||
notify,
|
||||
} = this.props
|
||||
const {isLoading} = this.state
|
||||
|
||||
if (isLoading || !(organizations.length && users.length)) {
|
||||
return <div className="generic-empty-state">Loading...</div>
|
||||
}
|
||||
|
||||
const organization = organizations.find(
|
||||
o => o.id === meCurrentOrganization.id
|
||||
)
|
||||
|
||||
return (
|
||||
<UsersTable
|
||||
meID={meID}
|
||||
users={users}
|
||||
organization={organization}
|
||||
onCreateUser={this.handleCreateUser}
|
||||
onUpdateUserRole={this.handleUpdateUserRole}
|
||||
onUpdateUserSuperAdmin={this.handleUpdateUserSuperAdmin}
|
||||
onDeleteUser={this.handleDeleteUser}
|
||||
notify={notify}
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
|
||||
UsersPage.propTypes = {
|
||||
links: shape({
|
||||
users: string.isRequired,
|
||||
}),
|
||||
meID: string.isRequired,
|
||||
meCurrentOrganization: shape({
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
}).isRequired,
|
||||
users: arrayOf(shape),
|
||||
organizations: arrayOf(shape),
|
||||
actions: shape({
|
||||
loadUsersAsync: func.isRequired,
|
||||
loadOrganizationsAsync: func.isRequired,
|
||||
createUserAsync: func.isRequired,
|
||||
updateUserAsync: func.isRequired,
|
||||
deleteUserAsync: func.isRequired,
|
||||
}),
|
||||
notify: func.isRequired,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({links, adminChronograf: {organizations, users}}) => ({
|
||||
links,
|
||||
organizations,
|
||||
users,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
actions: bindActionCreators(adminChronografActionCreators, dispatch),
|
||||
notify: bindActionCreators(publishAutoDismissingNotification, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UsersPage)
|
|
@ -1,5 +1,4 @@
|
|||
import AdminInfluxDBPage from './containers/AdminInfluxDBPage'
|
||||
import AdminChronografPage from './containers/AdminChronografPage'
|
||||
import OrganizationsPage from './containers/OrganizationsPage'
|
||||
|
||||
export {AdminChronografPage, AdminInfluxDBPage, OrganizationsPage}
|
||||
export {AdminChronografPage, AdminInfluxDBPage}
|
||||
|
|
Loading…
Reference in New Issue