Merge pull request #973 from influxdata/feature/934-ew-admin-filtering
Add filtering capability to users and roles admin pages.pull/10616/head
commit
6ebc68988e
|
@ -4,6 +4,8 @@ import {
|
|||
loadRoles,
|
||||
deleteRole,
|
||||
deleteUser,
|
||||
filterRoles,
|
||||
filterUsers,
|
||||
} from 'src/admin/actions'
|
||||
|
||||
let state = undefined
|
||||
|
@ -54,4 +56,40 @@ describe('Admin.Reducers', () => {
|
|||
|
||||
expect(actual.users).to.deep.equal(expected.users)
|
||||
})
|
||||
|
||||
it('can filter roles w/ "1" text', () => {
|
||||
state = {
|
||||
roles,
|
||||
}
|
||||
|
||||
const text = '1'
|
||||
|
||||
const actual = reducer(state, filterRoles(text))
|
||||
const expected = {
|
||||
roles: [
|
||||
{...r1, hidden: false},
|
||||
{...r2, hidden: true},
|
||||
],
|
||||
}
|
||||
|
||||
expect(actual.roles).to.deep.equal(expected.roles)
|
||||
})
|
||||
|
||||
it('can filter users w/ "2" text', () => {
|
||||
state = {
|
||||
users,
|
||||
}
|
||||
|
||||
const text = '2'
|
||||
|
||||
const actual = reducer(state, filterUsers(text))
|
||||
const expected = {
|
||||
users: [
|
||||
{...u1, hidden: true},
|
||||
{...u2, hidden: false},
|
||||
],
|
||||
}
|
||||
|
||||
expect(actual.users).to.deep.equal(expected.users)
|
||||
})
|
||||
})
|
||||
|
|
|
@ -55,6 +55,20 @@ export const deleteUser = (user) => ({
|
|||
},
|
||||
})
|
||||
|
||||
export const filterRoles = (text) => ({
|
||||
type: 'FILTER_ROLES',
|
||||
payload: {
|
||||
text,
|
||||
},
|
||||
})
|
||||
|
||||
export const filterUsers = (text) => ({
|
||||
type: 'FILTER_USERS',
|
||||
payload: {
|
||||
text,
|
||||
},
|
||||
})
|
||||
|
||||
// async actions
|
||||
export const loadUsersAsync = (url) => async (dispatch) => {
|
||||
const {data} = await getUsers(url)
|
||||
|
|
|
@ -22,7 +22,15 @@ class AdminTabs extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {users, roles, source, onDeleteRole, onDeleteUser} = this.props
|
||||
const {
|
||||
users,
|
||||
roles,
|
||||
source,
|
||||
onDeleteRole,
|
||||
onDeleteUser,
|
||||
onFilterRoles,
|
||||
onFilterUsers,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<Tabs onSelect={this.handleActivateTab}>
|
||||
|
@ -36,12 +44,14 @@ class AdminTabs extends Component {
|
|||
<UsersTable
|
||||
users={users}
|
||||
onDelete={onDeleteUser}
|
||||
onFilter={onFilterUsers}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
<RolesTable
|
||||
roles={roles}
|
||||
onDelete={onDeleteRole}
|
||||
onFilter={onFilterRoles}
|
||||
/>
|
||||
</TabPanel>
|
||||
<TabPanel>
|
||||
|
@ -71,6 +81,8 @@ AdminTabs.propTypes = {
|
|||
roles: arrayOf(shape()),
|
||||
onDeleteRole: func.isRequired,
|
||||
onDeleteUser: func.isRequired,
|
||||
onFilterRoles: func.isRequired,
|
||||
onFilterUsers: func.isRequired,
|
||||
}
|
||||
|
||||
export default AdminTabs
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
import React, {Component, PropTypes} from 'react'
|
||||
|
||||
class FilterBar extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
filterText: '',
|
||||
}
|
||||
|
||||
this.handleText = ::this.handleText
|
||||
}
|
||||
|
||||
handleText(e) {
|
||||
this.setState(
|
||||
{filterText: e.target.value},
|
||||
this.props.onFilter(e.target.value)
|
||||
)
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
|
||||
<div className="users__search-widget input-group admin__search-widget">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder={`Filter ${name}...`}
|
||||
value={this.state.filterText}
|
||||
onChange={this.handleText}
|
||||
/>
|
||||
<div className="input-group-addon">
|
||||
<span className="icon search" aria-hidden="true"></span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" className="btn btn-primary">Create {name}</a>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {
|
||||
func,
|
||||
string,
|
||||
} = PropTypes
|
||||
|
||||
FilterBar.propTypes = {
|
||||
onFilter: func.isRequired,
|
||||
name: string,
|
||||
}
|
||||
|
||||
export default FilterBar
|
|
@ -1,22 +1,11 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import RoleRow from 'src/admin/components/RoleRow'
|
||||
import EmptyRow from 'src/admin/components/EmptyRow'
|
||||
import FilterBar from 'src/admin/components/FilterBar'
|
||||
|
||||
const RolesTable = ({roles, onDelete}) => (
|
||||
const RolesTable = ({roles, onDelete, onFilter}) => (
|
||||
<div className="panel panel-info">
|
||||
<div className="panel-heading u-flex u-ai-center u-jc-space-between">
|
||||
<div className="users__search-widget input-group admin__search-widget">
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Filter Role..."
|
||||
/>
|
||||
<div className="input-group-addon">
|
||||
<span className="icon search" aria-hidden="true"></span>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" className="btn btn-primary">Create Role</a>
|
||||
</div>
|
||||
<FilterBar name="Roles" onFilter={onFilter} />
|
||||
<div className="panel-body">
|
||||
<table className="table v-center admin-table">
|
||||
<thead>
|
||||
|
@ -30,7 +19,7 @@ const RolesTable = ({roles, onDelete}) => (
|
|||
<tbody>
|
||||
{
|
||||
roles.length ?
|
||||
roles.map((role) =>
|
||||
roles.filter(r => !r.hidden).map((role) =>
|
||||
<RoleRow key={role.name} role={role} onDelete={onDelete} />
|
||||
) : <EmptyRow tableName={'Roles'} />
|
||||
}
|
||||
|
@ -59,6 +48,7 @@ RolesTable.propTypes = {
|
|||
})),
|
||||
})),
|
||||
onDelete: func.isRequired,
|
||||
onFilter: func,
|
||||
}
|
||||
|
||||
export default RolesTable
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import React, {PropTypes} from 'react'
|
||||
import UserRow from 'src/admin/components/UserRow'
|
||||
import EmptyRow from 'src/admin/components/EmptyRow'
|
||||
import FilterBar from 'src/admin/components/FilterBar'
|
||||
|
||||
const UsersTable = ({users, onDelete}) => (
|
||||
const UsersTable = ({users, onDelete, onFilter}) => (
|
||||
<div className="panel panel-info">
|
||||
<FilterBar name="Users" onFilter={onFilter} />
|
||||
<div className="panel-body">
|
||||
<table className="table v-center admin-table">
|
||||
<thead>
|
||||
|
@ -17,7 +19,7 @@ const UsersTable = ({users, onDelete}) => (
|
|||
<tbody>
|
||||
{
|
||||
users.length ?
|
||||
users.map((user) =>
|
||||
users.filter(u => !u.hidden).map((user) =>
|
||||
<UserRow key={user.name} user={user} onDelete={onDelete} />
|
||||
) : <EmptyRow tableName={'Users'} />
|
||||
}
|
||||
|
@ -46,6 +48,7 @@ UsersTable.propTypes = {
|
|||
})),
|
||||
})),
|
||||
onDelete: func.isRequired,
|
||||
onFilter: func,
|
||||
}
|
||||
|
||||
export default UsersTable
|
||||
|
|
|
@ -6,6 +6,8 @@ import {
|
|||
loadRolesAsync,
|
||||
deleteRoleAsync,
|
||||
deleteUserAsync,
|
||||
filterRoles as filterRolesAction,
|
||||
filterUsers as filterUsersAction,
|
||||
} from 'src/admin/actions'
|
||||
import AdminTabs from 'src/admin/components/AdminTabs'
|
||||
|
||||
|
@ -34,7 +36,7 @@ class AdminPage extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const {users, roles, source} = this.props
|
||||
const {users, roles, source, filterUsers, filterRoles} = this.props
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
|
@ -59,6 +61,8 @@ class AdminPage extends Component {
|
|||
source={source}
|
||||
onDeleteRole={this.handleDeleteRole}
|
||||
onDeleteUser={this.handleDeleteUser}
|
||||
onFilterUsers={filterUsers}
|
||||
onFilterRoles={filterRoles}
|
||||
/> :
|
||||
<span>Loading...</span>
|
||||
}
|
||||
|
@ -92,6 +96,8 @@ AdminPage.propTypes = {
|
|||
deleteRole: func,
|
||||
deleteUser: func,
|
||||
addFlashMessage: func,
|
||||
filterRoles: func,
|
||||
filterUsers: func,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({admin}) => ({
|
||||
|
@ -104,6 +110,8 @@ const mapDispatchToProps = (dispatch) => ({
|
|||
loadRoles: bindActionCreators(loadRolesAsync, dispatch),
|
||||
deleteRole: bindActionCreators(deleteRoleAsync, dispatch),
|
||||
deleteUser: bindActionCreators(deleteUserAsync, dispatch),
|
||||
filterRoles: bindActionCreators(filterRolesAction, dispatch),
|
||||
filterUsers: bindActionCreators(filterUsersAction, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(AdminPage)
|
||||
|
|
|
@ -39,6 +39,30 @@ export default function admin(state = initialState, action) {
|
|||
return {...state, ...action.payload}
|
||||
}
|
||||
|
||||
case 'FILTER_ROLES': {
|
||||
const {text} = action.payload
|
||||
const newState = {
|
||||
roles: state.roles.map(r => {
|
||||
r.hidden = !r.name.toLowerCase().includes(text)
|
||||
return r
|
||||
}),
|
||||
}
|
||||
|
||||
return {...state, ...newState}
|
||||
}
|
||||
|
||||
case 'FILTER_USERS': {
|
||||
const {text} = action.payload
|
||||
const newState = {
|
||||
users: state.users.map(u => {
|
||||
u.hidden = !u.name.toLowerCase().includes(text)
|
||||
return u
|
||||
}),
|
||||
}
|
||||
|
||||
return {...state, ...newState}
|
||||
}
|
||||
|
||||
case 'KILL_QUERY': {
|
||||
const {queryID} = action.payload
|
||||
const nextState = {
|
||||
|
|
Loading…
Reference in New Issue