mirror of
https://github.com/OpenNebula/one.git
synced 2025-03-21 14:50:08 +03:00
parent
aa1166ced0
commit
52ccdc307a
@ -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 />}
|
||||
|
@ -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} />}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -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;
|
@ -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)
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}, [])
|
||||
|
@ -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;
|
@ -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)
|
||||
);
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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 =>
|
||||
|
Loading…
x
Reference in New Issue
Block a user