/*
Copyright (C) 2017 Cloudbase Solutions SRL
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
import * as React from 'react'
import { observer } from 'mobx-react'
import styled from 'styled-components'
import Button from '../../../ui/Button/Button'
import FieldInput from '../../../ui/FieldInput/FieldInput'
import ToggleButtonBar from '../../../ui/ToggleButtonBar/ToggleButtonBar'
import type { Field } from '../../../../@types/Field'
import { ThemeProps } from '../../../Theme'
import LabelDictionary from '../../../../utils/LabelDictionary'
import assessmentImage from './images/assessment.svg'
const Wrapper = styled.div`
padding: 48px 32px 32px 32px;
display: flex;
flex-direction: column;
align-items: center;
min-height: 0;
`
const Image = styled.div`
width: 96px;
height: 96px;
background: url('${assessmentImage}') center no-repeat;
`
const ToggleButtonBarStyled = styled(ToggleButtonBar)`
margin-top: 48px;
`
const Fields = styled.div`
display: flex;
margin-top: 32px;
flex-direction: column;
overflow: auto;
width: 100%;
min-height: 0;
`
const FieldStyled = styled(FieldInput)`
${ThemeProps.exactWidth(`${ThemeProps.inputSizes.large.width}px`)}
margin-bottom: 16px;
`
const Row = styled.div`
display: flex;
flex-shrink: 0;
justify-content: space-between;
`
const Buttons = styled.div`
display: flex;
justify-content: space-between;
width: 100%;
margin-top: 32px;
`
const generalFields = [
{
name: 'use_replica',
type: 'boolean',
},
{
name: 'separate_vm',
type: 'boolean',
},
]
const replicaFields = [
{
name: 'shutdown_instances',
type: 'boolean',
},
]
const migrationFields = [
{
name: 'skip_os_morphing',
type: 'boolean',
},
]
type Props = {
onCancelClick: () => void,
onExecuteClick: (fieldValues: { [prop: string]: any }) => void,
executeButtonDisabled: boolean,
replicaSchema: Field[],
migrationSchema: Field[],
onResizeUpdate?: (scrollableRef: HTMLElement, scrollOffset?: number) => void,
}
type State = {
fieldValues: { [prop: string]: any },
showAdvancedOptions: boolean,
}
@observer
class AssessmentMigrationOptions extends React.Component {
state: State = {
fieldValues: {
separate_vm: true,
use_replica: false,
shutdown_instances: false,
skip_os_morphing: false,
},
showAdvancedOptions: false,
}
scrollableRef: HTMLElement | undefined | null
getFieldValue(fieldName: string) {
if (this.state.fieldValues[fieldName] != null) {
return this.state.fieldValues[fieldName]
}
return null
}
getObjectFieldValue(fieldName: string, propName: string) {
return this.state.fieldValues[fieldName] && this.state.fieldValues[fieldName][propName]
}
handleValueChange(fieldName: string, value: any) {
this.setState(prevState => {
const fieldValues = { ...prevState.fieldValues }
if (value != null) {
fieldValues[fieldName] = value
} else {
delete fieldValues[fieldName]
}
return { fieldValues }
})
}
UNSAFE_componentDidUpdate(_: Props, prevState: State) {
if (prevState.showAdvancedOptions !== this.state.showAdvancedOptions
&& this.props.onResizeUpdate && this.scrollableRef) {
this.props.onResizeUpdate(this.scrollableRef)
}
}
handleObjectValueChange(fieldName: string, propName: string, value: any) {
this.setState(prevState => {
const fieldValues = { ...prevState.fieldValues }
if (!fieldValues[fieldName]) {
fieldValues[fieldName] = {}
}
fieldValues[fieldName][propName] = value
return { fieldValues }
})
}
renderFields() {
let fields: any = generalFields
const useReplica = this.getFieldValue('use_replica')
const skipFields = ['location', 'resource_group', 'network_map', 'storage_map', 'vm_size', 'worker_size']
// eslint-disable-next-line no-shadow
const cleanup = (cleanupFields: any[]) => cleanupFields.filter((f: {
name: string
}) => !skipFields
.find(n => n === f.name)).map((f: { type: string; nullableBoolean: boolean }) => {
if (f.type === 'boolean') {
// eslint-disable-next-line no-param-reassign
f.nullableBoolean = true
}
return { ...f }
})
if (useReplica) {
fields = [...fields, ...replicaFields]
if (this.state.showAdvancedOptions) {
fields = [
...fields,
...cleanup(this.props.replicaSchema),
]
}
} else {
fields = [...fields, ...migrationFields]
if (this.state.showAdvancedOptions) {
fields = [
...fields,
...cleanup(this.props.migrationSchema),
]
}
}
const sortPriority: any = {
boolean: 1,
string: 2,
object: 3,
}
fields.sort((a: any, b: any) => {
if (sortPriority[a.type] && sortPriority[b.type]) {
return sortPriority[a.type] - sortPriority[b.type]
}
if (sortPriority[a.type]) {
return -1
}
if (sortPriority[b.type]) {
return 1
}
return a.name.localeCompare(b.name)
})
const rows: JSX.Element[] = []
let lastField: JSX.Element
fields.forEach((field: any, index: number) => {
let additionalProps
if (field.type === 'object' && field.properties) {
additionalProps = {
valueCallback: (
callbackField: { name: string },
) => this.getObjectFieldValue(field.name, callbackField.name),
onChange: (value: any, callbackField: { name: string }) => {
const propName = callbackField.name.substr(callbackField.name.lastIndexOf('/') + 1)
this.handleObjectValueChange(field.name, propName, value)
},
properties: field.properties.map((p: any) => ({ ...p, required: false })),
}
} else {
const value = this.getFieldValue(field.name)
additionalProps = {
value,
// eslint-disable-next-line no-shadow
onChange: (changeValue: any) => { this.handleValueChange(field.name, changeValue) },
type: field.type,
}
}
const currentField = (
)
const pushRow = (field1: React.ReactNode, field2?: React.ReactNode) => {
rows.push((
{field1}
{field2}
))
}
if (index === fields.length - 1 && index % 2 === 0) {
pushRow(currentField)
} else if (index % 2 !== 0) {
pushRow(lastField, currentField)
} else {
lastField = currentField
}
})
return (
{ this.scrollableRef = ref }}>
{rows}
)
}
render() {
return (
{ this.setState({ showAdvancedOptions: item.value === 'advanced' }) }}
/>
{this.renderFields()}
)
}
}
export default AssessmentMigrationOptions