mirror of
https://github.com/OpenNebula/one.git
synced 2025-01-26 10:03:37 +03:00
parent
00a61d74a5
commit
ab86d01b9e
@ -20,7 +20,7 @@ import { Button, MobileStepper, Typography, Box, alpha } from '@mui/material'
|
||||
import makeStyles from '@mui/styles/makeStyles'
|
||||
import { NavArrowLeft as PreviousIcon, NavArrowRight as NextIcon } from 'iconoir-react'
|
||||
|
||||
import { Tr, Translate, labelCanBeTranslated } from 'client/components/HOC'
|
||||
import { Translate, labelCanBeTranslated } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
@ -61,8 +61,8 @@ const CustomMobileStepper = ({
|
||||
</Typography>
|
||||
{Boolean(errors[id]) && (
|
||||
<Typography className={classes.error} variant='caption' color='error'>
|
||||
{labelCanBeTranslated(errors[id]?.message)
|
||||
? Tr(errors[id]?.message) : errors[id]?.message}
|
||||
{labelCanBeTranslated(label)
|
||||
? <Translate word={errors[id]?.message} /> : errors[id]?.message}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
@ -114,7 +114,7 @@ const FormStepper = ({ steps = [], schema, onSubmit }) => {
|
||||
|
||||
if (activeStep === lastStep) {
|
||||
const submitData = { ...formData, [id]: data }
|
||||
const schemaData = schema().cast(submitData, { context: submitData })
|
||||
const schemaData = schema().cast(submitData, { context: submitData, isSubmit: true })
|
||||
onSubmit(schemaData)
|
||||
} else {
|
||||
setFormData(prev => ({ ...prev, [id]: data }))
|
||||
|
@ -13,45 +13,37 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import * as yup from 'yup'
|
||||
import { boolean, string, object, ObjectSchema } from 'yup'
|
||||
|
||||
import { getValidationFromFields } from 'client/utils'
|
||||
import { INPUT_TYPES } from 'client/constants'
|
||||
import { Field, getValidationFromFields } from 'client/utils'
|
||||
import { T, INPUT_TYPES } from 'client/constants'
|
||||
|
||||
/** @type {Field} RDP connection field */
|
||||
const RDP_FIELD = {
|
||||
name: 'RDP',
|
||||
label: 'RDP connection',
|
||||
label: T.RdpConnection,
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
validation: yup
|
||||
.boolean()
|
||||
.transform(value => {
|
||||
if (typeof value === 'boolean') return value
|
||||
|
||||
return String(value).toUpperCase() === 'YES'
|
||||
})
|
||||
.default(false),
|
||||
validation: boolean().yesOrNo(),
|
||||
grid: { md: 12 }
|
||||
}
|
||||
|
||||
/** @type {Field} SSH connection field */
|
||||
const SSH_FIELD = {
|
||||
name: 'SSH',
|
||||
label: 'SSH connection',
|
||||
label: T.SshConnection,
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
validation: yup
|
||||
.boolean()
|
||||
.transform(value => {
|
||||
if (typeof value === 'boolean') return value
|
||||
|
||||
return String(value).toUpperCase() === 'YES'
|
||||
})
|
||||
.default(false),
|
||||
validation: boolean().yesOrNo(),
|
||||
grid: { md: 12 }
|
||||
}
|
||||
|
||||
const ALIAS_FIELD = ({ nics = [] } = {}) => ({
|
||||
/**
|
||||
* @param {object} currentFormData - Current form data
|
||||
* @param {object[]} currentFormData.nics - Nics
|
||||
* @returns {Field} Alias field
|
||||
*/
|
||||
const ALIAS_FIELD = ({ nics = [] }) => ({
|
||||
name: 'PARENT',
|
||||
label: 'Attach as an alias',
|
||||
label: T.AsAnAlias,
|
||||
dependOf: 'NAME',
|
||||
type: name => {
|
||||
const hasAlias = nics?.some(nic => nic.PARENT === name)
|
||||
@ -70,35 +62,33 @@ const ALIAS_FIELD = ({ nics = [] } = {}) => ({
|
||||
return { text, value: NAME }
|
||||
})
|
||||
],
|
||||
validation: yup
|
||||
.string()
|
||||
validation: string()
|
||||
.trim()
|
||||
.notRequired()
|
||||
.default(undefined)
|
||||
.default(() => undefined)
|
||||
})
|
||||
|
||||
/** @type {Field} External field */
|
||||
const EXTERNAL_FIELD = {
|
||||
name: 'EXTERNAL',
|
||||
label: 'External',
|
||||
label: T.External,
|
||||
tooltip: T.ExternalConcept,
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
tooltip: 'The NIC will be attached as an external alias of the VM',
|
||||
dependOf: ALIAS_FIELD().name,
|
||||
htmlType: type => !type?.length ? INPUT_TYPES.HIDDEN : undefined,
|
||||
validation: yup
|
||||
.boolean()
|
||||
.transform(value => {
|
||||
if (typeof value === 'boolean') return value
|
||||
|
||||
return String(value).toUpperCase() === 'YES'
|
||||
})
|
||||
.default(false)
|
||||
dependOf: 'PARENT',
|
||||
htmlType: parent => !parent?.length && INPUT_TYPES.HIDDEN,
|
||||
validation: boolean().yesOrNo()
|
||||
}
|
||||
|
||||
export const FIELDS = props => [
|
||||
/**
|
||||
* @param {object} [currentFormData] - Current form data
|
||||
* @returns {Field[]} List of Graphics fields
|
||||
*/
|
||||
export const FIELDS = (currentFormData = {}) => [
|
||||
RDP_FIELD,
|
||||
SSH_FIELD,
|
||||
ALIAS_FIELD(props),
|
||||
ALIAS_FIELD(currentFormData),
|
||||
EXTERNAL_FIELD
|
||||
]
|
||||
|
||||
export const SCHEMA = yup.object(getValidationFromFields(FIELDS()))
|
||||
/** @type {ObjectSchema} Advanced options schema */
|
||||
export const SCHEMA = object(getValidationFromFields(FIELDS()))
|
||||
|
@ -52,7 +52,7 @@ const Content = ({ data, setFormData }) => {
|
||||
|
||||
const NetworkStep = () => ({
|
||||
id: STEP_ID,
|
||||
label: T.Network,
|
||||
label: T.SelectNetwork,
|
||||
resolver: SCHEMA,
|
||||
content: Content
|
||||
})
|
||||
|
@ -13,12 +13,12 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as yup from 'yup'
|
||||
import { array, object, ArraySchema } from 'yup'
|
||||
|
||||
export const SCHEMA = yup
|
||||
.array(yup.object())
|
||||
.min(1, 'Select network')
|
||||
.max(1, 'Max. one network selected')
|
||||
.required('Network field is required')
|
||||
/** @type {ArraySchema} Virtual Network table schema */
|
||||
export const SCHEMA = array(object())
|
||||
.min(1)
|
||||
.max(1)
|
||||
.required()
|
||||
.ensure()
|
||||
.default(() => [])
|
||||
|
@ -15,7 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import NetworksTable, { STEP_ID as NETWORK_ID } from 'client/components/Forms/Vm/AttachNicForm/Steps/NetworksTable'
|
||||
import AdvancedOptions, { STEP_ID as ADVANCED_ID } from 'client/components/Forms/Vm/AttachNicForm/Steps/AdvancedOptions'
|
||||
import { mapUserInputs, createSteps } from 'client/utils'
|
||||
import { createSteps } from 'client/utils'
|
||||
|
||||
const Steps = createSteps(
|
||||
[NetworksTable, AdvancedOptions],
|
||||
@ -52,7 +52,7 @@ const Steps = createSteps(
|
||||
NETWORK_UID: UID,
|
||||
NETWORK_UNAME: UNAME,
|
||||
SECURITY_GROUPS,
|
||||
...mapUserInputs(advanced)
|
||||
...advanced
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +178,7 @@ export const FIRMWARE = {
|
||||
grid: { md: 12 }
|
||||
}
|
||||
|
||||
/** @type {Field} Feature secure field */
|
||||
/** @type {Field} Firmware secure field */
|
||||
export const FIRMWARE_SECURE = {
|
||||
name: 'OS.FIRMWARE_SECURE',
|
||||
label: T.FirmwareSecure,
|
||||
@ -186,13 +186,7 @@ export const FIRMWARE_SECURE = {
|
||||
type: INPUT_TYPES.CHECKBOX,
|
||||
dependOf: FEATURE_CUSTOM_ENABLED.name,
|
||||
htmlType: custom => !custom && INPUT_TYPES.HIDDEN,
|
||||
validation: boolean()
|
||||
.default(() => false)
|
||||
.transform(value => {
|
||||
if (typeof value === 'boolean') return value
|
||||
|
||||
return String(value).toUpperCase() === 'YES'
|
||||
}),
|
||||
validation: boolean().yesOrNo(),
|
||||
grid: { md: 12 }
|
||||
}
|
||||
|
||||
|
@ -54,13 +54,7 @@ const VALIDATE = {
|
||||
tooltip: T.RawValidateConcept,
|
||||
type: INPUT_TYPES.CHECKBOX,
|
||||
notOnHypervisors: [lxc, vcenter, firecracker],
|
||||
validation: boolean()
|
||||
.transform(value => {
|
||||
if (typeof value === 'boolean') return value
|
||||
|
||||
return String(value).toUpperCase() === 'YES'
|
||||
})
|
||||
.default(() => false),
|
||||
validation: boolean().yesOrNo(),
|
||||
grid: { md: 12 }
|
||||
}
|
||||
|
||||
|
@ -20,13 +20,7 @@ import { Field, getObjectSchemaFromFields } from 'client/utils'
|
||||
|
||||
const switchField = {
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
validation: boolean()
|
||||
.notRequired()
|
||||
.transform(value => {
|
||||
if (typeof value === 'boolean') return value
|
||||
|
||||
return String(value).toUpperCase() === 'YES'
|
||||
}),
|
||||
validation: boolean().yesOrNo(),
|
||||
grid: { md: 12 }
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { object, array, string, boolean, number, ref, ObjectSchema } from 'yup'
|
||||
|
||||
import { userInputsToObject } from 'client/models/Helper'
|
||||
import { UserInputType, T, INPUT_TYPES, USER_INPUT_TYPES } from 'client/constants'
|
||||
import { Field, arrayToOptions, sentenceCase, getObjectSchemaFromFields } from 'client/utils'
|
||||
|
||||
@ -183,6 +184,13 @@ export const USER_INPUT_SCHEMA = getObjectSchemaFromFields(USER_INPUT_FIELDS)
|
||||
|
||||
/** @type {ObjectSchema} User Inputs schema */
|
||||
export const USER_INPUTS_SCHEMA = object({
|
||||
USER_INPUTS: array(USER_INPUT_SCHEMA).ensure(),
|
||||
INPUTS_ORDER: string().trim().strip()
|
||||
USER_INPUTS: array(USER_INPUT_SCHEMA)
|
||||
.ensure()
|
||||
.afterSubmit(userInputs => userInputsToObject(userInputs)),
|
||||
INPUTS_ORDER: string()
|
||||
.trim()
|
||||
.afterSubmit((_, { context }) => {
|
||||
const userInputs = context?.extra?.USER_INPUTS
|
||||
return userInputs?.map(({ name }) => name).join(',')
|
||||
})
|
||||
})
|
||||
|
@ -13,9 +13,9 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { string, boolean } from 'yup'
|
||||
import { string, boolean, ObjectSchema } from 'yup'
|
||||
|
||||
import { Field, arrayToOptions, filterFieldsByHypervisor } from 'client/utils'
|
||||
import { Field, arrayToOptions, filterFieldsByHypervisor, getObjectSchemaFromFields } from 'client/utils'
|
||||
import { T, INPUT_TYPES, HYPERVISORS } from 'client/constants'
|
||||
|
||||
const { vcenter, lxc, kvm } = HYPERVISORS
|
||||
@ -90,13 +90,7 @@ const RANDOM_PASSWD = {
|
||||
type: INPUT_TYPES.CHECKBOX,
|
||||
dependOf: TYPE.name,
|
||||
htmlType: noneType => !noneType && INPUT_TYPES.HIDDEN,
|
||||
validation: boolean()
|
||||
.default(() => false)
|
||||
.transform(value => {
|
||||
if (typeof value === 'boolean') return value
|
||||
|
||||
return String(value).toUpperCase() === 'YES'
|
||||
}),
|
||||
validation: boolean().yesOrNo(),
|
||||
grid: { md: 12 }
|
||||
}
|
||||
|
||||
@ -139,3 +133,7 @@ export const GRAPHICS_FIELDS = hypervisor =>
|
||||
[TYPE, LISTEN, PORT, KEYMAP, PASSWD, RANDOM_PASSWD, COMMAND],
|
||||
hypervisor
|
||||
)
|
||||
|
||||
/** @type {ObjectSchema} Context Files schema */
|
||||
export const GRAPHICS_SCHEMA = hypervisor =>
|
||||
getObjectSchemaFromFields(GRAPHICS_FIELDS(hypervisor))
|
||||
|
@ -23,7 +23,7 @@ import { FormWithSchema } from 'client/components/Forms'
|
||||
import { STEP_ID as EXTRA_ID, TabType } from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration'
|
||||
import InputsSection, { SECTION_ID as INPUT_ID } from './inputsSection'
|
||||
import PciDevicesSection, { SECTION_ID as PCI_ID } from './pciDevicesSection'
|
||||
import { INPUT_OUTPUT_FIELDS, INPUTS_FIELDS, PCI_FIELDS } from './schema'
|
||||
import { GRAPHICS_FIELDS, INPUTS_FIELDS, PCI_FIELDS } from './schema'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
export const TAB_ID = ['GRAPHICS', INPUT_ID, PCI_ID]
|
||||
@ -40,7 +40,7 @@ const InputOutput = ({ hypervisor }) => {
|
||||
>
|
||||
<FormWithSchema
|
||||
cy={`create-vm-template-${EXTRA_ID}.io-graphics`}
|
||||
fields={INPUT_OUTPUT_FIELDS(hypervisor)}
|
||||
fields={GRAPHICS_FIELDS(hypervisor)}
|
||||
legend={T.Graphics}
|
||||
id={EXTRA_ID}
|
||||
/>
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { string, array, object, ObjectSchema, ArraySchema } from 'yup'
|
||||
import { string, array, object, ObjectSchema } from 'yup'
|
||||
import { PcMouse, PenTablet, Usb, PlugTypeG } from 'iconoir-react'
|
||||
|
||||
import { Field, arrayToOptions, filterFieldsByHypervisor, getValidationFromFields } from 'client/utils'
|
||||
@ -69,5 +69,7 @@ export const INPUTS_FIELDS = (hypervisor) =>
|
||||
/** @type {ObjectSchema} Graphic input object schema */
|
||||
export const INPUT_SCHEMA = object(getValidationFromFields([TYPE, BUS]))
|
||||
|
||||
/** @type {ArraySchema} Graphic inputs schema */
|
||||
export const INPUTS_SCHEMA = array(INPUT_SCHEMA).ensure()
|
||||
/** @type {ObjectSchema} Graphic inputs schema */
|
||||
export const INPUTS_SCHEMA = object({
|
||||
INPUT: array(INPUT_SCHEMA).ensure()
|
||||
})
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { string, array, ObjectSchema, ArraySchema } from 'yup'
|
||||
import { object, string, array, ObjectSchema } from 'yup'
|
||||
|
||||
import { useHost } from 'client/features/One'
|
||||
import { getPciDevices } from 'client/models/Host'
|
||||
@ -88,5 +88,7 @@ export const PCI_FIELDS = (hypervisor) =>
|
||||
/** @type {ObjectSchema} PCI devices object schema */
|
||||
export const PCI_SCHEMA = getObjectSchemaFromFields([DEVICE, VENDOR, CLASS])
|
||||
|
||||
/** @type {ArraySchema} PCI devices schema */
|
||||
export const PCI_DEVICES_SCHEMA = array(PCI_SCHEMA).ensure()
|
||||
/** @type {ObjectSchema} PCI devices schema */
|
||||
export const PCI_DEVICES_SCHEMA = object({
|
||||
PCI: array(PCI_SCHEMA).ensure()
|
||||
})
|
||||
|
@ -15,28 +15,18 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import { object, ObjectSchema } from 'yup'
|
||||
|
||||
import { GRAPHICS_FIELDS } from './graphicsSchema'
|
||||
import { GRAPHICS_SCHEMA } from './graphicsSchema'
|
||||
import { INPUTS_SCHEMA } from './inputsSchema'
|
||||
import { PCI_DEVICES_SCHEMA } from './pciDevicesSchema'
|
||||
import { Field, getObjectSchemaFromFields } from 'client/utils'
|
||||
|
||||
/**
|
||||
* @param {string} [hypervisor] - VM hypervisor
|
||||
* @returns {Field[]} List of I/O fields
|
||||
*/
|
||||
export const INPUT_OUTPUT_FIELDS = hypervisor =>
|
||||
[...GRAPHICS_FIELDS(hypervisor)]
|
||||
|
||||
/**
|
||||
* @param {string} [hypervisor] - VM hypervisor
|
||||
* @returns {ObjectSchema} I/O schema
|
||||
*/
|
||||
export const SCHEMA = hypervisor => object({
|
||||
INPUT: INPUTS_SCHEMA,
|
||||
PCI: PCI_DEVICES_SCHEMA
|
||||
}).concat(getObjectSchemaFromFields([
|
||||
...GRAPHICS_FIELDS(hypervisor)
|
||||
]))
|
||||
export const SCHEMA = hypervisor => object()
|
||||
.concat(INPUTS_SCHEMA)
|
||||
.concat(PCI_DEVICES_SCHEMA)
|
||||
.concat(GRAPHICS_SCHEMA(hypervisor))
|
||||
|
||||
export * from './graphicsSchema'
|
||||
export * from './inputsSchema'
|
||||
|
@ -43,13 +43,7 @@ export const ENABLE_HR_MEMORY = {
|
||||
name: 'HOT_RESIZE.MEMORY_HOT_ADD_ENABLED',
|
||||
label: T.EnableHotResize,
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
validation: boolean()
|
||||
.transform(value => {
|
||||
if (typeof value === 'boolean') return value
|
||||
|
||||
return String(value).toUpperCase() === 'YES'
|
||||
})
|
||||
.default(() => false),
|
||||
validation: boolean().yesOrNo(),
|
||||
grid: { xs: 4, md: 6 }
|
||||
}
|
||||
|
||||
@ -94,13 +88,7 @@ export const ENABLE_HR_VCPU = {
|
||||
name: 'HOT_RESIZE.CPU_HOT_ADD_ENABLED',
|
||||
label: T.EnableHotResize,
|
||||
type: INPUT_TYPES.SWITCH,
|
||||
validation: boolean()
|
||||
.transform(value => {
|
||||
if (typeof value === 'boolean') return value
|
||||
|
||||
return String(value).toUpperCase() === 'YES'
|
||||
})
|
||||
.default(() => false),
|
||||
validation: boolean().yesOrNo(),
|
||||
grid: { xs: 4, md: 6 }
|
||||
}
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useMemo } from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import { useWatch } from 'react-hook-form'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
@ -28,7 +29,7 @@ import { T } from 'client/constants'
|
||||
|
||||
export const STEP_ID = 'general'
|
||||
|
||||
const Content = () => {
|
||||
const Content = ({ isUpdate }) => {
|
||||
const classes = useStyles()
|
||||
const { view, getResourceView } = useAuth()
|
||||
const hypervisor = useWatch({ name: `${STEP_ID}.HYPERVISOR` })
|
||||
@ -37,7 +38,7 @@ const Content = () => {
|
||||
const dialog = getResourceView('VM-TEMPLATE')?.dialogs?.create_dialog
|
||||
const sectionsAvailable = getSectionsAvailable(dialog, hypervisor)
|
||||
|
||||
return SECTIONS(hypervisor)
|
||||
return SECTIONS(hypervisor, isUpdate)
|
||||
.filter(({ id, required }) => required || sectionsAvailable.includes(id))
|
||||
}, [view, hypervisor])
|
||||
|
||||
@ -62,15 +63,23 @@ const Content = () => {
|
||||
)
|
||||
}
|
||||
|
||||
const General = () => ({
|
||||
id: STEP_ID,
|
||||
label: T.General,
|
||||
resolver: formData => {
|
||||
const hypervisor = formData?.[STEP_ID]?.HYPERVISOR
|
||||
return SCHEMA(hypervisor)
|
||||
},
|
||||
optionsValidate: { abortEarly: false },
|
||||
content: Content
|
||||
})
|
||||
const General = initialValues => {
|
||||
const isUpdate = initialValues?.NAME
|
||||
|
||||
return {
|
||||
id: STEP_ID,
|
||||
label: T.General,
|
||||
resolver: formData => {
|
||||
const hypervisor = formData?.[STEP_ID]?.HYPERVISOR
|
||||
return SCHEMA(hypervisor, isUpdate)
|
||||
},
|
||||
optionsValidate: { abortEarly: false },
|
||||
content: () => Content({ isUpdate })
|
||||
}
|
||||
}
|
||||
|
||||
Content.propTypes = {
|
||||
isUpdate: PropTypes.bool
|
||||
}
|
||||
|
||||
export default General
|
||||
|
@ -19,8 +19,11 @@ import Image from 'client/components/Image'
|
||||
import { T, LOGO_IMAGES_URL, INPUT_TYPES, HYPERVISORS } from 'client/constants'
|
||||
import { Field, arrayToOptions } from 'client/utils'
|
||||
|
||||
/** @type {Field} Name field */
|
||||
export const NAME = {
|
||||
/**
|
||||
* @param {boolean} isUpdate - If `true`, the form is being updated
|
||||
* @returns {Field} Name field
|
||||
*/
|
||||
export const NAME = isUpdate => ({
|
||||
name: 'NAME',
|
||||
label: T.Name,
|
||||
type: INPUT_TYPES.TEXT,
|
||||
@ -28,8 +31,9 @@ export const NAME = {
|
||||
.trim()
|
||||
.required()
|
||||
.default(() => undefined),
|
||||
grid: { sm: 6 }
|
||||
}
|
||||
grid: { sm: 6 },
|
||||
...(isUpdate && { fieldProps: { disabled: true } })
|
||||
})
|
||||
|
||||
/** @type {Field} Description field */
|
||||
export const DESCRIPTION = {
|
||||
@ -100,9 +104,12 @@ export const LOGO = {
|
||||
.default(() => undefined)
|
||||
}
|
||||
|
||||
/** @type {Field[]} List of information fields */
|
||||
export const FIELDS = [
|
||||
NAME,
|
||||
/**
|
||||
* @param {boolean} isUpdate - If `true`, the form is being updated
|
||||
* @returns {Field[]} List of information fields
|
||||
*/
|
||||
export const FIELDS = isUpdate => [
|
||||
NAME(isUpdate),
|
||||
DESCRIPTION,
|
||||
LOGO
|
||||
]
|
||||
|
@ -26,14 +26,15 @@ import { T, HYPERVISORS } from 'client/constants'
|
||||
|
||||
/**
|
||||
* @param {HYPERVISORS} [hypervisor] - Template hypervisor
|
||||
* @param {boolean} [isUpdate] - If `true`, the form is being updated
|
||||
* @returns {Section[]} Fields
|
||||
*/
|
||||
const SECTIONS = hypervisor => [
|
||||
const SECTIONS = (hypervisor, isUpdate) => [
|
||||
{
|
||||
id: 'information',
|
||||
legend: T.Information,
|
||||
required: true,
|
||||
fields: filterFieldsByHypervisor(INFORMATION_FIELDS, hypervisor)
|
||||
fields: filterFieldsByHypervisor(INFORMATION_FIELDS(isUpdate), hypervisor)
|
||||
},
|
||||
{
|
||||
id: 'capacity',
|
||||
|
@ -15,37 +15,43 @@
|
||||
* ------------------------------------------------------------------------- */
|
||||
import General, { STEP_ID as GENERAL_ID } from 'client/components/Forms/VmTemplate/CreateForm/Steps/General'
|
||||
import ExtraConfiguration, { STEP_ID as EXTRA_ID } from 'client/components/Forms/VmTemplate/CreateForm/Steps/ExtraConfiguration'
|
||||
// import { jsonToXml } from 'client/models/Helper'
|
||||
import { userInputsToArray, userInputsToObject } from 'client/models/Helper'
|
||||
import { jsonToXml, userInputsToArray } from 'client/models/Helper'
|
||||
import { createSteps, isBase64 } from 'client/utils'
|
||||
|
||||
const Steps = createSteps(
|
||||
[General, ExtraConfiguration],
|
||||
{
|
||||
transformInitialValue: (vmTemplate, schema) => ({
|
||||
...schema.pick([GENERAL_ID]).cast({
|
||||
[GENERAL_ID]: { ...vmTemplate, ...vmTemplate?.TEMPLATE }
|
||||
}, { stripUnknown: true }),
|
||||
...schema.pick([EXTRA_ID]).cast({
|
||||
[EXTRA_ID]: {
|
||||
...vmTemplate?.TEMPLATE,
|
||||
USER_INPUTS: userInputsToArray(vmTemplate?.TEMPLATE?.USER_INPUTS)
|
||||
}
|
||||
}, { stripUnknown: true, context: { [EXTRA_ID]: vmTemplate.TEMPLATE } })
|
||||
}),
|
||||
transformInitialValue: (vmTemplate, schema) => {
|
||||
const generalStep = schema
|
||||
.pick([GENERAL_ID])
|
||||
.cast(
|
||||
{ [GENERAL_ID]: { ...vmTemplate, ...vmTemplate?.TEMPLATE } },
|
||||
{ stripUnknown: true }
|
||||
)
|
||||
|
||||
const inputsOrder = vmTemplate?.TEMPLATE?.INPUTS_ORDER?.split(',') ?? []
|
||||
const userInputs = userInputsToArray(vmTemplate?.TEMPLATE?.USER_INPUTS)
|
||||
.sort((a, b) => inputsOrder.indexOf(a.name) - inputsOrder.indexOf(b.name))
|
||||
|
||||
const configurationStep = schema
|
||||
.pick([EXTRA_ID])
|
||||
.cast(
|
||||
{ [EXTRA_ID]: { ...vmTemplate?.TEMPLATE, USER_INPUTS: userInputs } },
|
||||
{ stripUnknown: true, context: { [EXTRA_ID]: vmTemplate.TEMPLATE } }
|
||||
)
|
||||
|
||||
return { ...generalStep, ...configurationStep }
|
||||
},
|
||||
transformBeforeSubmit: formData => {
|
||||
const {
|
||||
[GENERAL_ID]: general = {},
|
||||
[EXTRA_ID]: { USER_INPUTS, CONTEXT, ...extraTemplate } = {}
|
||||
[EXTRA_ID]: {
|
||||
USER_INPUTS,
|
||||
CONTEXT: { START_SCRIPT, ENCODE_START_SCRIPT, ...restOfContext },
|
||||
...extraTemplate
|
||||
} = {}
|
||||
} = formData ?? {}
|
||||
|
||||
// const templateXML = jsonToXml({ ...general, ...extraTemplate })
|
||||
// return { template: templateXML }
|
||||
|
||||
const userInputs = userInputsToObject(USER_INPUTS)
|
||||
const inputsOrder = USER_INPUTS.map(({ name }) => name).join(',')
|
||||
const { START_SCRIPT, ENCODE_START_SCRIPT, ...restOfContext } = CONTEXT
|
||||
|
||||
const context = {
|
||||
...restOfContext,
|
||||
// transform start script to base64 if needed
|
||||
@ -56,18 +62,17 @@ const Steps = createSteps(
|
||||
}
|
||||
|
||||
// add user inputs to context
|
||||
for (const { name } of USER_INPUTS) {
|
||||
const userInputsNames = Object.keys(USER_INPUTS).forEach(name => {
|
||||
const upperName = String(name).toUpperCase()
|
||||
context[upperName] = `$${upperName}`
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
return jsonToXml({
|
||||
...extraTemplate,
|
||||
...general,
|
||||
CONTEXT: context,
|
||||
USER_INPUTS: userInputs,
|
||||
INPUTS_ORDER: inputsOrder
|
||||
}
|
||||
USER_INPUTS: USER_INPUTS
|
||||
})
|
||||
}
|
||||
}
|
||||
)
|
||||
|
@ -126,7 +126,10 @@ TranslateProvider.propTypes = {
|
||||
}
|
||||
|
||||
Translate.propTypes = {
|
||||
word: PropTypes.string,
|
||||
word: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.array
|
||||
]),
|
||||
values: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.number,
|
||||
|
@ -102,6 +102,7 @@ module.exports = {
|
||||
Select: 'Select',
|
||||
SelectGroup: 'Select a group',
|
||||
SelectHost: 'Select a host',
|
||||
SelectNetwork: 'Select a network',
|
||||
SelectRequest: 'Select request',
|
||||
SelectVmTemplate: 'Select a VM Template',
|
||||
Share: 'Share',
|
||||
@ -212,6 +213,7 @@ module.exports = {
|
||||
System: 'System',
|
||||
Language: 'Language',
|
||||
DisableDashboardAnimations: 'Disable dashboard animations',
|
||||
ConfigurationUI: 'Configuration UI',
|
||||
|
||||
/* sections - system */
|
||||
User: 'User',
|
||||
@ -346,6 +348,11 @@ module.exports = {
|
||||
/* VM schema - network */
|
||||
NIC: 'NIC',
|
||||
Alias: 'Alias',
|
||||
AsAnAlias: 'Attach as an alias',
|
||||
RdpConnection: 'RDP connection',
|
||||
SshConnection: 'SSH connection',
|
||||
External: 'External',
|
||||
ExternalConcept: 'The NIC will be attached as an external alias of the VM',
|
||||
|
||||
/* VM Template schema */
|
||||
/* VM schema - general */
|
||||
|
@ -13,17 +13,17 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useEffect } from 'react'
|
||||
import { useEffect, JSXElementConstructor } from 'react'
|
||||
import { Container, Box, Grid } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useFetchAll } from 'client/hooks'
|
||||
import { useProvisionApi, useProviderApi } from 'client/features/One'
|
||||
import * as Widgets from 'client/components/Widgets'
|
||||
import dashboardStyles from 'client/containers/Dashboard/Provision/styles'
|
||||
import { stringToBoolean } from 'client/models/Helper'
|
||||
|
||||
function Dashboard () {
|
||||
/** @returns {JSXElementConstructor} Provision dashboard container */
|
||||
function ProvisionDashboard () {
|
||||
const { status, fetchRequestAll, STATUS } = useFetchAll()
|
||||
const { INIT, PENDING } = STATUS
|
||||
|
||||
@ -31,9 +31,6 @@ function Dashboard () {
|
||||
const { getProviders } = useProviderApi()
|
||||
|
||||
const { settings: { disableanimations } = {} } = useAuth()
|
||||
const classes = dashboardStyles({ disableanimations })
|
||||
|
||||
const withoutAnimations = String(disableanimations).toUpperCase() === 'YES'
|
||||
|
||||
useEffect(() => {
|
||||
fetchRequestAll([
|
||||
@ -45,8 +42,12 @@ function Dashboard () {
|
||||
return (
|
||||
<Container
|
||||
disableGutters
|
||||
{...withoutAnimations && {
|
||||
className: classes.withoutAnimations
|
||||
{...stringToBoolean(disableanimations) && {
|
||||
sx: {
|
||||
'& *, & *::before, & *::after': {
|
||||
animation: 'none !important'
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Box py={3}>
|
||||
@ -72,4 +73,4 @@ function Dashboard () {
|
||||
)
|
||||
}
|
||||
|
||||
export default Dashboard
|
||||
export default ProvisionDashboard
|
||||
|
@ -1,24 +0,0 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import makeStyles from '@mui/styles/makeStyles'
|
||||
|
||||
export default makeStyles({
|
||||
withoutAnimations: {
|
||||
'& *, & *::before, & *::after': {
|
||||
animation: 'none !important'
|
||||
}
|
||||
}
|
||||
})
|
@ -13,18 +13,17 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useEffect } from 'react'
|
||||
import { useEffect, JSXElementConstructor } from 'react'
|
||||
import { Container, Box, Grid } from '@mui/material'
|
||||
|
||||
import { useAuth } from 'client/features/Auth'
|
||||
import { useFetchAll } from 'client/hooks'
|
||||
import { useUserApi, useImageApi, useVNetworkApi, useDatastoreApi } from 'client/features/One'
|
||||
|
||||
import * as Widgets from 'client/components/Widgets'
|
||||
import dashboardStyles from 'client/containers/Dashboard/Provision/styles'
|
||||
import { stringToBoolean } from 'client/models/Helper'
|
||||
|
||||
function Dashboard () {
|
||||
/** @returns {JSXElementConstructor} Sunstone dashboard container */
|
||||
function SunstoneDashboard () {
|
||||
const { status, fetchRequestAll, STATUS } = useFetchAll()
|
||||
const { INIT, PENDING } = STATUS
|
||||
|
||||
@ -34,9 +33,6 @@ function Dashboard () {
|
||||
const { getDatastores } = useDatastoreApi()
|
||||
|
||||
const { settings: { disableanimations } = {} } = useAuth()
|
||||
const classes = dashboardStyles({ disableanimations })
|
||||
|
||||
const withoutAnimations = String(disableanimations).toUpperCase() === 'YES'
|
||||
|
||||
useEffect(() => {
|
||||
fetchRequestAll([
|
||||
@ -50,8 +46,12 @@ function Dashboard () {
|
||||
return (
|
||||
<Container
|
||||
disableGutters
|
||||
{...withoutAnimations && {
|
||||
className: classes.withoutAnimations
|
||||
{...stringToBoolean(disableanimations) && {
|
||||
sx: {
|
||||
'& *, & *::before, & *::after': {
|
||||
animation: 'none !important'
|
||||
}
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Box py={3}>
|
||||
@ -67,4 +67,4 @@ function Dashboard () {
|
||||
)
|
||||
}
|
||||
|
||||
export default Dashboard
|
||||
export default SunstoneDashboard
|
||||
|
@ -1,24 +0,0 @@
|
||||
/* ------------------------------------------------------------------------- *
|
||||
* Copyright 2002-2021, OpenNebula Project, OpenNebula Systems *
|
||||
* *
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may *
|
||||
* not use this file except in compliance with the License. You may obtain *
|
||||
* a copy of the License at *
|
||||
* *
|
||||
* http://www.apache.org/licenses/LICENSE-2.0 *
|
||||
* *
|
||||
* Unless required by applicable law or agreed to in writing, software *
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, *
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import makeStyles from '@mui/styles/makeStyles'
|
||||
|
||||
export default makeStyles({
|
||||
withoutAnimations: {
|
||||
'& *, & *::before, & *::after': {
|
||||
animation: 'none !important'
|
||||
}
|
||||
}
|
||||
})
|
@ -13,12 +13,8 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { useEffect } from 'react'
|
||||
|
||||
import { Container, Paper, Box, Typography } from '@mui/material'
|
||||
import makeStyles from '@mui/styles/makeStyles'
|
||||
|
||||
import { JSXElementConstructor } from 'react'
|
||||
import { Container, Paper, Box, Typography, Divider } from '@mui/material'
|
||||
import { useForm, FormProvider } from 'react-hook-form'
|
||||
import { yupResolver } from '@hookform/resolvers/yup'
|
||||
|
||||
@ -28,38 +24,14 @@ import SubmitButton from 'client/components/FormControl/SubmitButton'
|
||||
import { useAuth, useAuthApi } from 'client/features/Auth'
|
||||
import { useUserApi } from 'client/features/One'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { Tr } from 'client/components/HOC'
|
||||
import { Translate, Tr } from 'client/components/HOC'
|
||||
import { T } from 'client/constants'
|
||||
|
||||
import { FORM_FIELDS, FORM_SCHEMA } from 'client/containers/Settings/schema'
|
||||
import { mapUserInputs } from 'client/utils'
|
||||
import * as Helper from 'client/models/Helper'
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
header: {
|
||||
paddingTop: '1rem'
|
||||
},
|
||||
title: {
|
||||
flexGrow: 1,
|
||||
letterSpacing: 0.1,
|
||||
fontWeight: 500
|
||||
},
|
||||
wrapper: {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
maxWidth: 550,
|
||||
padding: '1rem'
|
||||
},
|
||||
subheader: {
|
||||
marginBottom: '1rem'
|
||||
},
|
||||
actions: {
|
||||
padding: '1rem 0',
|
||||
textAlign: 'end'
|
||||
}
|
||||
}))
|
||||
|
||||
/** @returns {JSXElementConstructor} Settings container */
|
||||
const Settings = () => {
|
||||
const classes = useStyles()
|
||||
const { user, settings } = useAuth()
|
||||
const { getAuthUser } = useAuthApi()
|
||||
const { updateUser } = useUserApi()
|
||||
@ -67,47 +39,43 @@ const Settings = () => {
|
||||
|
||||
const { handleSubmit, setError, reset, formState, ...methods } = useForm({
|
||||
reValidateMode: 'onSubmit',
|
||||
defaultValues: settings,
|
||||
defaultValues: FORM_SCHEMA.cast(settings),
|
||||
resolver: yupResolver(FORM_SCHEMA)
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
reset(
|
||||
FORM_SCHEMA.cast(settings),
|
||||
{ keepIsSubmitted: false, keepErrors: false }
|
||||
)
|
||||
}, [settings])
|
||||
|
||||
const onSubmit = async dataForm => {
|
||||
try {
|
||||
const inputs = mapUserInputs(dataForm)
|
||||
const template = Helper.jsonToXml({ FIREEDGE: inputs })
|
||||
|
||||
const template = Helper.jsonToXml({ FIREEDGE: dataForm })
|
||||
await updateUser(user.ID, { template }).then(getAuthUser)
|
||||
} catch {
|
||||
enqueueError(Tr(T.SomethingWrong))
|
||||
enqueueError(T.SomethingWrong)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Container disableGutters>
|
||||
<div className={classes.header}>
|
||||
<Typography variant='h5' className={classes.title}>
|
||||
{Tr(T.Settings)}
|
||||
</Typography>
|
||||
</div>
|
||||
<Typography variant='h5' pt='1em'>
|
||||
<Translate word={T.Settings} />
|
||||
</Typography>
|
||||
|
||||
<hr />
|
||||
<Divider sx={{ my: '1em' }} />
|
||||
|
||||
<Paper className={classes.wrapper} variant='outlined'>
|
||||
<Typography variant='overline' component='div' className={classes.subheader}>
|
||||
{`${Tr(T.Configuration)} UI`}
|
||||
</Typography>
|
||||
<Paper
|
||||
variant='outlined'
|
||||
sx={{
|
||||
p: '1em',
|
||||
maxWidth: { sm: 'auto', md: 550 }
|
||||
}}
|
||||
>
|
||||
<Box component="form" onSubmit={handleSubmit(onSubmit)}>
|
||||
<FormProvider {...methods}>
|
||||
<FormWithSchema cy='settings' fields={FORM_FIELDS} />
|
||||
<FormWithSchema
|
||||
cy='settings'
|
||||
fields={FORM_FIELDS}
|
||||
legend={T.ConfigurationUI}
|
||||
/>
|
||||
</FormProvider>
|
||||
<div className={classes.actions}>
|
||||
<Box py='1em' textAlign='end'>
|
||||
<SubmitButton
|
||||
color='secondary'
|
||||
data-cy='settings-submit-button'
|
||||
@ -116,7 +84,7 @@ const Settings = () => {
|
||||
disabled={!formState.isDirty}
|
||||
isSubmitting={formState.isSubmitting}
|
||||
/>
|
||||
</div>
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
</Container>
|
||||
|
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
import * as yup from 'yup'
|
||||
import { object, boolean, string } from 'yup'
|
||||
import { T, INPUT_TYPES, SCHEMES, DEFAULT_SCHEME, DEFAULT_LANGUAGE } from 'client/constants'
|
||||
import { getValidationFromFields } from 'client/utils'
|
||||
|
||||
@ -26,11 +26,10 @@ const SCHEME = {
|
||||
{ text: T.Dark, value: SCHEMES.DARK },
|
||||
{ text: T.Light, value: SCHEMES.LIGHT }
|
||||
],
|
||||
validation: yup
|
||||
.string()
|
||||
validation: string()
|
||||
.trim()
|
||||
.required('Scheme field is required')
|
||||
.default(DEFAULT_SCHEME),
|
||||
.required()
|
||||
.default(() => DEFAULT_SCHEME),
|
||||
grid: { md: 12 }
|
||||
}
|
||||
|
||||
@ -40,11 +39,10 @@ const LANGUAGES = {
|
||||
type: INPUT_TYPES.SELECT,
|
||||
values: () =>
|
||||
window?.langs?.map(({ key, value }) => ({ text: value, value: key })) ?? [],
|
||||
validation: yup
|
||||
.string()
|
||||
validation: string()
|
||||
.trim()
|
||||
.required('Language field is required')
|
||||
.default(DEFAULT_LANGUAGE),
|
||||
.required()
|
||||
.default(() => DEFAULT_LANGUAGE),
|
||||
grid: { md: 12 }
|
||||
}
|
||||
|
||||
@ -52,19 +50,12 @@ const DISABLE_ANIMATIONS = {
|
||||
name: 'disableanimations',
|
||||
label: T.DisableDashboardAnimations,
|
||||
type: INPUT_TYPES.CHECKBOX,
|
||||
validation: yup
|
||||
.boolean()
|
||||
.transform(value => {
|
||||
if (typeof value === 'boolean') return value
|
||||
|
||||
return String(value).toUpperCase() === 'YES'
|
||||
})
|
||||
.default(false),
|
||||
validation: boolean()
|
||||
.yesOrNo()
|
||||
.default(() => false),
|
||||
grid: { md: 12 }
|
||||
}
|
||||
|
||||
export const FORM_FIELDS = [SCHEME, LANGUAGES, DISABLE_ANIMATIONS]
|
||||
|
||||
export const FORM_SCHEMA = yup.object(
|
||||
getValidationFromFields(FORM_FIELDS)
|
||||
)
|
||||
export const FORM_SCHEMA = object(getValidationFromFields(FORM_FIELDS))
|
||||
|
@ -13,32 +13,39 @@
|
||||
* See the License for the specific language governing permissions and *
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
/* eslint-disable jsdoc/require-jsdoc */
|
||||
import { /* useHistory, */ useLocation } from 'react-router'
|
||||
import { JSXElementConstructor } from 'react'
|
||||
import { useHistory, useLocation } from 'react-router'
|
||||
import { Container } from '@mui/material'
|
||||
|
||||
// import { useGeneralApi } from 'client/features/General'
|
||||
// import { useVmTemplateApi } from 'client/features/One'
|
||||
import { useGeneralApi } from 'client/features/General'
|
||||
import { useVmTemplateApi } from 'client/features/One'
|
||||
import { CreateForm } from 'client/components/Forms/VmTemplate'
|
||||
// import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
import { PATH } from 'client/apps/sunstone/routesOne'
|
||||
import { isDevelopment } from 'client/utils'
|
||||
|
||||
/**
|
||||
* Displays the creation or modification form to a VM Template.
|
||||
*
|
||||
* @returns {JSXElementConstructor} VM Template form
|
||||
*/
|
||||
function CreateVmTemplate () {
|
||||
// const history = useHistory()
|
||||
const { state: { ID: templateId } = {} } = useLocation()
|
||||
const history = useHistory()
|
||||
const { state: { ID: templateId, NAME } = {} } = useLocation()
|
||||
|
||||
// const { enqueueInfo } = useGeneralApi()
|
||||
// const { instantiate } = useVmTemplateApi()
|
||||
const { enqueueSuccess } = useGeneralApi()
|
||||
const { update, allocate } = useVmTemplateApi()
|
||||
|
||||
const onSubmit = async formData => {
|
||||
const onSubmit = async template => {
|
||||
try {
|
||||
console.log({ formData })
|
||||
/* const { ID, NAME } = templateSelected
|
||||
|
||||
await Promise.all(templates.map(template => instantiate(ID, template)))
|
||||
|
||||
history.push(templateId ? PATH.TEMPLATE.VMS.LIST : PATH.INSTANCE.VMS.LIST)
|
||||
enqueueInfo(`VM Template instantiated x${templates.length} - #${ID} ${NAME}`) */
|
||||
if (templateId === undefined) {
|
||||
await allocate(template)
|
||||
history.push(PATH.TEMPLATE.VMS.LIST)
|
||||
enqueueSuccess(`VM Template created - #${templateId}`)
|
||||
} else {
|
||||
await update(templateId, template)
|
||||
history.push(PATH.TEMPLATE.VMS.LIST)
|
||||
enqueueSuccess(`VM Template updated - #${templateId} ${NAME}`)
|
||||
}
|
||||
} catch (err) {
|
||||
isDevelopment() && console.error(err)
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ const initial = () => ({
|
||||
settings: {
|
||||
scheme: DEFAULT_SCHEME,
|
||||
lang: DEFAULT_LANGUAGE,
|
||||
disableAnimations: 'NO'
|
||||
disableanimations: 'NO'
|
||||
},
|
||||
isLoginInProgress: false,
|
||||
isLoading: false
|
||||
|
@ -14,14 +14,41 @@
|
||||
* limitations under the License. *
|
||||
* ------------------------------------------------------------------------- */
|
||||
|
||||
/* eslint-disable react/display-name */
|
||||
/* eslint-disable react/prop-types */
|
||||
import { setLocale, addMethod, number, string } from 'yup'
|
||||
import { setLocale, addMethod, number, string, boolean, object, array, date } from 'yup'
|
||||
|
||||
import { T } from 'client/constants'
|
||||
import { isDivisibleBy, isBase64 } from 'client/utils/helpers'
|
||||
|
||||
const buildMethods = () => {
|
||||
[number, string, boolean, object, array, date].forEach(schemaType => {
|
||||
addMethod(schemaType, 'afterSubmit', function (fn) {
|
||||
this.submit = (...args) => typeof fn === 'function' ? fn(...args) : args[0]
|
||||
return this
|
||||
})
|
||||
addMethod(schemaType, 'cast', function (value, options = {}) {
|
||||
const resolvedSchema = this.resolve({ value, ...options })
|
||||
let result = resolvedSchema._cast(value, options)
|
||||
|
||||
if (options.isSubmit) {
|
||||
result = this.submit?.(result, options) ?? result
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
})
|
||||
addMethod(boolean, 'yesOrNo', function (addAfterSubmit = true) {
|
||||
const schema = this.transform(function (value) {
|
||||
return !this.isType(value)
|
||||
? String(value).toUpperCase() === 'YES'
|
||||
: value
|
||||
})
|
||||
|
||||
if (addAfterSubmit) {
|
||||
schema.afterSubmit(value => value ? 'YES' : 'NO')
|
||||
}
|
||||
|
||||
return schema
|
||||
})
|
||||
addMethod(number, 'isDivisibleBy', function (divisor) {
|
||||
return this.test(
|
||||
'is-divisible',
|
||||
|
Loading…
x
Reference in New Issue
Block a user