1
0
mirror of https://github.com/OpenNebula/one.git synced 2025-03-20 10:50:08 +03:00

F #3951: Fix provision UI (#639)

This commit is contained in:
Sergio Betanzos 2021-01-14 12:39:36 +01:00 committed by GitHub
parent 436613ef23
commit b119d6a22b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 258 additions and 143 deletions

View File

@ -3703,6 +3703,11 @@
"resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
"integrity": "sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA=="
},
"dompurify": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.2.6.tgz",
"integrity": "sha512-7b7ZArhhH0SP6W2R9cqK6RjaU82FZ2UPM7RO8qN1b1wyvC/NY1FNWcX1Pu00fFOAnzEORtwXe4bPaClg6pUybQ=="
},
"duplexify": {
"version": "3.7.1",
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",

View File

@ -76,6 +76,7 @@
"cors": "^2.8.5",
"cross-env": "^7.0.2",
"dagre": "^0.8.5",
"dompurify": "^2.2.6",
"express": "^4.14.0",
"fs-extra": "^9.0.1",
"fuse.js": "^6.4.1",

View File

@ -20,7 +20,7 @@ const ProvisionCard = memo(
const [{ image, ...body }, setBody] = useState({})
const IMAGES_URL = isProvider ? PROVIDER_IMAGES_URL : PROVISION_IMAGES_URL
const { NAME, TEMPLATE: { PLAIN = {}, BODY = {} } } = value
const { ID, NAME, TEMPLATE: { PLAIN = {}, BODY = {} } } = value
const stateInfo = PROVISIONS_STATES[body?.state]
useEffect(() => {
@ -36,7 +36,7 @@ const ProvisionCard = memo(
return (
<SelectCard
title={NAME}
title={`(ID: ${ID}) - ${NAME}`}
isSelected={isSelected}
handleClick={handleClick}
action={actions?.map(action => <Action key={action?.cy} {...action} />)}

View File

@ -68,7 +68,7 @@ const SelectCard = memo(({
}}
title={title}
titleTypographyProps={{
variant: 'body2',
variant: 'body1',
noWrap: true,
className: classes.header,
title

View File

@ -7,7 +7,8 @@ import {
Dialog,
DialogTitle,
DialogContent,
DialogActions
DialogActions,
Typography
} from '@material-ui/core'
import SubmitButton from 'client/components/FormControl/SubmitButton'
@ -18,6 +19,7 @@ const DialogConfirmation = memo(
({
open,
title,
subheader,
contentProps,
handleAccept,
acceptButtonProps,
@ -34,7 +36,7 @@ const DialogConfirmation = memo(
onEntering={handleEntering}
open={open}
onClose={handleCancel}
maxWidth="lg"
maxWidth='lg'
scroll="paper"
PaperProps={{
style: {
@ -43,7 +45,10 @@ const DialogConfirmation = memo(
}
}}
>
<DialogTitle>{title}</DialogTitle>
<DialogTitle disableTypography>
<Typography variant='h6'>{title}</Typography>
{subheader && <Typography variant='subtitle1'>{subheader}</Typography>}
</DialogTitle>
<DialogContent dividers {...contentProps}>
{children}
</DialogContent>
@ -52,7 +57,7 @@ const DialogConfirmation = memo(
{handleCancel && (
<Button
onClick={handleCancel}
data-cy="dg-cancel-button"
data-cy='dg-cancel-button'
{...cancelButtonProps}
>
{Tr(T.Cancel)}
@ -61,7 +66,7 @@ const DialogConfirmation = memo(
{handleAccept && (
<SubmitButton
color='secondary'
data-cy="dg-accept-button"
data-cy='dg-accept-button'
onClick={handleAccept}
label={Tr(T.Accept)}
{...acceptButtonProps}
@ -77,6 +82,7 @@ const DialogConfirmation = memo(
DialogConfirmation.propTypes = {
open: PropTypes.bool.isRequired,
title: PropTypes.string.isRequired,
subheader: PropTypes.string,
contentProps: PropTypes.objectOf(PropTypes.any),
handleAccept: PropTypes.func,
acceptButtonProps: PropTypes.objectOf(PropTypes.any),
@ -92,6 +98,7 @@ DialogConfirmation.propTypes = {
DialogConfirmation.defaultProps = {
open: true,
title: 'Confirmation dialog',
subheader: undefined,
contentProps: undefined,
handleAccept: undefined,
acceptButtonProps: undefined,

View File

@ -2,7 +2,7 @@ import { makeStyles } from '@material-ui/core'
export default makeStyles(theme => ({
footer: {
color: theme.palette.common.white,
color: theme.palette.primary.contrastText,
backgroundColor: theme.palette.primary.light,
position: 'absolute',
bottom: 0,
@ -18,7 +18,7 @@ export default makeStyles(theme => ({
color: theme.palette.error.dark
},
link: {
color: theme.palette.common.white,
color: theme.palette.primary.contrastText,
marginLeft: theme.spacing(1)
}
}))

View File

@ -8,11 +8,12 @@ import { Tr } from 'client/components/HOC'
import ErrorHelper from 'client/components/FormControl/ErrorHelper'
const TextController = memo(
({ control, cy, type, name, label, error, fieldProps }) => (
({ control, cy, type, multiline, name, label, error, fieldProps }) => (
<Controller
render={({ value, ...props }) =>
<TextField
fullWidth
multiline={multiline}
color='secondary'
value={value ?? ''}
type={type}
@ -37,6 +38,7 @@ TextController.propTypes = {
control: PropTypes.object,
cy: PropTypes.string,
type: PropTypes.string,
multiline: PropTypes.bool,
name: PropTypes.string.isRequired,
label: PropTypes.string,
error: PropTypes.oneOfType([
@ -50,6 +52,7 @@ TextController.defaultProps = {
control: {},
cy: 'cy',
type: 'text',
multiline: false,
name: '',
label: '',
error: false,

View File

@ -34,6 +34,7 @@ const FormWithSchema = ({ id, cy, fields }) => {
htmlType,
label,
values,
multiline,
multiple,
dependOf,
tooltip,
@ -66,6 +67,7 @@ const FormWithSchema = ({ id, cy, fields }) => {
name: inputName,
label,
tooltip,
multiline,
multiple,
values: typeof values === 'function'
? values(dependValue)

View File

@ -43,14 +43,16 @@ module.exports = {
/* steps form */
/* steps form - flow */
ApplicationOverview: 'Application Overview',
ApplicationOverview: 'Application overview',
WhereWillItRun: 'Where will it run?',
ConfigureNetworking: 'Configure Networking',
TierDefinition: 'Tier Definition',
ConfigureNetworking: 'Configure networking',
TierDefinition: 'Tier definition',
ConfigureTiers: 'Configure Tiers',
ConfigurePolicies: 'Configure policies',
ConfigureTemplate: 'Configure template',
/* steps form - provision */
ProviderOverview: 'Provider overview',
ProvisionOverview: 'Provision overview',
ConfigureConnection: 'Configure connection',
Location: 'Location',
ProviderTemplate: 'Provider template',

View File

@ -0,0 +1,23 @@
import React, { useCallback } from 'react'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
import { T } from 'client/constants'
import {
FORM_FIELDS, STEP_FORM_SCHEMA
} from 'client/containers/Providers/Form/Create/Steps/BasicConfiguration/schema'
export const STEP_ID = 'configuration'
const BasicConfiguration = ({ isUpdate }) => ({
id: STEP_ID,
label: T.ProviderOverview,
resolver: () => STEP_FORM_SCHEMA({ isUpdate }),
optionsValidate: { abortEarly: false },
content: useCallback(
() => <FormWithSchema cy="form-provider" fields={FORM_FIELDS({ isUpdate })} id={STEP_ID} />,
[]
)
})
export default BasicConfiguration

View File

@ -0,0 +1,35 @@
import * as yup from 'yup'
import { INPUT_TYPES } from 'client/constants'
import { getValidationFromFields } from 'client/utils'
const NAME = {
name: 'name',
label: 'Name',
type: INPUT_TYPES.TEXT,
validation: yup
.string()
.min(1, 'Name field is required')
.trim()
.required('Name field is required')
.default('')
}
const DESCRIPTION = {
name: 'description',
label: 'Description',
type: INPUT_TYPES.TEXT,
multiline: true,
validation: yup
.string()
.trim()
.default('')
}
export const FORM_FIELDS = ({ isUpdate }) => [
!isUpdate && NAME,
DESCRIPTION
].filter(Boolean)
export const STEP_FORM_SCHEMA = ({ isUpdate }) => yup.object(
getValidationFromFields(FORM_FIELDS({ isUpdate }))
)

View File

@ -41,7 +41,7 @@ const Connection = () => ({
connection: { [locationKey]: _, ...connectionEditable } = {}
} = providerTemplate
connection = connectionEditable ?? {}
connection = connectionEditable
setFields(FORM_FIELDS(connection))
// set defaults connection values when first render

View File

@ -1,59 +0,0 @@
import React, { useCallback, useEffect, useState } from 'react'
import { useFormContext } from 'react-hook-form'
import { useProvision } from 'client/hooks'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
import { EmptyCard } from 'client/components/Cards'
import { T } from 'client/constants'
import { STEP_ID as TEMPLATE_ID } from 'client/containers/Providers/Form/Create/Steps/Template'
import {
FORM_FIELDS, STEP_FORM_SCHEMA
} from 'client/containers/Providers/Form/Create/Steps/Inputs/schema'
export const STEP_ID = 'inputs'
let inputs = []
const Inputs = () => ({
id: STEP_ID,
label: T.ConfigureInputs,
resolver: () => STEP_FORM_SCHEMA(inputs),
optionsValidate: { abortEarly: false },
content: useCallback(() => {
const [fields, setFields] = useState([])
const { provisionsTemplates } = useProvision()
const { watch, reset } = useFormContext()
useEffect(() => {
const {
[TEMPLATE_ID]: templateSelected,
[STEP_ID]: currentInputs
} = watch()
const { name, provision, provider } = templateSelected?.[0]
const providerTemplate = provisionsTemplates
?.[provision]
?.providers?.[provider]
?.find(providerSelected => providerSelected.name === name) ?? {}
inputs = providerTemplate?.inputs ?? []
setFields(FORM_FIELDS(inputs))
// set defaults inputs values when first render
!currentInputs && reset({
...watch(),
[STEP_ID]: STEP_FORM_SCHEMA(inputs).default()
})
}, [])
return (fields?.length === 0) ? (
<EmptyCard title={'✔️ There is not inputs to fill'} />
) : (
<FormWithSchema cy="form-provider" fields={fields} id={STEP_ID} />
)
}, [])
})
export default Inputs

View File

@ -1,31 +0,0 @@
import * as yup from 'yup'
import { getValidationFromFields, schemaUserInput } from 'client/utils'
export const FORM_FIELDS = inputs =>
inputs?.map(({
name,
description,
type,
default: defaultValue,
min_value: min,
max_value: max,
options
}) => {
const optionsValue = options?.join(',') ?? `${min}..${max}`
return {
name,
label: `${description ?? name} *`,
...schemaUserInput({
mandatory: true,
name,
type,
options: optionsValue,
defaultValue
})
}
})
export const STEP_FORM_SCHEMA = inputs => yup.object(
getValidationFromFields(FORM_FIELDS(inputs))
)

View File

@ -1,14 +1,15 @@
import React, { useCallback } from 'react'
import { Divider, Select, Breadcrumbs } from '@material-ui/core'
import { Divider, Select, Breadcrumbs, Link } from '@material-ui/core'
import ArrowIcon from '@material-ui/icons/ArrowForwardIosRounded'
import { useProvision, useListForm, useGeneral } from 'client/hooks'
import { ListCards } from 'client/components/List'
import { EmptyCard, ProvisionTemplateCard } from 'client/components/Cards'
import { isExternalURL, sanitize } from 'client/utils'
import { T } from 'client/constants'
import { STEP_ID as CONFIGURATION_ID } from 'client/containers/Providers/Form/Create/Steps/BasicConfiguration'
import { STEP_ID as CONNECTION_ID } from 'client/containers/Providers/Form/Create/Steps/Connection'
import { STEP_ID as INPUTS_ID } from 'client/containers/Providers/Form/Create/Steps/Inputs'
import { STEP_FORM_SCHEMA } from 'client/containers/Providers/Form/Create/Steps/Template/schema'
export const STEP_ID = 'template'
@ -25,6 +26,7 @@ const Template = () => ({
const { showError } = useGeneral()
const { provisionsTemplates } = useProvision()
const provisionSelectedDescription = provisionsTemplates?.[provisionSelected]?.description
const providersTypes = provisionsTemplates?.[provisionSelected]?.providers ?? []
const templatesAvailable = providersTypes?.[providerSelected]
@ -45,14 +47,14 @@ const Template = () => ({
templateSelected && handleClear()
}
const handleClick = ({ name, provider, plain = {} }, isSelected) => {
const handleClick = ({ name, description, provider, plain = {} }, isSelected) => {
const { provision_type: provisionType } = plain
if ([name, provisionType, provider].includes(undefined)) {
showError({ message: 'This template has bad format. Ask your cloud administrator' })
} else {
// reset rest of form when change template
setFormData({ [INPUTS_ID]: undefined, [CONNECTION_ID]: undefined })
setFormData({ [CONFIGURATION_ID]: { name, description }, [CONNECTION_ID]: undefined })
isSelected
? handleUnselect(name, item => item.name === name)
@ -64,8 +66,17 @@ const Template = () => ({
<option key={option} value={option}>{option}</option>
))
const RenderDescription = ({ description = '' }) => (
<p>{(sanitize`${description}`)?.split(' ').map((string, idx) =>
isExternalURL(string)
? <Link key={`link-${idx}`} color='textPrimary' href={string}>{string}</Link>
: ` ${string}`
)}</p>
)
return (
<>
{/* -- SELECTORS -- */}
<Breadcrumbs separator={<ArrowIcon color="secondary" />}>
<Select
color='secondary'
@ -92,12 +103,26 @@ const Template = () => ({
<RenderOptions options={providersTypes} />
</Select>}
</Breadcrumbs>
{/* -- DESCRIPTION -- */}
{React.useMemo(() => provisionSelectedDescription && (
<RenderDescription description={provisionSelectedDescription} />
), [provisionSelectedDescription])}
<Divider style={{ margin: '1rem 0' }} />
{/* -- LIST -- */}
<ListCards
keyProp='name'
list={templatesAvailable}
EmptyComponent={
<EmptyCard title={'Your providers templates list is empty'} />
<EmptyCard title={
!provisionSelected
? 'Please choose your provision type'
: !providerSelected
? 'Please choose your provider type'
: 'Your providers templates list is empty'
} />
}
CardComponent={ProvisionTemplateCard}
cardsProps={({ value = {} }) => {

View File

@ -1,22 +1,22 @@
import * as yup from 'yup'
import Template from './Template'
import BasicConfiguration from './BasicConfiguration'
import Connection from './Connection'
import Inputs from './Inputs'
const Steps = ({ isUpdate }) => {
const template = Template()
const configuration = BasicConfiguration({ isUpdate })
const connection = Connection()
const inputs = Inputs()
const steps = [connection, inputs]
const steps = [configuration, connection]
!isUpdate && steps.unshift(template)
const resolvers = () => yup
.object({
[template.id]: template.resolver(),
[connection.id]: connection.resolver(),
[inputs.id]: inputs.resolver()
[configuration.id]: configuration.resolver(),
[connection.id]: connection.resolver()
})
const defaultValues = resolvers().default()

View File

@ -10,7 +10,6 @@ import Steps from 'client/containers/Providers/Form/Create/Steps'
import { useFetch, useProvision, useGeneral } from 'client/hooks'
import { PATH } from 'client/router/provision'
import { get, mapUserInputs } from 'client/utils'
function ProviderCreateForm () {
const history = useHistory()
@ -52,31 +51,33 @@ function ProviderCreateForm () {
?.find(providerSelected => providerSelected.name === name)
const onSubmit = formData => {
const { template, inputs, connection, registration_time: time } = formData
const { template, configuration, connection, registration_time: time } = formData
const { name, description } = configuration
const templateSelected = template?.[0]
const providerTemplate = getProviderTemplateByDir(templateSelected)
if (!providerTemplate) return redirectWithError(templateSelected?.name)
const parseInputs = mapUserInputs(inputs)
const {
inputs,
name: templateName,
plain,
name,
provider,
location_key: locationKey,
connection: { [locationKey]: connectionFixed }
} = providerTemplate
const formatData = {
...(!isUpdate && { plain, name, provider }),
...(!isUpdate && {
name,
plain,
provider,
inputs,
template: templateName
}),
description,
connection: { ...connection, [locationKey]: connectionFixed },
inputs: providerTemplate?.inputs?.map(input => ({
...input,
value: `${parseInputs[input?.name]}`,
default: `${parseInputs[input?.name]}`
})),
registration_time: time
}
@ -97,29 +98,26 @@ function ProviderCreateForm () {
if (data) {
const {
connection,
inputs,
description,
name,
provider,
template: templateName,
registration_time: time
} = data?.TEMPLATE?.PROVISION_BODY ?? {}
const { provision_type: provisionType } = data?.TEMPLATE?.PLAIN ?? {}
const templateSelected = { name, provision: provisionType, provider }
const templateSelected = { name: templateName, provision: provisionType, provider }
const template = getProviderTemplateByDir(templateSelected)
if (!template) return redirectWithError(name)
if (!template) return redirectWithError(templateName)
const { location_key: locationKey } = template
const { [locationKey]: _, ...connectionEditable } = connection
const inputsNameValue = inputs?.reduce((res, input) => (
{ ...res, [input.name]: input.value }
), {})
methods.reset({
connection: connectionEditable,
inputs: inputsNameValue,
configuration: { name, description },
template: [templateSelected],
registration_time: time
}, { errors: false })

View File

@ -14,6 +14,7 @@ const Info = memo(({ data }) => {
const { ID, NAME, GNAME, UNAME, PERMISSIONS, TEMPLATE } = data
const {
connection,
description,
provider: providerName,
registration_time: time
} = TEMPLATE?.PROVISION_BODY
@ -38,6 +39,10 @@ const Info = memo(({ data }) => {
<Typography>{Tr(T.Name)}</Typography>
<Typography>{NAME}</Typography>
</ListItem>
<ListItem>
<Typography >{Tr(T.Description)}</Typography>
<Typography noWrap>{description}</Typography>
</ListItem>
<ListItem>
<Typography>{Tr(T.Provider)}</Typography>
<Typography>{providerName}</Typography>

View File

@ -47,7 +47,7 @@ const Info = memo(({ data }) => {
</ListItem>
<ListItem>
<Typography>{Tr(T.Description)}</Typography>
<Typography>{description}</Typography>
<Typography noWrap>{description}</Typography>
</ListItem>
<ListItem>
<Typography>{Tr(T.Provider)}</Typography>

View File

@ -0,0 +1,23 @@
import React, { useCallback } from 'react'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
import { T } from 'client/constants'
import {
FORM_FIELDS, STEP_FORM_SCHEMA
} from 'client/containers/Provisions/Form/Create/Steps/BasicConfiguration/schema'
export const STEP_ID = 'configuration'
const BasicConfiguration = () => ({
id: STEP_ID,
label: T.ProvisionOverview,
resolver: () => STEP_FORM_SCHEMA,
optionsValidate: { abortEarly: false },
content: useCallback(
() => <FormWithSchema cy="form-provision" fields={FORM_FIELDS} id={STEP_ID} />,
[]
)
})
export default BasicConfiguration

View File

@ -0,0 +1,32 @@
import * as yup from 'yup'
import { INPUT_TYPES } from 'client/constants'
import { getValidationFromFields } from 'client/utils'
const NAME = {
name: 'name',
label: 'Name',
type: INPUT_TYPES.TEXT,
validation: yup
.string()
.min(1, 'Name field is required')
.trim()
.required('Name field is required')
.default('')
}
const DESCRIPTION = {
name: 'description',
label: 'Description',
type: INPUT_TYPES.TEXT,
multiline: true,
validation: yup
.string()
.trim()
.default('')
}
export const FORM_FIELDS = [NAME, DESCRIPTION]
export const STEP_FORM_SCHEMA = yup.object(
getValidationFromFields(FORM_FIELDS)
)

View File

@ -7,14 +7,12 @@ import { useProvision, useFetch, useGeneral } from 'client/hooks'
import FormWithSchema from 'client/components/Forms/FormWithSchema'
import { EmptyCard } from 'client/components/Cards'
import { T } from 'client/constants'
import { deepmerge } from 'client/utils/merge'
import { STEP_ID as PROVIDER_ID } from 'client/containers/Provisions/Form/Create/Steps/Provider'
import { STEP_ID as TEMPLATE_ID } from 'client/containers/Provisions/Form/Create/Steps/Template'
import {
FORM_FIELDS, STEP_FORM_SCHEMA
} from 'client/containers/Provisions/Form/Create/Steps/Inputs/schema'
import { console } from 'window-or-global'
export const STEP_ID = 'inputs'

View File

@ -1,13 +1,15 @@
import React, { useCallback } from 'react'
import { Divider, Select, Breadcrumbs } from '@material-ui/core'
import { Divider, Select, Breadcrumbs, Link } from '@material-ui/core'
import ArrowIcon from '@material-ui/icons/ArrowForwardIosRounded'
import { useProvision, useListForm, useGeneral } from 'client/hooks'
import { ListCards } from 'client/components/List'
import { EmptyCard, ProvisionTemplateCard } from 'client/components/Cards'
import { isExternalURL, sanitize } from 'client/utils'
import { T } from 'client/constants'
import { STEP_ID as PROVIDER_ID } from 'client/containers/Provisions/Form/Create/Steps/Provider'
import { STEP_ID as CONFIGURATION_ID } from 'client/containers/Provisions/Form/Create/Steps/BasicConfiguration'
import { STEP_ID as INPUTS_ID } from 'client/containers/Provisions/Form/Create/Steps/Inputs'
import { STEP_FORM_SCHEMA } from 'client/containers/Provisions/Form/Create/Steps/Template/schema'
@ -25,6 +27,7 @@ const Template = () => ({
const { showError } = useGeneral()
const { provisionsTemplates, providers } = useProvision()
const provisionSelectedDescription = provisionsTemplates?.[provisionSelected]?.description
const providersTypes = provisionsTemplates?.[provisionSelected]?.provisions ?? []
const templatesAvailable = providersTypes?.[providerSelected] ?? []
@ -46,7 +49,7 @@ const Template = () => ({
}
const handleClick = (template, isSelected) => {
const { name, provision_type: provisionType, provider, defaults, hosts } = template
const { name, description, provision_type: provisionType, provider, defaults, hosts } = template
if ([name, provisionType, provider].includes(undefined)) {
showError({ message: 'This template has bad format. Ask your cloud administrator' })
@ -54,7 +57,11 @@ const Template = () => ({
// reset rest of form when change template
const providerName = defaults?.provision?.provider_name ?? hosts?.[0]?.provision.provider_name
const { ID } = providers?.find(({ NAME }) => NAME === providerName) ?? {}
setFormData({ [INPUTS_ID]: undefined, [PROVIDER_ID]: [ID] })
setFormData({
[PROVIDER_ID]: [ID],
[CONFIGURATION_ID]: { name, description },
[INPUTS_ID]: undefined
})
isSelected
? handleUnselect(name, item => item.name === name)
@ -66,8 +73,17 @@ const Template = () => ({
<option key={option} value={option}>{option}</option>
))
const RenderDescription = ({ description = '' }) => (
<p>{(sanitize`${description}`)?.split(' ').map((string, idx) =>
isExternalURL(string)
? <Link key={`link-${idx}`} color='textPrimary' href={string}>{string}</Link>
: ` ${string}`
)}</p>
)
return (
<>
{/* -- SELECTORS -- */}
<Breadcrumbs separator={<ArrowIcon color="secondary" />}>
<Select
color='secondary'
@ -94,12 +110,26 @@ const Template = () => ({
<RenderOptions options={providersTypes} />
</Select>}
</Breadcrumbs>
{/* -- DESCRIPTION -- */}
{React.useMemo(() => provisionSelectedDescription && (
<RenderDescription description={provisionSelectedDescription} />
), [provisionSelectedDescription])}
<Divider style={{ margin: '1rem 0' }} />
{/* -- LIST -- */}
<ListCards
keyProp='name'
list={templatesAvailable}
EmptyComponent={
<EmptyCard title={'Your providers templates list is empty'} />
<EmptyCard title={
!provisionSelected
? 'Please choose your provision type'
: !providerSelected
? 'Please choose your provider type'
: 'Your provisions templates list is empty'
} />
}
CardComponent={ProvisionTemplateCard}
cardsProps={({ value = {} }) => {

View File

@ -2,19 +2,22 @@ import * as yup from 'yup'
import Template from './Template'
import Provider from './Provider'
import BasicConfiguration from './BasicConfiguration'
import Inputs from './Inputs'
const Steps = () => {
const template = Template()
const provider = Provider()
const configuration = BasicConfiguration()
const inputs = Inputs()
const steps = [template, provider, inputs]
const steps = [template, provider, configuration, inputs]
const resolvers = () => yup
.object({
[template.id]: template.resolver(),
[provider.id]: provider.resolver(),
[configuration.id]: configuration.resolver(),
[inputs.id]: inputs.resolver()
})

View File

@ -47,7 +47,8 @@ function ProvisionCreateForm () {
?.find(provisionTemplate => provisionTemplate.name === name)
const onSubmit = formData => {
const { template, provider, inputs } = formData
const { template, provider, configuration, inputs } = formData
const { name, description } = configuration
const provisionTemplateSelected = template?.[0] ?? {}
const providerIdSelected = provider?.[0]
const providerName = providers?.find(({ ID }) => ID === providerIdSelected)?.NAME
@ -66,8 +67,11 @@ function ProvisionCreateForm () {
}
const parseInputs = mapUserInputs(inputs)
const formatData = {
...provisionTemplate,
name,
description,
inputs: provisionTemplate?.inputs
?.map(input => ({ ...input, value: `${parseInputs[input?.name]}` }))
}

View File

@ -1,7 +1,16 @@
import DOMPurify from 'dompurify'
export const fakeDelay = ms => new Promise(resolve => setTimeout(resolve, ms))
export const isExternalURL = url => RegExp(/^(http|https):/g).test(url)
export function sanitize (strings, ...values) {
const dirty = strings.reduce((prev, next, i) =>
`${prev}${next}${values[i] || ''}`, '')
return DOMPurify.sanitize(dirty)
}
export const addOpacityToColor = (color, opacity) => {
const opacityHex = Math.round(opacity * 255).toString(16)
return `${color}${opacityHex}`