chore(xo-web/VM): move boot order setting from Disks to Advanced (#4975)

See #1523
This commit is contained in:
Pierre Donias
2020-05-07 09:37:00 +02:00
committed by GitHub
parent a05191e112
commit 6555e2c440
4 changed files with 181 additions and 181 deletions

View File

@@ -7,6 +7,8 @@
> Users must be able to say: “Nice enhancement, I'm eager to test it”
- [VM] Move boot order setting from Disk tab to Advanced tab [#1523](https://github.com/vatesfr/xen-orchestra/issues/1523#issuecomment-563141573) (PR [#4975](https://github.com/vatesfr/xen-orchestra/pull/4975))
### Bug fixes
> Users must be able to say: “I had this issue, happy to know it's fixed”

View File

@@ -0,0 +1,168 @@
import _ from 'intl'
import ActionButton from 'action-button'
import Component from 'base-component'
import HTML5Backend from 'react-dnd-html5-backend'
import Icon from 'icon'
import PropTypes from 'prop-types'
import React from 'react'
import { DragDropContext, DragSource, DropTarget } from 'react-dnd'
import { Toggle } from 'form'
import { forEach, map } from 'lodash'
import { setVmBootOrder } from 'xo'
const parseBootOrder = bootOrder => {
// FIXME missing translation
const bootOptions = {
c: 'Hard-Drive',
d: 'DVD-Drive',
n: 'Network',
}
const order = []
if (bootOrder) {
for (const id of bootOrder) {
if (id in bootOptions) {
order.push({ id, text: bootOptions[id], active: true })
delete bootOptions[id]
}
}
}
forEach(bootOptions, (text, id) => {
order.push({ id, text, active: false })
})
return order
}
const orderItemSource = {
beginDrag: props => ({
id: props.id,
index: props.index,
}),
}
const orderItemTarget = {
hover: (props, monitor, component) => {
const dragIndex = monitor.getItem().index
const hoverIndex = props.index
if (dragIndex === hoverIndex) {
return
}
props.move(dragIndex, hoverIndex)
monitor.getItem().index = hoverIndex
},
}
const GRAB_STYLE = { cursor: 'grab' }
@DropTarget('orderItem', orderItemTarget, connect => ({
connectDropTarget: connect.dropTarget(),
}))
@DragSource('orderItem', orderItemSource, (connect, monitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
}))
class OrderItem extends Component {
static propTypes = {
connectDragSource: PropTypes.func.isRequired,
connectDropTarget: PropTypes.func.isRequired,
index: PropTypes.number.isRequired,
isDragging: PropTypes.bool.isRequired,
id: PropTypes.any.isRequired,
item: PropTypes.object.isRequired,
move: PropTypes.func.isRequired,
}
_toggle = checked => {
const { item } = this.props
item.active = checked
this.forceUpdate()
}
render() {
const { item, connectDragSource, connectDropTarget } = this.props
return connectDragSource(
connectDropTarget(
<li className='list-group-item'>
<span className='mr-1' style={GRAB_STYLE}>
<Icon icon='grab' /> <Icon icon='grab' />
</span>
{item.text}
<span className='pull-right'>
<Toggle value={item.active} onChange={this._toggle} />
</span>
</li>
)
)
}
}
@DragDropContext(HTML5Backend)
export default class BootOrder extends Component {
static propTypes = {
onClose: PropTypes.func,
vm: PropTypes.object.isRequired,
}
constructor(props) {
super(props)
const { vm } = props
const order = parseBootOrder(vm.boot && vm.boot.order)
this.state = { order }
}
_moveOrderItem = (dragIndex, hoverIndex) => {
const order = this.state.order.slice()
const dragItem = order.splice(dragIndex, 1)
if (dragItem.length) {
order.splice(hoverIndex, 0, dragItem.pop())
this.setState({ order })
}
}
_reset = () => {
const { vm } = this.props
const order = parseBootOrder(vm.boot && vm.boot.order)
this.setState({ order })
}
_save = () => {
const { vm } = this.props
const { order: newOrder } = this.state
let order = ''
forEach(newOrder, item => {
item.active && (order += item.id)
})
return setVmBootOrder(vm, order)
}
render() {
const { order } = this.state
return (
<form>
<ul className='pl-0'>
{map(order, (item, index) => (
<OrderItem
key={index}
index={index}
id={item.id}
// FIXME missing translation
item={item}
move={this._moveOrderItem}
/>
))}
</ul>
<fieldset className='form-inline'>
<span className='pull-right'>
<ActionButton icon='save' btnStyle='primary' handler={this._save}>
{_('saveBootOption')}
</ActionButton>{' '}
<ActionButton icon='reset' handler={this._reset}>
{_('resetBootOption')}
</ActionButton>
</span>
</fieldset>
</form>
)
}
}

View File

@@ -83,6 +83,8 @@ import {
isAdmin,
} from 'selectors'
import BootOrder from './boot-order'
// Button's height = react-select's height(36 px) + react-select's border-width(1 px) * 2
// https://github.com/JedWatson/react-select/blob/916ab0e62fc7394be8e24f22251c399a68de8b1c/less/select.less#L21, L22
const SHARE_BUTTON_STYLE = { height: '38px' }
@@ -629,6 +631,14 @@ export default class TabAdvanced extends Component {
/>
</Col>
</Row>
{vm.virtualizationMode !== 'pv' && (
<Row>
<Col>
<h3>{_('vdiBootOrder')}</h3>
<BootOrder vm={vm} />
</Col>
</Row>
)}
<Row>
<Col>
<h3>{_('xenSettingsLabel')}</h3>

View File

@@ -3,7 +3,6 @@ import ActionButton from 'action-button'
import Component from 'base-component'
import copy from 'copy-to-clipboard'
import defined from '@xen-orchestra/defined'
import HTML5Backend from 'react-dnd-html5-backend'
import Icon from 'icon'
import IsoDevice from 'iso-device'
import MigrateVdiModalBody from 'xo/migrate-vdi-modal'
@@ -22,7 +21,6 @@ import {
getCheckPermissions,
isAdmin,
} from 'selectors'
import { DragDropContext, DragSource, DropTarget } from 'react-dnd'
import { injectIntl } from 'react-intl'
import {
addSubscriptions,
@@ -65,7 +63,6 @@ import {
isVmRunning,
migrateVdi,
setBootableVbd,
setVmBootOrder,
subscribeResourceSets,
} from 'xo'
@@ -202,28 +199,6 @@ const INDIVIDUAL_ACTIONS = [
},
]
const parseBootOrder = bootOrder => {
// FIXME missing translation
const bootOptions = {
c: 'Hard-Drive',
d: 'DVD-Drive',
n: 'Network',
}
const order = []
if (bootOrder) {
for (const id of bootOrder) {
if (id in bootOptions) {
order.push({ id, text: bootOptions[id], active: true })
delete bootOptions[id]
}
}
}
forEach(bootOptions, (text, id) => {
order.push({ id, text, active: false })
})
return order
}
@injectIntl
@addSubscriptions({
resourceSets: subscribeResourceSets,
@@ -494,137 +469,6 @@ class AttachDisk extends Component {
}
}
const orderItemSource = {
beginDrag: props => ({
id: props.id,
index: props.index,
}),
}
const orderItemTarget = {
hover: (props, monitor, component) => {
const dragIndex = monitor.getItem().index
const hoverIndex = props.index
if (dragIndex === hoverIndex) {
return
}
props.move(dragIndex, hoverIndex)
monitor.getItem().index = hoverIndex
},
}
@DropTarget('orderItem', orderItemTarget, connect => ({
connectDropTarget: connect.dropTarget(),
}))
@DragSource('orderItem', orderItemSource, (connect, monitor) => ({
connectDragSource: connect.dragSource(),
isDragging: monitor.isDragging(),
}))
class OrderItem extends Component {
static propTypes = {
connectDragSource: PropTypes.func.isRequired,
connectDropTarget: PropTypes.func.isRequired,
index: PropTypes.number.isRequired,
isDragging: PropTypes.bool.isRequired,
id: PropTypes.any.isRequired,
item: PropTypes.object.isRequired,
move: PropTypes.func.isRequired,
}
_toggle = checked => {
const { item } = this.props
item.active = checked
this.forceUpdate()
}
render() {
const { item, connectDragSource, connectDropTarget } = this.props
return connectDragSource(
connectDropTarget(
<li className='list-group-item'>
<Icon icon='grab' /> <Icon icon='grab' /> {item.text}
<span className='pull-right'>
<Toggle value={item.active} onChange={this._toggle} />
</span>
</li>
)
)
}
}
@DragDropContext(HTML5Backend)
class BootOrder extends Component {
static propTypes = {
onClose: PropTypes.func,
vm: PropTypes.object.isRequired,
}
constructor(props) {
super(props)
const { vm } = props
const order = parseBootOrder(vm.boot && vm.boot.order)
this.state = { order }
}
_moveOrderItem = (dragIndex, hoverIndex) => {
const order = this.state.order.slice()
const dragItem = order.splice(dragIndex, 1)
if (dragItem.length) {
order.splice(hoverIndex, 0, dragItem.pop())
this.setState({ order })
}
}
_reset = () => {
const { vm } = this.props
const order = parseBootOrder(vm.boot && vm.boot.order)
this.setState({ order })
}
_save = () => {
const { vm, onClose = noop } = this.props
const { order: newOrder } = this.state
let order = ''
forEach(newOrder, item => {
item.active && (order += item.id)
})
return setVmBootOrder(vm, order).then(onClose)
}
render() {
const { order } = this.state
return (
<form>
<ul>
{map(order, (item, index) => (
<OrderItem
key={index}
index={index}
id={item.id}
// FIXME missing translation
item={item}
move={this._moveOrderItem}
/>
))}
</ul>
<fieldset className='form-inline'>
<span className='pull-right'>
<ActionButton icon='save' btnStyle='primary' handler={this._save}>
{_('saveBootOption')}
</ActionButton>{' '}
<ActionButton icon='reset' handler={this._reset}>
{_('resetBootOption')}
</ActionButton>
</span>
</fieldset>
</form>
)
}
}
@connectStore(() => ({
checkPermissions: getCheckPermissions,
isAdmin,
@@ -635,7 +479,6 @@ export default class TabDisks extends Component {
super(props)
this.state = {
attachDisk: false,
bootOrder: false,
newDisk: false,
}
}
@@ -670,20 +513,11 @@ export default class TabDisks extends Component {
this.setState({
newDisk: !this.state.newDisk,
attachDisk: false,
bootOrder: false,
})
_toggleAttachDisk = () =>
this.setState({
attachDisk: !this.state.attachDisk,
bootOrder: false,
newDisk: false,
})
_toggleBootOrder = () =>
this.setState({
bootOrder: !this.state.bootOrder,
attachDisk: false,
newDisk: false,
})
@@ -806,7 +640,7 @@ export default class TabDisks extends Component {
render() {
const { allVbds, vm } = this.props
const { attachDisk, bootOrder, newDisk } = this.state
const { attachDisk, newDisk } = this.state
return (
<Container>
@@ -826,14 +660,6 @@ export default class TabDisks extends Component {
labelId='vdiAttachDevice'
/>
)}
{vm.virtualizationMode !== 'pv' && (
<TabButton
btnStyle={bootOrder ? 'info' : 'primary'}
handler={this._toggleBootOrder}
icon='sort'
labelId='vdiBootOrder'
/>
)}
</Col>
</Row>
<Row>
@@ -859,12 +685,6 @@ export default class TabDisks extends Component {
<hr />
</div>
)}
{bootOrder && (
<div>
<BootOrder vm={vm} onClose={this._toggleBootOrder} />
<hr />
</div>
)}
</Col>
</Row>
<Row>