Add capability to delete a role.
parent
c7aa12b48c
commit
e214afba82
|
@ -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)
|
||||
})
|
||||
})
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue