Add capability to delete a role.

pull/971/head
Hunter Trujillo 2017-03-06 13:22:54 -07:00
parent c7aa12b48c
commit e214afba82
8 changed files with 122 additions and 17 deletions

View File

@ -0,0 +1,37 @@
import reducer from 'src/admin/reducers/admin'
import {
loadRoles,
deleteRole,
} from 'src/admin/actions'
let state = undefined
const r1 = {name: 'role1'}
const r2 = {name: 'role2'}
const roles = [r1, r2]
describe('Admin.Reducers', () => {
it('it can load the roles', () => {
const actual = reducer(state, loadRoles({roles}))
const expected = {
roles,
}
expect(actual.roles).to.deep.equal(expected.roles)
})
it('it can delete the roles', () => {
state = {
roles: [
r1,
]
}
const actual = reducer(state, deleteRole(r1))
const expected = {
roles: [],
}
expect(actual.roles).to.deep.equal(expected.roles)
})
})

View File

@ -1,4 +1,4 @@
import {getUsers, getRoles} from 'src/admin/apis'
import {getUsers, getRoles, deleteRole as deleteRoleAJAX} from 'src/admin/apis'
import {killQuery as killQueryProxy} from 'shared/apis/metaQuery'
export const loadUsers = ({users}) => ({
@ -29,11 +29,6 @@ export const loadQueries = (queries) => ({
},
})
// async actions
export const loadUsersAsync = (url) => async (dispatch) => {
const {data} = await getUsers(url)
dispatch(loadUsers(data))
}
export const loadRoles = ({roles}) => ({
type: 'LOAD_ROLES',
payload: {
@ -41,6 +36,19 @@ export const loadRoles = ({roles}) => ({
},
})
export const deleteRole = (role) => ({
type: 'DELETE_ROLE',
payload: {
role,
},
})
// async actions
export const loadUsersAsync = (url) => async (dispatch) => {
const {data} = await getUsers(url)
dispatch(loadUsers(data))
}
export const loadRolesAsync = (url) => async (dispatch) => {
const {data} = await getRoles(url)
dispatch(loadRoles(data))
@ -54,3 +62,11 @@ export const killQueryAsync = (source, queryID) => (dispatch) => {
// kill query on server
killQueryProxy(source, queryID)
}
export const deleteRoleAsync = (role) => (dispatch) => {
// optimistic update
dispatch(deleteRole(role))
// delete role on server
deleteRoleAJAX(role.links.self)
}

View File

@ -21,3 +21,14 @@ export const getRoles = async (url) => {
console.error(error) // eslint-disable-line no-console
}
}
export const deleteRole = async (url) => {
try {
return await AJAX({
method: 'DELETE',
url,
})
} catch (error) {
console.error(error) // eslint-disable-line no-console
}
}

View File

@ -22,7 +22,7 @@ class AdminTabs extends Component {
}
render() {
const {users, roles, source} = this.props
const {users, roles, source, onDeleteRole} = this.props
return (
<Tabs onSelect={this.handleActivateTab}>
@ -40,6 +40,7 @@ class AdminTabs extends Component {
<TabPanel>
<RolesTable
roles={roles}
onDelete={onDeleteRole}
/>
</TabPanel>
<TabPanel>
@ -53,6 +54,7 @@ class AdminTabs extends Component {
const {
arrayOf,
func,
shape,
string,
} = PropTypes
@ -66,6 +68,7 @@ AdminTabs.propTypes = {
})),
source: shape(),
roles: arrayOf(shape()),
onDeleteRole: func.isRequired,
}
export default AdminTabs

View File

@ -25,7 +25,7 @@ const PERMISSIONS = [
"KapacitorConfigAPI",
]
const RoleRow = ({role: {name, permissions, users}}) => (
const RoleRow = ({role: {name, permissions, users}, role, onDelete}) => (
<tr>
<td>{name}</td>
<td>
@ -43,7 +43,7 @@ const RoleRow = ({role: {name, permissions, users}}) => (
{
users && users.length ?
<MultiSelectDropdown
items={users.map((role) => role.name)}
items={users.map((r) => r.name)}
selectedItems={[]}
label={'Select Users'}
onApply={() => '//TODO'}
@ -51,13 +51,19 @@ const RoleRow = ({role: {name, permissions, users}}) => (
}
</td>
<td className="text-right">
<button className="btn btn-xs btn-danger admin-table--delete">Delete</button>
<button
className="btn btn-xs btn-danger admin-table--delete"
onClick={() => onDelete(role)}
>
Delete
</button>
</td>
</tr>
)
const {
arrayOf,
func,
shape,
string,
} = PropTypes
@ -72,6 +78,7 @@ RoleRow.propTypes = {
name: string,
})),
}).isRequired,
onDelete: func.isRequired,
}
export default RoleRow

View File

@ -2,7 +2,7 @@ import React, {PropTypes} from 'react'
import RoleRow from 'src/admin/components/RoleRow'
import EmptyRow from 'src/admin/components/EmptyRow'
const RolesTable = ({roles}) => (
const RolesTable = ({roles, onDelete}) => (
<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">
@ -31,7 +31,7 @@ const RolesTable = ({roles}) => (
{
roles.length ?
roles.map((role) =>
<RoleRow key={role.name} role={role} />
<RoleRow key={role.name} role={role} onDelete={onDelete} />
) : <EmptyRow tableName={'Roles'} />
}
</tbody>
@ -42,6 +42,7 @@ const RolesTable = ({roles}) => (
const {
arrayOf,
func,
shape,
string,
} = PropTypes
@ -57,6 +58,7 @@ RolesTable.propTypes = {
name: string,
})),
})),
onDelete: func.isRequired,
}
export default RolesTable

View File

@ -1,12 +1,17 @@
import React, {Component, PropTypes} from 'react'
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {loadUsersAsync, loadRolesAsync} from 'src/admin/actions'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import {
loadUsersAsync,
loadRolesAsync,
deleteRoleAsync,
} from 'src/admin/actions'
import AdminTabs from 'src/admin/components/AdminTabs'
class AdminPage extends Component {
constructor(props) {
super(props)
this.handleDeleteRole = ::this.handleDeleteRole
}
componentDidMount() {
@ -18,6 +23,10 @@ class AdminPage extends Component {
}
}
handleDeleteRole(role) {
this.props.deleteRole(role)
}
render() {
const {users, roles, source} = this.props
@ -36,7 +45,16 @@ class AdminPage extends Component {
<div className="container-fluid">
<div className="row">
<div className="col-md-12">
{users.length ? <AdminTabs users={users} roles={roles} source={source}/> : <span>Loading...</span>}
{
users.length ?
<AdminTabs
users={users}
roles={roles}
source={source}
onDeleteRole={this.handleDeleteRole}
/> :
<span>Loading...</span>
}
</div>
</div>
</div>
@ -64,6 +82,7 @@ AdminPage.propTypes = {
roles: arrayOf(shape()),
loadUsers: func,
loadRoles: func,
deleteRole: func,
}
const mapStateToProps = ({admin}) => ({
@ -74,6 +93,7 @@ const mapStateToProps = ({admin}) => ({
const mapDispatchToProps = (dispatch) => ({
loadUsers: bindActionCreators(loadUsersAsync, dispatch),
loadRoles: bindActionCreators(loadRolesAsync, dispatch),
deleteRole: bindActionCreators(deleteRoleAsync, dispatch),
})
export default connect(mapStateToProps, mapDispatchToProps)(AdminPage);
export default connect(mapStateToProps, mapDispatchToProps)(AdminPage)

View File

@ -17,6 +17,15 @@ export default function admin(state = initialState, action) {
return {...state, ...action.payload}
}
case 'DELETE_ROLE': {
const {role} = action.payload
const newState = {
roles: state.roles.filter(r => r.name !== role.name),
}
return {...state, ...newState}
}
case 'LOAD_QUERIES': {
return {...state, ...action.payload}
}