parent
0d718bd632
commit
664d648435
@ -1,8 +1,7 @@
|
||||
import Component from 'base-component'
|
||||
import forEach from 'lodash/forEach'
|
||||
import React from 'react'
|
||||
import remove from 'lodash/remove'
|
||||
import { Shortcuts as ReactShortcuts } from 'react-shortcuts'
|
||||
import { forEach, remove } from 'lodash'
|
||||
|
||||
let enabled = true
|
||||
const instances = []
|
||||
@ -29,7 +28,20 @@ export default class Shortcuts extends Component {
|
||||
remove(instances, this)
|
||||
}
|
||||
|
||||
_handler = (command, event) => {
|
||||
// When an input is focused, shortcuts are disabled by default *except* for
|
||||
// non-printable keys (Esc, Enter, ...) but we want to disable them as well
|
||||
// https://github.com/avocode/react-shortcuts/issues/13#issuecomment-255868423
|
||||
if (event.target.tagName === 'INPUT') {
|
||||
return
|
||||
}
|
||||
|
||||
this.props.handler(command, event)
|
||||
}
|
||||
|
||||
render () {
|
||||
return enabled ? <ReactShortcuts {...this.props} /> : null
|
||||
return enabled ? (
|
||||
<ReactShortcuts {...this.props} handler={this._handler} />
|
||||
) : null
|
||||
}
|
||||
}
|
||||
|
@ -830,8 +830,8 @@ export default class SortedTable extends Component {
|
||||
{shortcutsTarget !== undefined && (
|
||||
<Shortcuts
|
||||
handler={this._getShortcutsHandler()}
|
||||
isolate
|
||||
name='SortedTable'
|
||||
stopPropagation
|
||||
targetNodeSelector={shortcutsTarget}
|
||||
/>
|
||||
)}
|
||||
|
@ -912,10 +912,10 @@ export default class Home extends Component {
|
||||
return (
|
||||
<Page header={this._renderHeader()}>
|
||||
<Shortcuts
|
||||
name='Home'
|
||||
handler={this._getShortcutsHandler()}
|
||||
isolate
|
||||
name='Home'
|
||||
targetNodeSelector='body'
|
||||
stopPropagation={false}
|
||||
/>
|
||||
<div>
|
||||
<div className={styles.itemContainer}>
|
||||
|
@ -1,6 +1,5 @@
|
||||
import _, { messages } from 'intl'
|
||||
import ActionButton from 'action-button'
|
||||
import ActionRowButton from 'action-row-button'
|
||||
import Component from 'base-component'
|
||||
import HTML5Backend from 'react-dnd-html5-backend'
|
||||
import Icon from 'icon'
|
||||
@ -10,8 +9,8 @@ import propTypes from 'prop-types-decorator'
|
||||
import React from 'react'
|
||||
import SingleLineRow from 'single-line-row'
|
||||
import StateButton from 'state-button'
|
||||
import SortedTable from 'sorted-table'
|
||||
import TabButton from 'tab-button'
|
||||
import Tooltip from 'tooltip'
|
||||
import { Container, Row, Col } from 'grid'
|
||||
import {
|
||||
createSelector,
|
||||
@ -33,13 +32,15 @@ import { SizeInput, Toggle } from 'form'
|
||||
import { XoSelect, Size, Text } from 'editable'
|
||||
import { confirm } from 'modal'
|
||||
import { error } from 'notification'
|
||||
import { forEach, get, isEmpty, map, some } from 'lodash'
|
||||
import { filter, find, forEach, get, map, mapValues, some } from 'lodash'
|
||||
import {
|
||||
attachDiskToVm,
|
||||
createDisk,
|
||||
connectVbd,
|
||||
deleteVbd,
|
||||
deleteVbds,
|
||||
deleteVdi,
|
||||
deleteVdis,
|
||||
disconnectVbd,
|
||||
editVdi,
|
||||
isSrWritable,
|
||||
@ -50,6 +51,132 @@ import {
|
||||
subscribeResourceSets,
|
||||
} from 'xo'
|
||||
|
||||
const COLUMNS_VM_PV = [
|
||||
{
|
||||
itemRenderer: vdi => (
|
||||
<Text
|
||||
value={vdi.name_label}
|
||||
onChange={value => editVdi(vdi, { name_label: value })}
|
||||
/>
|
||||
),
|
||||
name: _('vdiNameLabel'),
|
||||
sortCriteria: 'name_label',
|
||||
default: true,
|
||||
},
|
||||
{
|
||||
itemRenderer: vdi => (
|
||||
<Text
|
||||
value={vdi.name_description}
|
||||
onChange={value => editVdi(vdi, { name_description: value })}
|
||||
/>
|
||||
),
|
||||
name: _('vdiNameDescription'),
|
||||
sortCriteria: 'name_description',
|
||||
},
|
||||
{
|
||||
itemRenderer: vdi => (
|
||||
<Size
|
||||
value={vdi.size || null}
|
||||
onChange={size => editVdi(vdi, { size })}
|
||||
/>
|
||||
),
|
||||
name: _('vdiSize'),
|
||||
sortCriteria: 'size',
|
||||
},
|
||||
{
|
||||
itemRenderer: (vdi, userData) => {
|
||||
const sr = userData.srs[vdi.$SR]
|
||||
return (
|
||||
sr !== undefined && (
|
||||
<XoSelect
|
||||
labelProp='name_label'
|
||||
onChange={sr => migrateVdi(vdi, sr)}
|
||||
predicate={sr => sr.$pool === userData.vm.$pool && isSrWritable(sr)}
|
||||
useLongClick
|
||||
value={sr}
|
||||
xoType='SR'
|
||||
>
|
||||
<Link to={`/srs/${sr.id}`}>{sr.name_label}</Link>
|
||||
</XoSelect>
|
||||
)
|
||||
)
|
||||
},
|
||||
name: _('vdiSr'),
|
||||
sortCriteria: (vdi, userData) => {
|
||||
const sr = userData.srs[vdi.$SR]
|
||||
return sr !== undefined && sr.name_label
|
||||
},
|
||||
},
|
||||
{
|
||||
itemRenderer: (vdi, userData) => {
|
||||
const vbd = userData.vbdsByVdi[vdi.id]
|
||||
return (
|
||||
<Toggle
|
||||
onChange={bootable => setBootableVbd(vbd, bootable)}
|
||||
value={vbd.bootable}
|
||||
/>
|
||||
)
|
||||
},
|
||||
name: _('vbdBootableStatus'),
|
||||
id: 'vbdBootableStatus',
|
||||
},
|
||||
{
|
||||
itemRenderer: (vdi, userData) => {
|
||||
const vbd = userData.vbdsByVdi[vdi.id]
|
||||
return (
|
||||
<StateButton
|
||||
disabledLabel={_('vbdStatusDisconnected')}
|
||||
disabledHandler={connectVbd}
|
||||
disabledTooltip={_('vbdConnect')}
|
||||
enabledLabel={_('vbdStatusConnected')}
|
||||
enabledHandler={disconnectVbd}
|
||||
enabledTooltip={_('vbdDisconnect')}
|
||||
disabled={!(vbd.attached || isVmRunning(userData.vm))}
|
||||
handlerParam={vbd}
|
||||
state={vbd.attached}
|
||||
/>
|
||||
)
|
||||
},
|
||||
name: _('vbdStatus'),
|
||||
},
|
||||
]
|
||||
|
||||
const COLUMNS = filter(COLUMNS_VM_PV, col => col.id !== 'vbdBootableStatus')
|
||||
|
||||
const ACTIONS = [
|
||||
{
|
||||
disabled: (selectedItems, userData) =>
|
||||
some(map(selectedItems, vdi => userData.vbdsByVdi[vdi.id]), 'attached'),
|
||||
handler: (selectedItems, userData) =>
|
||||
deleteVbds(map(selectedItems, vdi => userData.vbdsByVdi[vdi.id])),
|
||||
individualDisabled: (vdi, userData) => {
|
||||
const vbd = userData.vbdsByVdi[vdi.id]
|
||||
return vbd !== undefined && vbd.attached
|
||||
},
|
||||
individualHandler: (vdi, userData) => {
|
||||
const vbd = userData.vbdsByVdi[vdi.id]
|
||||
return vbd !== undefined && deleteVbd(vbd)
|
||||
},
|
||||
icon: 'vdi-forget',
|
||||
label: _('vdiForget'),
|
||||
level: 'danger',
|
||||
},
|
||||
{
|
||||
disabled: (selectedItems, userData) =>
|
||||
some(map(selectedItems, vdi => userData.vbdsByVdi[vdi.id]), 'attached'),
|
||||
handler: deleteVdis,
|
||||
individualDisabled: (vdi, userData) => {
|
||||
const vbd = userData.vbdsByVdi[vdi.id]
|
||||
return vbd !== undefined && vbd.attached
|
||||
},
|
||||
individualHandler: deleteVdi,
|
||||
individualLabel: _('vdiRemove'),
|
||||
icon: 'vdi-remove',
|
||||
label: _('deleteSelectedVdis'),
|
||||
level: 'danger',
|
||||
},
|
||||
]
|
||||
|
||||
const parseBootOrder = bootOrder => {
|
||||
// FIXME missing translation
|
||||
const bootOptions = {
|
||||
@ -516,6 +643,21 @@ export default class TabDisks extends Component {
|
||||
isAdmin || (resourceSet == null && isVmAdmin)
|
||||
)
|
||||
|
||||
_getVbdsByVdi = createSelector(
|
||||
() => this.props.vdis,
|
||||
() => this.props.vbds,
|
||||
() => this.props.vm,
|
||||
(vdis, vbds, vm) => mapValues(vdis, vdi => find(vbds, { VDI: vdi.id }))
|
||||
)
|
||||
|
||||
individualActions = [
|
||||
{
|
||||
handler: this._migrateVdi,
|
||||
icon: 'vdi-migrate',
|
||||
label: _('vdiMigrate'),
|
||||
},
|
||||
]
|
||||
|
||||
render () {
|
||||
const { srs, vbds, vdis, vm } = this.props
|
||||
|
||||
@ -577,129 +719,17 @@ export default class TabDisks extends Component {
|
||||
</Row>
|
||||
<Row>
|
||||
<Col>
|
||||
{!isEmpty(vbds) ? (
|
||||
<table className='table'>
|
||||
<thead className='thead-default'>
|
||||
<tr>
|
||||
<th>{_('vdiNameLabel')}</th>
|
||||
<th>{_('vdiNameDescription')}</th>
|
||||
<th>{_('vdiSize')}</th>
|
||||
<th>{_('vdiSr')}</th>
|
||||
{vm.virtualizationMode === 'pv' && (
|
||||
<th>{_('vbdBootableStatus')}</th>
|
||||
)}
|
||||
<th>{_('vbdStatus')}</th>
|
||||
<th className='text-xs-right'>{_('vbdAction')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{map(vbds, vbd => {
|
||||
const vdi = vdis[vbd.VDI]
|
||||
if (vbd.is_cd_drive || !vdi) {
|
||||
return
|
||||
}
|
||||
|
||||
const sr = srs[vdi.$SR]
|
||||
|
||||
return (
|
||||
<tr key={vbd.id}>
|
||||
<td>
|
||||
<Text
|
||||
value={vdi.name_label}
|
||||
onChange={value =>
|
||||
editVdi(vdi, { name_label: value })
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<Text
|
||||
value={vdi.name_description}
|
||||
onChange={value =>
|
||||
editVdi(vdi, { name_description: value })
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<Size
|
||||
value={vdi.size || null}
|
||||
onChange={size => editVdi(vdi, { size })}
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
{' '}
|
||||
{sr && (
|
||||
<XoSelect
|
||||
onChange={sr => migrateVdi(vdi, sr)}
|
||||
xoType='SR'
|
||||
predicate={sr =>
|
||||
sr.$pool === vm.$pool && isSrWritable(sr)
|
||||
}
|
||||
labelProp='name_label'
|
||||
value={sr}
|
||||
useLongClick
|
||||
>
|
||||
<Link to={`/srs/${sr.id}`}>{sr.name_label}</Link>
|
||||
</XoSelect>
|
||||
)}
|
||||
</td>
|
||||
{vm.virtualizationMode === 'pv' && (
|
||||
<td>
|
||||
<Toggle
|
||||
value={vbd.bootable}
|
||||
onChange={bootable =>
|
||||
setBootableVbd(vbd, bootable)
|
||||
}
|
||||
/>
|
||||
</td>
|
||||
)}
|
||||
<td>
|
||||
<StateButton
|
||||
disabledLabel={_('vbdStatusDisconnected')}
|
||||
disabledHandler={connectVbd}
|
||||
disabledTooltip={_('vbdConnect')}
|
||||
enabledLabel={_('vbdStatusConnected')}
|
||||
enabledHandler={disconnectVbd}
|
||||
enabledTooltip={_('vbdDisconnect')}
|
||||
disabled={!(vbd.attached || isVmRunning(vm))}
|
||||
handlerParam={vbd}
|
||||
state={vbd.attached}
|
||||
/>
|
||||
</td>
|
||||
<td className='text-xs-right'>
|
||||
<Tooltip content={_('vdiMigrate')}>
|
||||
<ActionRowButton
|
||||
icon='vdi-migrate'
|
||||
handler={this._migrateVdi}
|
||||
handlerParam={vdi}
|
||||
/>
|
||||
</Tooltip>
|
||||
{!vbd.attached && (
|
||||
<span>
|
||||
<Tooltip content={_('vdiForget')}>
|
||||
<ActionRowButton
|
||||
icon='vdi-forget'
|
||||
handler={deleteVbd}
|
||||
handlerParam={vbd}
|
||||
/>
|
||||
</Tooltip>
|
||||
<Tooltip content={_('vdiRemove')}>
|
||||
<ActionRowButton
|
||||
icon='vdi-remove'
|
||||
handler={deleteVdi}
|
||||
handlerParam={vdi}
|
||||
/>
|
||||
</Tooltip>
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<h4 className='text-xs-center'>{_('vbdNoVbd')}</h4>
|
||||
)}
|
||||
<SortedTable
|
||||
actions={ACTIONS}
|
||||
collection={vdis}
|
||||
columns={vm.virtualizationMode === 'pv' ? COLUMNS_VM_PV : COLUMNS}
|
||||
data-srs={srs}
|
||||
data-vbdsByVdi={this._getVbdsByVdi()}
|
||||
data-vm={vm}
|
||||
individualActions={this.individualActions}
|
||||
shortcutsTarget='body'
|
||||
stateUrlParam='s'
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
|
Loading…
Reference in New Issue
Block a user