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

F #3951: Add policies & network in role form (#228)

This commit is contained in:
Sergio Betanzos 2020-09-18 17:21:25 +02:00 committed by GitHub
parent aa1166ced0
commit 52ccdc307a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 171 additions and 152 deletions

View File

@ -74,7 +74,7 @@ const ClusterCard = React.memo(
>
<CardActionArea
className={classes.actionArea}
onClick={() => (isSelected ? handleUnselect(ID) : handleSelect(ID))}
onClick={() => (isSelected ? handleUnselect() : handleSelect())}
>
<CardHeader
avatar={<StorageIcon />}

View File

@ -22,7 +22,7 @@ const FormStepper = ({ steps, initialValue, onSubmit }) => {
const disabledBack = useMemo(() => activeStep === FIRST_STEP, [activeStep]);
useEffect(() => {
reset({ ...formData }, { errors: true });
reset({ ...formData }, { errors: false });
}, [formData]);
const handleNext = () => {
@ -31,10 +31,12 @@ const FormStepper = ({ steps, initialValue, onSubmit }) => {
trigger(id).then(isValid => {
if (!isValid) return;
const data = { ...formData, ...watch() };
if (activeStep === lastStep) {
onSubmit(formData);
onSubmit(data);
} else {
setFormData(prevData => ({ ...prevData, ...watch() }));
setFormData(data);
setActiveStep(prevActiveStep => prevActiveStep + 1);
}
});
@ -46,6 +48,12 @@ const FormStepper = ({ steps, initialValue, onSubmit }) => {
setActiveStep(prevActiveStep => prevActiveStep - 1);
}, [activeStep]);
const { id, content: Content } = React.useMemo(() => steps[activeStep], [
formData,
activeStep,
setFormData
]);
return (
<>
{/* STEPPER */}
@ -69,20 +77,10 @@ const FormStepper = ({ steps, initialValue, onSubmit }) => {
/>
)}
{/* FORM CONTENT */}
{React.useMemo(() => {
const { id, content: Content } = steps[activeStep];
return (
Content && (
<>
{typeof errors[id]?.message === 'string' && (
<ErrorHelper label={errors[id]?.message} />
)}
<Content data={formData[id]} setFormData={setFormData} />
</>
)
);
}, [steps, errors, formData, activeStep, setFormData])}
{typeof errors[id]?.message === 'string' && (
<ErrorHelper label={errors[id]?.message} />
)}
{Content && <Content data={formData[id]} setFormData={setFormData} />}
</>
);
};

View File

@ -1,86 +0,0 @@
import React, { useState, useEffect, createElement } from 'react';
import PropTypes from 'prop-types';
import { Fade, Button, IconButton } from '@material-ui/core';
import BackIcon from '@material-ui/icons/ArrowBackIosOutlined';
function ProcessScreen({ screens, id, values, setFormData }) {
const [process, setProcess] = useState(undefined);
useEffect(() => {
const keyValues = Object.keys(values);
if (keyValues.length > 0) {
const currentScreen = keyValues[0];
const index = screens.findIndex(scr => scr.id === currentScreen);
if (index !== -1) setProcess(index);
}
}, []);
const handleSetData = data =>
setFormData(prevData => ({
...prevData,
[id]: data ? { [screens[process]?.id]: data } : undefined
}));
const handleBack = () => {
setProcess(undefined);
handleSetData();
};
return (
<>
{process !== undefined ? (
createElement(screens[process]?.screen, {
backButton: (
<IconButton onClick={handleBack}>
<BackIcon />
</IconButton>
),
handleSetData,
currentValue: values[screens[process]?.id]
})
) : (
<div style={{ flexGrow: 1 }}>
<div
style={{
height: '100%',
display: 'flex',
justifyContent: 'space-around',
alignItems: 'center',
flexWrap: 'wrap'
}}
>
{screens?.map(({ id, button }, index) => (
<Fade in timeout={500} key={`option-${id}`}>
<Button
variant="contained"
style={{ backgroundColor: '#fff' }}
onClick={() => setProcess(index)}
>
{button}
</Button>
</Fade>
))}
</div>
</div>
)}
</>
);
}
ProcessScreen.propTypes = {
screens: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string,
button: PropTypes.element,
screen: PropTypes.func
})
)
};
ProcessScreen.defaultProps = {
screens: []
};
export default ProcessScreen;

View File

@ -2,6 +2,7 @@ import React, { useCallback, useContext } from 'react';
import useListForm from 'client/hooks/useListForm';
import ListCards from 'client/components/List/ListCards';
import { SelectCard } from 'client/components/Cards';
import { STEP_ID as NETWORKING } from 'client/containers/Application/Create/Steps/Networking';
import { Context } from 'client/containers/Application/Create/Steps/Roles';
@ -21,21 +22,17 @@ const Networks = () => ({
setList: setFormData
});
console.log('list', list);
return (
<ListCards
list={list[NETWORKING]}
CardComponent={() => <h1>hi</h1>}
cardsProps={({ value }) => {
const { ID } = value;
return {
isSelected: data?.some(selected => selected === ID),
handleSelect: () => handleSelect(ID),
handleUnselect: () => handleUnselect(ID)
};
}}
CardComponent={SelectCard}
cardsProps={({ value, index }) => ({
ID: String(index),
NAME: value?.name,
isSelected: data?.some(selected => selected === index),
handleSelect: () => handleSelect(index),
handleUnselect: () => handleUnselect(index)
})}
/>
);
}, [])

View File

@ -0,0 +1,21 @@
import React, { useCallback } from 'react';
import FormWithSchema from 'client/components/Forms/FormWithSchema';
import { FORM_FIELDS, STEP_FORM_SCHEMA } from './schema';
export const STEP_ID = 'policies';
const Policies = () => ({
id: STEP_ID,
label: 'Policies',
resolver: STEP_FORM_SCHEMA,
content: useCallback(
() => (
<FormWithSchema cy="form-policies" fields={FORM_FIELDS} id={STEP_ID} />
),
[]
)
});
export default Policies;

View File

@ -0,0 +1,30 @@
import * as yup from 'yup';
import { TYPE_INPUT } from 'client/constants';
import { getValidationFromFields } from 'client/utils/helpers';
export const FORM_FIELDS = [
{
name: 'elasticity',
label: 'Elasticity',
type: TYPE_INPUT.TEXT,
validation: yup
.string()
.trim()
.required()
.default('')
},
{
name: 'scheduled',
label: 'Scheduled',
type: TYPE_INPUT.TEXT,
validation: yup
.string()
.trim()
.required()
.default('')
}
];
export const STEP_FORM_SCHEMA = yup.object(
getValidationFromFields(FORM_FIELDS)
);

View File

@ -1,14 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
function ImportDockerFile({ backButton }) {
return (
<div style={{ display: 'flex', alignItems: 'center' }}>
{backButton}
<h1 style={{ marginLeft: 5, flexGrow: 1 }}>Docker file</h1>
</div>
);
}
const ImportDockerFile = ({ backButton }) => (
<div style={{ display: 'flex', alignItems: 'center' }}>
{backButton}
<h1 style={{ marginLeft: 5, flexGrow: 1 }}>Docker file</h1>
</div>
);
ImportDockerFile.propTypes = {
backButton: PropTypes.node

View File

@ -7,7 +7,7 @@ import { SelectCard } from 'client/components/Cards';
const sortByID = (a, b) => a.ID - b.ID;
function ListMarketApp({ backButton, currentValue, handleSetData }) {
const ListMarketApp = ({ backButton, currentValue, handleSetData }) => {
const { apps, getMarketApps } = useOpennebula();
useEffect(() => {
@ -42,7 +42,7 @@ function ListMarketApp({ backButton, currentValue, handleSetData }) {
}}
/>
);
}
};
ListMarketApp.propTypes = {
backButton: PropTypes.node,

View File

@ -7,7 +7,7 @@ import { SelectCard } from 'client/components/Cards';
const sortByID = (a, b) => a.ID - b.ID;
function ListTemplates({ backButton, currentValue, handleSetData }) {
const ListTemplates = ({ backButton, currentValue, handleSetData }) => {
const { templates, getTemplates } = useOpennebula();
useEffect(() => {
@ -42,7 +42,7 @@ function ListTemplates({ backButton, currentValue, handleSetData }) {
}}
/>
);
}
};
ListTemplates.propTypes = {
backButton: PropTypes.node,

View File

@ -1,11 +1,13 @@
import React, { useCallback } from 'react';
import React, { useCallback, useState, useEffect, useMemo } from 'react';
import TemplateIcon from '@material-ui/icons/InsertDriveFileOutlined';
import MarketplaceIcon from '@material-ui/icons/ShoppingCartOutlined';
import {
ArrowBackIosOutlined as BackIcon,
ShoppingCartOutlined as MarketplaceIcon,
InsertDriveFileOutlined as TemplateIcon
} from '@material-ui/icons';
import { makeStyles, IconButton, Button, Fade } from '@material-ui/core';
import DockerLogo from 'client/icons/docker';
import ProcessScreen from 'client/components/ProcessScreen';
import ListTemplates from './List/Templates';
import ListMarketApps from './List/MarketApps';
import DockerFile from './List/Docker';
@ -17,34 +19,90 @@ const SCREENS = [
{
id: 'template',
button: <TemplateIcon style={{ fontSize: 100 }} />,
screen: ListTemplates
content: ListTemplates
},
{
id: 'app',
button: <MarketplaceIcon style={{ fontSize: 100 }} />,
screen: ListMarketApps
content: ListMarketApps
},
{
id: 'docker',
button: <DockerLogo width="100" height="100%" color="#066da5" />,
screen: DockerFile
content: DockerFile
}
];
const useStyles = makeStyles(() => ({
root: {
flexGrow: 1
},
wrapper: {
height: '100%',
display: 'flex',
justifyContent: 'space-around',
alignItems: 'center',
flexWrap: 'wrap'
},
button: { backgroundColor: '#fff' }
}));
const Template = () => ({
id: STEP_ID,
label: 'Template',
resolver: STEP_FORM_SCHEMA,
content: useCallback(
({ data, setFormData }) =>
ProcessScreen({
screens: SCREENS,
id: STEP_ID,
values: data ?? {},
setFormData
}),
[]
)
content: useCallback(({ data = {}, setFormData }) => {
const classes = useStyles();
const [screen, setScreen] = useState(undefined);
useEffect(() => {
if (Object.keys(data).length > 0) {
const currentScreen = Object.keys(data)[0];
setScreen(SCREENS.find(src => src.id === currentScreen.id));
}
}, []);
const handleSetTemplate = template =>
setFormData(prevData => ({
...prevData,
[STEP_ID]: template ? { [screen.id]: template } : undefined
}));
const handleBack = () => {
setScreen(undefined);
handleSetTemplate();
};
const Content = useMemo(() => screen?.content, [screen]);
return screen !== undefined ? (
<Content
backButton={
<IconButton onClick={handleBack}>
<BackIcon />
</IconButton>
}
handleSetData={handleSetTemplate}
currentValue={data[screen.id]}
/>
) : (
<div className={classes.root}>
<div className={classes.wrapper}>
{SCREENS?.map(scr => (
<Fade in timeout={500} key={`option-${scr.id}`}>
<Button
variant="contained"
className={classes.button}
onClick={() => setScreen(scr)}
>
{scr.button}
</Button>
</Fade>
))}
</div>
</div>
);
}, [])
});
export default Template;

View File

@ -1,20 +1,23 @@
import * as yup from 'yup';
import BasicConfiguration from './BasicConfiguration';
import Networks from './Networks';
import Template from './Template';
// import Policies from './Policies';
import Policies from './Policies';
const Steps = () => {
const basic = BasicConfiguration();
const networks = Networks();
const template = Template();
// const policies = Policies();
const policies = Policies();
const steps = [basic, template];
const steps = [basic, networks, template, policies];
const resolvers = yup.object({
[basic.id]: basic.resolver,
[template.id]: template.resolver
// [policies.id]: policies.resolver
[networks.id]: networks.resolver,
[template.id]: template.resolver,
[policies.id]: policies.resolver
});
const defaultValues = resolvers.default();

View File

@ -6,7 +6,7 @@ const useListSelect = ({ multiple, key, list, setList, defaultValue }) => {
const handleSelect = index =>
setList(prevData => ({
...prevData,
[key]: multiple ? [...prevData[key], index] : [index]
[key]: multiple ? [...(prevData[key] ?? []), index] : [index]
}));
const handleUnselect = indexRemove =>