Introduce delete confirmation for database
parent
9d63d2ac13
commit
94e4ea3d4b
|
@ -7,6 +7,7 @@ import {
|
|||
createUser as createUserAJAX,
|
||||
createRole as createRoleAJAX,
|
||||
createDatabase as createDatabaseAJAX,
|
||||
deleteDatabase as deleteDatabaseAJAX,
|
||||
deleteUser as deleteUserAJAX,
|
||||
deleteRole as deleteRoleAJAX,
|
||||
updateRole as updateRoleAJAX,
|
||||
|
@ -180,6 +181,28 @@ export const filterRoles = (text) => ({
|
|||
},
|
||||
})
|
||||
|
||||
export const startDeleteDatabase = (database) => ({
|
||||
type: 'START_DELETE_DATABASE',
|
||||
payload: {
|
||||
database,
|
||||
},
|
||||
})
|
||||
|
||||
export const updateDatabaseDeleteCode = (database, deleteCode) => ({
|
||||
type: 'UPDATE_DATABASE_DELETE_CODE',
|
||||
payload: {
|
||||
database,
|
||||
deleteCode,
|
||||
},
|
||||
})
|
||||
|
||||
export const removeDatabaseDeleteCode = (database) => ({
|
||||
type: 'REMOVE_DATABASE_DELETE_CODE',
|
||||
payload: {
|
||||
database,
|
||||
},
|
||||
})
|
||||
|
||||
// async actions
|
||||
export const loadUsersAsync = (url) => async (dispatch) => {
|
||||
const {data} = await getUsersAJAX(url)
|
||||
|
@ -233,6 +256,7 @@ export const createRoleAsync = (url, role) => async (dispatch) => {
|
|||
|
||||
export const createDatabaseAsync = (url, database) => async (dispatch) => {
|
||||
try {
|
||||
// TODO: implement once server is up
|
||||
// const {data} = await createDatabaseAJAX(url, database)
|
||||
dispatch(publishNotification('success', 'Database created successfully'))
|
||||
// dispatch(syncDatabase(database, {...data, id: uuid.v4()}))
|
||||
|
@ -268,6 +292,18 @@ export const deleteUserAsync = (user, addFlashMessage) => (dispatch) => {
|
|||
deleteUserAJAX(user.links.self, addFlashMessage, user.name)
|
||||
}
|
||||
|
||||
export const deleteDatabaseAsync = (url, database) => (dispatch) => {
|
||||
dispatch(removeDatabase(database))
|
||||
dispatch(publishNotification('success', 'Database deleted'))
|
||||
|
||||
// TODO: implement once server is up
|
||||
// try {
|
||||
// await deleteDatabaseAJAX(url, database.name)
|
||||
// } catch (error) {
|
||||
// dispatch(publishNotification('error', `Failed to delete database: ${error.data.message}`))
|
||||
// }
|
||||
}
|
||||
|
||||
export const updateRoleUsersAsync = (role, users) => async (dispatch) => {
|
||||
try {
|
||||
const {data} = await updateRoleAJAX(role.links.self, users, role.permissions)
|
||||
|
|
|
@ -112,6 +112,18 @@ export const deleteUser = async (url, addFlashMessage, username) => {
|
|||
}
|
||||
}
|
||||
|
||||
export const deleteDatabase = async (url, name) => {
|
||||
try {
|
||||
return await AJAX({
|
||||
method: 'DELETE',
|
||||
url: `${url}/${name}`,
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export const updateRole = async (url, users, permissions) => {
|
||||
try {
|
||||
return await AJAX({
|
||||
|
|
|
@ -9,6 +9,8 @@ const DatabaseManager = ({
|
|||
onKeyDownDatabase,
|
||||
onCancelDatabase,
|
||||
onConfirmDatabase,
|
||||
onStartDeleteDatabase,
|
||||
onDatabaseDeleteConfirm,
|
||||
}) => {
|
||||
return (
|
||||
<div className="panel panel-info">
|
||||
|
@ -27,6 +29,8 @@ const DatabaseManager = ({
|
|||
onKeyDownDatabase={onKeyDownDatabase}
|
||||
onCancelDatabase={onCancelDatabase}
|
||||
onConfirmDatabase={onConfirmDatabase}
|
||||
onStartDeleteDatabase={onStartDeleteDatabase}
|
||||
onDatabaseDeleteConfirm={onDatabaseDeleteConfirm}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -49,6 +53,8 @@ DatabaseManager.propTypes = {
|
|||
onKeyDownDatabase: func,
|
||||
onCancelDatabase: func,
|
||||
onConfirmDatabase: func,
|
||||
onStartDeleteDatabase: func,
|
||||
onDatabaseDeleteConfirm: func,
|
||||
}
|
||||
|
||||
export default DatabaseManager
|
||||
|
|
|
@ -2,6 +2,12 @@ import React, {PropTypes} from 'react'
|
|||
import {DatabaseRow} from 'src/admin/components/DatabaseRow'
|
||||
import ConfirmButtons from 'src/admin/components/ConfirmButtons'
|
||||
|
||||
const {
|
||||
arrayOf,
|
||||
func,
|
||||
shape,
|
||||
} = PropTypes
|
||||
|
||||
const DatabaseTable = ({
|
||||
database,
|
||||
retentionPolicies,
|
||||
|
@ -9,6 +15,8 @@ const DatabaseTable = ({
|
|||
onKeyDownDatabase,
|
||||
onCancelDatabase,
|
||||
onConfirmDatabase,
|
||||
onStartDeleteDatabase,
|
||||
onDatabaseDeleteConfirm,
|
||||
}) => {
|
||||
return (
|
||||
<div className="db-manager">
|
||||
|
@ -18,6 +26,8 @@ const DatabaseTable = ({
|
|||
onKeyDown={onKeyDownDatabase}
|
||||
onCancel={onCancelDatabase}
|
||||
onConfirm={onConfirmDatabase}
|
||||
onStartDelete={onStartDeleteDatabase}
|
||||
onDatabaseDeleteConfirm={onDatabaseDeleteConfirm}
|
||||
/>
|
||||
<div className="db-manager-table">
|
||||
<table className="table v-center admin-table">
|
||||
|
@ -50,7 +60,26 @@ const DatabaseTable = ({
|
|||
)
|
||||
}
|
||||
|
||||
const DatabaseTableHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => {
|
||||
DatabaseTable.propTypes = {
|
||||
onEditDatabase: func,
|
||||
database: shape(),
|
||||
retentionPolicies: arrayOf(shape()),
|
||||
onKeyDownDatabase: func,
|
||||
onCancelDatabase: func,
|
||||
onConfirmDatabase: func,
|
||||
onStartDeleteDatabase: func,
|
||||
onDatabaseDeleteConfirm: func,
|
||||
}
|
||||
|
||||
const DatabaseTableHeader = ({
|
||||
database,
|
||||
onEdit,
|
||||
onKeyDown,
|
||||
onConfirm,
|
||||
onCancel,
|
||||
onStartDelete,
|
||||
onDatabaseDeleteConfirm,
|
||||
}) => {
|
||||
if (database.isEditing) {
|
||||
return (
|
||||
<EditHeader
|
||||
|
@ -63,22 +92,78 @@ const DatabaseTableHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel})
|
|||
)
|
||||
}
|
||||
|
||||
return <Header database={database} />
|
||||
return (
|
||||
<Header
|
||||
database={database}
|
||||
onStartDelete={onStartDelete}
|
||||
onDatabaseDeleteConfirm={onDatabaseDeleteConfirm}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
const Header = ({database}) => (
|
||||
<div className="db-manager-header">
|
||||
<h4>{database.name}</h4>
|
||||
DatabaseTableHeader.propTypes = {
|
||||
onEdit: func,
|
||||
database: shape(),
|
||||
onKeyDown: func,
|
||||
onCancel: func,
|
||||
onConfirm: func,
|
||||
onStartDelete: func,
|
||||
onDatabaseDeleteConfirm: func,
|
||||
}
|
||||
|
||||
const Header = ({
|
||||
database,
|
||||
onStartDelete,
|
||||
onDatabaseDeleteConfirm,
|
||||
}) => {
|
||||
const confirmStyle = {
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}
|
||||
|
||||
const buttons = (
|
||||
<div className="text-right">
|
||||
<button className="btn btn-xs btn-danger">
|
||||
<button className="btn btn-xs btn-danger" onClick={() => onStartDelete(database)}>
|
||||
Delete
|
||||
</button>
|
||||
<button className="btn btn-xs btn-primary">
|
||||
{`Add retention policy`}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
|
||||
const deleteConfirm = (
|
||||
<div style={confirmStyle}>
|
||||
<div className="admin-table--delete-cell">
|
||||
<input
|
||||
className="form-control"
|
||||
name="name"
|
||||
type="text"
|
||||
value={database.deleteCode || ''}
|
||||
placeholder="type DELETE to confirm"
|
||||
onChange={(e) => onDatabaseDeleteConfirm(database, e)}
|
||||
onKeyDown={(e) => onDatabaseDeleteConfirm(database, e)}
|
||||
autoFocus={true}
|
||||
/>
|
||||
</div>
|
||||
<ConfirmButtons item={database} onConfirm={() => {}} onCancel={() => {}} />
|
||||
</div>
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="db-manager-header">
|
||||
<h4>{database.name}</h4>
|
||||
{database.hasOwnProperty('deleteCode') ? deleteConfirm : buttons}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
database: shape(),
|
||||
onStartDelete: func,
|
||||
onDatabaseDeleteConfirm: func,
|
||||
}
|
||||
|
||||
const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => (
|
||||
<div className="db-manager-header">
|
||||
|
@ -100,25 +185,6 @@ const EditHeader = ({database, onEdit, onKeyDown, onConfirm, onCancel}) => (
|
|||
</div>
|
||||
)
|
||||
|
||||
const {
|
||||
arrayOf,
|
||||
func,
|
||||
shape,
|
||||
} = PropTypes
|
||||
|
||||
DatabaseTable.propTypes = {
|
||||
onEditDatabase: func,
|
||||
database: shape(),
|
||||
retentionPolicies: arrayOf(shape()),
|
||||
onKeyDownDatabase: func,
|
||||
onCancelDatabase: func,
|
||||
onConfirmDatabase: func,
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
database: shape(),
|
||||
}
|
||||
|
||||
EditHeader.propTypes = {
|
||||
database: shape(),
|
||||
onEdit: func,
|
||||
|
@ -127,12 +193,4 @@ EditHeader.propTypes = {
|
|||
onConfirm: func,
|
||||
}
|
||||
|
||||
DatabaseTableHeader.propTypes = {
|
||||
onEdit: func,
|
||||
database: shape(),
|
||||
onKeyDown: func,
|
||||
onCancel: func,
|
||||
onConfirm: func,
|
||||
}
|
||||
|
||||
export default DatabaseTable
|
||||
|
|
|
@ -9,6 +9,7 @@ class DatabaseManagerPage extends Component {
|
|||
constructor(props) {
|
||||
super(props)
|
||||
this.handleKeyDownDatabase = ::this.handleKeyDownDatabase
|
||||
this.handleDatabaseDeleteConfirm = ::this.handleDatabaseDeleteConfirm
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
|
@ -30,18 +31,35 @@ class DatabaseManagerPage extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
handleDatabaseDeleteConfirm(database, e) {
|
||||
const {key, target: {value}} = e
|
||||
const {actions, source} = this.props
|
||||
|
||||
if (key === 'Escape') {
|
||||
return actions.removeDatabaseDeleteCode(database)
|
||||
}
|
||||
|
||||
if (key === 'Enter' && database.deleteCode === 'DELETE') {
|
||||
return actions.deleteDatabaseAsync(source, database)
|
||||
}
|
||||
|
||||
actions.updateDatabaseDeleteCode(database, value)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {databases, retentionPolicies, actions} = this.props
|
||||
|
||||
return (
|
||||
<DatabaseManager
|
||||
addDatabase={actions.addDatabase}
|
||||
databases={databases}
|
||||
retentionPolicies={retentionPolicies}
|
||||
onKeyDownDatabase={this.handleKeyDownDatabase}
|
||||
onDatabaseDeleteConfirm={this.handleDatabaseDeleteConfirm}
|
||||
addDatabase={actions.addDatabase}
|
||||
onEditDatabase={actions.editDatabase}
|
||||
onCancelDatabase={actions.removeDatabase}
|
||||
onConfirmDatabase={actions.createDatabaseAsync}
|
||||
onStartDeleteDatabase={actions.startDeleteDatabase}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
@ -77,6 +95,9 @@ DatabaseManagerPage.propTypes = {
|
|||
createDatabaseAsync: func,
|
||||
addDatabase: func,
|
||||
removeDatabase: func,
|
||||
startDeleteDatabase: func,
|
||||
updateDatabaseDeleteCode: func,
|
||||
removeDatabaseDeleteCode: func,
|
||||
}),
|
||||
}
|
||||
|
||||
|
|
|
@ -178,6 +178,35 @@ export default function admin(state = initialState, action) {
|
|||
return {...state, ...newState}
|
||||
}
|
||||
|
||||
case 'START_DELETE_DATABASE': {
|
||||
const {database} = action.payload
|
||||
const newState = {
|
||||
databases: state.databases.map(db => db.id === database.id ? {...db, deleteCode: ''} : db),
|
||||
}
|
||||
|
||||
return {...state, ...newState}
|
||||
}
|
||||
|
||||
case 'UPDATE_DATABASE_DELETE_CODE': {
|
||||
const {database, deleteCode} = action.payload
|
||||
const newState = {
|
||||
databases: state.databases.map(db => db.id === database.id ? {...db, deleteCode} : db),
|
||||
}
|
||||
|
||||
return {...state, ...newState}
|
||||
}
|
||||
|
||||
case 'REMOVE_DATABASE_DELETE_CODE': {
|
||||
const {database} = action.payload
|
||||
delete database.deleteCode
|
||||
|
||||
const newState = {
|
||||
databases: state.databases.map(db => db.id === database.id ? {...database} : db),
|
||||
}
|
||||
|
||||
return {...state, ...newState}
|
||||
}
|
||||
|
||||
case 'LOAD_QUERIES': {
|
||||
return {...state, ...action.payload}
|
||||
}
|
||||
|
|
|
@ -121,6 +121,23 @@
|
|||
}
|
||||
}
|
||||
|
||||
.admin-table--delete-cell {
|
||||
margin: 0 !important;
|
||||
display: flex !important;
|
||||
justify-content: space-between;
|
||||
|
||||
> input {
|
||||
height: 30px;
|
||||
padding: 0 9px;
|
||||
flex-grow: 1;
|
||||
margin: 0 2px;
|
||||
min-width: 110px;
|
||||
|
||||
&:first-child {margin-left: 0;}
|
||||
&:last-child {margin-right: 0;}
|
||||
}
|
||||
}
|
||||
|
||||
.db-manager-header {
|
||||
height: 32px;
|
||||
display: flex;
|
||||
|
|
Loading…
Reference in New Issue