mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-20 10:50:08 +03:00
parent
436613ef23
commit
b119d6a22b
5
src/fireedge/package-lock.json
generated
5
src/fireedge/package-lock.json
generated
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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} />)}
|
||||
|
@ -68,7 +68,7 @@ const SelectCard = memo(({
|
||||
}}
|
||||
title={title}
|
||||
titleTypographyProps={{
|
||||
variant: 'body2',
|
||||
variant: 'body1',
|
||||
noWrap: true,
|
||||
className: classes.header,
|
||||
title
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
}))
|
||||
|
@ -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,
|
||||
|
@ -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)
|
||||
|
@ -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',
|
||||
|
@ -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
|
@ -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 }))
|
||||
)
|
@ -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
|
||||
|
@ -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
|
@ -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))
|
||||
)
|
@ -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 = {} }) => {
|
||||
|
@ -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()
|
||||
|
@ -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 })
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
@ -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)
|
||||
)
|
@ -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'
|
||||
|
||||
|
@ -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 = {} }) => {
|
||||
|
@ -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()
|
||||
})
|
||||
|
||||
|
@ -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]}` }))
|
||||
}
|
||||
|
@ -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}`
|
||||
|
Loading…
x
Reference in New Issue
Block a user