From aa1166ced021bb86d36a9c17ab979362187510c0 Mon Sep 17 00:00:00 2001 From: Sergio Betanzos Date: Fri, 18 Sep 2020 11:55:21 +0200 Subject: [PATCH] F #3951: Improvement performance (#226) --- .../public/components/FormStepper/FormList.js | 99 ----------- .../components/FormStepper/FormListSelect.js | 69 -------- .../public/components/FormStepper/FormStep.js | 63 ------- .../public/components/FormStepper/index.js | 18 +- .../src/public/components/List/ListCards.js | 2 +- .../Create/Steps/BasicConfiguration/index.js | 25 ++- .../Create/Steps/Clusters/index.js | 54 ++++-- .../Create/Steps/Networking/index.js | 97 +++++++---- .../Create/Steps/Networking/schema.js | 25 +-- .../Roles/Steps/BasicConfiguration/index.js | 25 ++- .../Steps/Roles/Steps/Networks/index.js | 44 +++++ .../Steps/Roles/Steps/Networks/schema.js | 6 + .../Steps/Roles/Steps/Template/index.js | 64 +++---- .../Application/Create/Steps/Roles/index.js | 158 ++++++++++++------ src/fireedge/src/public/hooks/useListForm.js | 61 +++++++ 15 files changed, 397 insertions(+), 413 deletions(-) delete mode 100644 src/fireedge/src/public/components/FormStepper/FormList.js delete mode 100644 src/fireedge/src/public/components/FormStepper/FormListSelect.js delete mode 100644 src/fireedge/src/public/components/FormStepper/FormStep.js create mode 100644 src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Networks/index.js create mode 100644 src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Networks/schema.js create mode 100644 src/fireedge/src/public/hooks/useListForm.js diff --git a/src/fireedge/src/public/components/FormStepper/FormList.js b/src/fireedge/src/public/components/FormStepper/FormList.js deleted file mode 100644 index 64cb392dda..0000000000 --- a/src/fireedge/src/public/components/FormStepper/FormList.js +++ /dev/null @@ -1,99 +0,0 @@ -import React, { useEffect, useState, useMemo } from 'react'; -import PropTypes from 'prop-types'; - -import { useFormContext } from 'react-hook-form'; - -import ErrorHelper from 'client/components/FormControl/ErrorHelper'; - -function FormList({ step, data, setFormData }) { - const { errors } = useFormContext(); - const [dialogFormData, setDialogFormData] = useState({}); - const [showDialog, setShowDialog] = useState(false); - - const { id, preRender, ListComponent, DialogComponent, DEFAULT_DATA } = step; - - useEffect(() => { - if (preRender) preRender(); - }, []); - - const handleSubmit = values => { - setFormData(prevData => ({ - ...prevData, - [id]: Object.assign(prevData[id], { - [dialogFormData.index]: values - }) - })); - - setShowDialog(false); - }; - - const handleOpen = (index = data?.length) => { - const openData = data[index] ?? DEFAULT_DATA; - - setDialogFormData({ index, data: openData }); - setShowDialog(true); - }; - - const handleClone = index => { - const item = data[index]; - const cloneItem = { ...item, name: `${item?.name}_clone` }; - const cloneData = [...data]; - cloneData.splice(index + 1, 0, cloneItem); - - setFormData(prevData => ({ ...prevData, [id]: cloneData })); - }; - - const handleRemove = indexRemove => { - // TODO confirmation?? - setFormData(prevData => ({ - ...prevData, - [id]: prevData[id]?.filter((_, index) => index !== indexRemove) - })); - }; - - const handleClose = () => setShowDialog(false); - - return ( - <> - {typeof errors[id]?.message === 'string' && ( - - )} - {useMemo( - () => ( - handleOpen()} - itemsProps={({ index }) => ({ - handleEdit: () => handleOpen(index), - handleClone: () => handleClone(index), - handleRemove: () => handleRemove(index) - })} - /> - ), - [data, handleOpen, handleClone, handleRemove] - )} - {showDialog && DialogComponent && ( - - )} - - ); -} - -FormList.propTypes = { - step: PropTypes.objectOf(PropTypes.any).isRequired, - data: PropTypes.arrayOf(PropTypes.object).isRequired, - setFormData: PropTypes.func.isRequired -}; - -FormList.defaultProps = { - step: {}, - data: [], - setFormData: data => data -}; - -export default FormList; diff --git a/src/fireedge/src/public/components/FormStepper/FormListSelect.js b/src/fireedge/src/public/components/FormStepper/FormListSelect.js deleted file mode 100644 index 1cb806cbf9..0000000000 --- a/src/fireedge/src/public/components/FormStepper/FormListSelect.js +++ /dev/null @@ -1,69 +0,0 @@ -import React, { useEffect } from 'react'; -import PropTypes from 'prop-types'; - -import { Grid } from '@material-ui/core'; -import { useFormContext } from 'react-hook-form'; - -import ErrorHelper from 'client/components/FormControl/ErrorHelper'; -import { EmptyCard } from 'client/components/Cards'; - -function FormListSelect({ step, data, setFormData }) { - const { errors } = useFormContext(); - const { id, multiple, preRender, list, ItemComponent } = step; - - useEffect(() => { - if (preRender) preRender(); - }, []); - - const handleSelect = index => - setFormData(prevData => ({ - ...prevData, - [id]: multiple ? [...prevData[id], index] : [index] - })); - - const handleUnselect = indexRemove => - setFormData(prevData => ({ - ...prevData, - [id]: prevData[id]?.filter(index => index !== indexRemove) - })); - - return ( - - {typeof errors[id]?.message === 'string' && ( - - - - )} - {list?.length === 0 ? ( - - - - ) : ( - list?.map((info, index) => ( - - selected === info?.ID)} - handleSelect={handleSelect} - handleUnselect={handleUnselect} - /> - - )) - )} - - ); -} - -FormListSelect.propTypes = { - step: PropTypes.objectOf(PropTypes.any).isRequired, - data: PropTypes.arrayOf(PropTypes.any).isRequired, - setFormData: PropTypes.func.isRequired -}; - -FormListSelect.defaultProps = { - step: {}, - data: [], - setFormData: data => data -}; - -export default FormListSelect; diff --git a/src/fireedge/src/public/components/FormStepper/FormStep.js b/src/fireedge/src/public/components/FormStepper/FormStep.js deleted file mode 100644 index 54011c5a68..0000000000 --- a/src/fireedge/src/public/components/FormStepper/FormStep.js +++ /dev/null @@ -1,63 +0,0 @@ -import React, { useEffect, useState, useMemo } from 'react'; -import PropTypes from 'prop-types'; - -import { useFormContext } from 'react-hook-form'; - -import ErrorHelper from 'client/components/FormControl/ErrorHelper'; - -function FormStep({ step, data, setFormData }) { - const { errors } = useFormContext(); - const [showDialog, setShowDialog] = useState(false); - const { id, preRender, FormComponent, DialogComponent } = step; - - useEffect(() => { - if (preRender) preRender(); - }, []); - - const handleOpen = () => setShowDialog(true); - const handleClose = () => setShowDialog(false); - - return ( - <> - {typeof errors[id]?.message === 'string' && ( - - )} - {useMemo( - () => ( - - ), - [id, handleOpen, setFormData, data] - )} - {showDialog && DialogComponent && ( - - )} - - ); -} - -FormStep.propTypes = { - step: PropTypes.objectOf(PropTypes.any).isRequired, - data: PropTypes.oneOfType([ - PropTypes.array, - PropTypes.object, - PropTypes.string - ]).isRequired, - setFormData: PropTypes.func.isRequired -}; - -FormStep.defaultProps = { - step: {}, - data: {}, - setFormData: data => data -}; - -export default FormStep; diff --git a/src/fireedge/src/public/components/FormStepper/index.js b/src/fireedge/src/public/components/FormStepper/index.js index 595d89cd3a..f6e7972436 100644 --- a/src/fireedge/src/public/components/FormStepper/index.js +++ b/src/fireedge/src/public/components/FormStepper/index.js @@ -6,12 +6,13 @@ import { useMediaQuery } from '@material-ui/core'; import CustomMobileStepper from 'client/components/FormStepper/MobileStepper'; import CustomStepper from 'client/components/FormStepper/Stepper'; +import ErrorHelper from 'client/components/FormControl/ErrorHelper'; const FIRST_STEP = 0; const FormStepper = ({ steps, initialValue, onSubmit }) => { const isMobile = useMediaQuery(theme => theme.breakpoints.only('xs')); - const { watch, trigger, reset } = useFormContext(); + const { watch, trigger, reset, errors } = useFormContext(); const [activeStep, setActiveStep] = useState(FIRST_STEP); const [formData, setFormData] = useState(initialValue); @@ -73,14 +74,15 @@ const FormStepper = ({ steps, initialValue, onSubmit }) => { return ( Content && ( - + <> + {typeof errors[id]?.message === 'string' && ( + + )} + + ) ); - }, [steps, formData, activeStep, setFormData])} + }, [steps, errors, formData, activeStep, setFormData])} ); }; @@ -90,7 +92,7 @@ FormStepper.propTypes = { PropTypes.shape({ id: PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired, label: PropTypes.string.isRequired, - content: PropTypes.func.isRequired + content: PropTypes.any.isRequired }) ), initialValue: PropTypes.objectOf(PropTypes.any), diff --git a/src/fireedge/src/public/components/List/ListCards.js b/src/fireedge/src/public/components/List/ListCards.js index 3cc3b8cec1..616b78e6d9 100644 --- a/src/fireedge/src/public/components/List/ListCards.js +++ b/src/fireedge/src/public/components/List/ListCards.js @@ -42,7 +42,7 @@ function ListCards({ handleCreate, list, CardComponent, cardsProps }) { {Array.isArray(list) && list?.map((value, index) => ( - + ))} diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/BasicConfiguration/index.js b/src/fireedge/src/public/containers/Application/Create/Steps/BasicConfiguration/index.js index babd591159..493c3ff58d 100644 --- a/src/fireedge/src/public/containers/Application/Create/Steps/BasicConfiguration/index.js +++ b/src/fireedge/src/public/containers/Application/Create/Steps/BasicConfiguration/index.js @@ -1,22 +1,19 @@ -import React from 'react'; +import React, { useCallback } from 'react'; -import FormStep from 'client/components/FormStepper/FormStep'; import FormWithSchema from 'client/components/Forms/FormWithSchema'; import { FORM_FIELDS, STEP_FORM_SCHEMA } from './schema'; -const BasicConfiguration = () => { - const STEP_ID = 'application'; +export const STEP_ID = 'application'; - return { - id: STEP_ID, - label: 'Application Overview', - content: FormStep, - resolver: STEP_FORM_SCHEMA, - FormComponent: () => ( - - ) - }; -}; +const BasicConfiguration = () => ({ + id: STEP_ID, + label: 'Application Overview', + resolver: STEP_FORM_SCHEMA, + content: useCallback( + () => , + [] + ) +}); export default BasicConfiguration; diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Clusters/index.js b/src/fireedge/src/public/containers/Application/Create/Steps/Clusters/index.js index b67b45358a..84705c7ebd 100644 --- a/src/fireedge/src/public/containers/Application/Create/Steps/Clusters/index.js +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Clusters/index.js @@ -1,28 +1,46 @@ -import { useMemo } from 'react'; +import React, { useEffect, useCallback } from 'react'; import useOpennebula from 'client/hooks/useOpennebula'; +import useListForm from 'client/hooks/useListForm'; +import ListCards from 'client/components/List/ListCards'; import { ClusterCard } from 'client/components/Cards'; -import FormListSelect from 'client/components/FormStepper/FormListSelect'; import { STEP_FORM_SCHEMA } from './schema'; -const Clusters = () => { - const STEP_ID = 'clusters'; - const { clusters, getClusters } = useOpennebula(); +export const STEP_ID = 'clusters'; - return useMemo( - () => ({ - id: STEP_ID, - label: 'Where will it run?', - content: FormListSelect, - resolver: STEP_FORM_SCHEMA, - preRender: getClusters, - list: clusters?.sort((a, b) => a.ID - b.ID), - ItemComponent: ClusterCard - }), - [getClusters, clusters] - ); -}; +const Clusters = () => ({ + id: STEP_ID, + label: 'Where will it run?', + resolver: STEP_FORM_SCHEMA, + content: useCallback(({ data, setFormData }) => { + const { clusters, getClusters } = useOpennebula(); + const { handleSelect, handleUnselect } = useListForm({ + key: STEP_ID, + setList: setFormData + }); + + useEffect(() => { + getClusters(); + }, []); + + return ( + { + const { ID } = value; + + return { + isSelected: data?.some(selected => selected === ID), + handleSelect: () => handleSelect(ID), + handleUnselect: () => handleUnselect(ID) + }; + }} + /> + ); + }, []) +}); export default Clusters; diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Networking/index.js b/src/fireedge/src/public/containers/Application/Create/Steps/Networking/index.js index 759d0758d4..34e64420cb 100644 --- a/src/fireedge/src/public/containers/Application/Create/Steps/Networking/index.js +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Networking/index.js @@ -1,52 +1,79 @@ -import React, { useMemo } from 'react'; +import React, { useState, useEffect, useCallback } from 'react'; import useOpennebula from 'client/hooks/useOpennebula'; import { DialogForm } from 'client/components/Dialogs'; import { NetworkCard } from 'client/components/Cards'; -import FormList from 'client/components/FormStepper/FormList'; +import useListForm from 'client/hooks/useListForm'; import FormWithSchema from 'client/components/Forms/FormWithSchema'; import ListCards from 'client/components/List/ListCards'; import { FORM_FIELDS, NETWORK_FORM_SCHEMA, STEP_FORM_SCHEMA } from './schema'; -const Networks = () => { - const STEP_ID = 'networking'; - const { getVNetworks, getVNetworksTemplates } = useOpennebula(); +export const STEP_ID = 'networking'; - return useMemo( - () => ({ - id: STEP_ID, - label: 'Configure Networking', - content: FormList, - preRender: () => { - getVNetworks(); - getVNetworksTemplates(); - }, - resolver: STEP_FORM_SCHEMA, - DEFAULT_DATA: NETWORK_FORM_SCHEMA.default(), - ListComponent: ({ list, handleCreate, itemsProps }) => ( +const Networks = () => ({ + id: STEP_ID, + label: 'Configure Networking', + resolver: STEP_FORM_SCHEMA, + content: useCallback(({ data, setFormData }) => { + const [showDialog, setShowDialog] = useState(false); + const { getVNetworks, getVNetworksTemplates } = useOpennebula(); + const { + editingData, + handleSave, + handleEdit, + handleClone, + handleRemove + } = useListForm({ + key: STEP_ID, + list: data, + setList: setFormData, + defaultValue: NETWORK_FORM_SCHEMA.default() + }); + + useEffect(() => { + getVNetworks(); + getVNetworksTemplates(); + }, []); + + return ( + <> { + handleEdit(); + setShowDialog(true); + }} + cardsProps={({ index }) => ({ + handleEdit: () => { + handleEdit(index); + setShowDialog(true); + }, + handleClone: () => handleClone(index), + handleRemove: () => handleRemove(index) + })} /> - ), - ItemComponent: NetworkCard, - DialogComponent: props => ( - - - - ) - }), - [getVNetworks, getVNetworksTemplates] - ); -}; + {showDialog && ( + { + handleSave(values); + setShowDialog(false); + }} + onCancel={() => setShowDialog(false)} + > + + + )} + + ); + }, []) +}); export default Networks; diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Networking/schema.js b/src/fireedge/src/public/containers/Application/Create/Steps/Networking/schema.js index b411b3b373..da7757b928 100644 --- a/src/fireedge/src/public/containers/Application/Create/Steps/Networking/schema.js +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Networking/schema.js @@ -81,22 +81,15 @@ export const FORM_FIELDS = [ }, validation: yup .string() - .when('type', { - is: type => - TYPES_NETWORKS.some( - ({ value, select }) => type === value && select === SELECT.network - ), - then: yup - .string() - .trim() - .required('Network is required field'), - otherwise: yup - .string() - .trim() - .required('Network template is required field') - }) - .required() - .default(null) + .trim() + .when('type', (type, schema) => + TYPES_NETWORKS.some( + ({ value, select }) => type === value && select === SELECT.network + ) + ? schema.required('Network is required field') + : schema.required('Network template is required field') + ) + .default(undefined) }, { name: 'extra', diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/BasicConfiguration/index.js b/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/BasicConfiguration/index.js index 27704c53d5..bf704fde68 100644 --- a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/BasicConfiguration/index.js +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/BasicConfiguration/index.js @@ -1,22 +1,19 @@ -import React from 'react'; +import React, { useCallback } from 'react'; -import FormStep from 'client/components/FormStepper/FormStep'; import FormWithSchema from 'client/components/Forms/FormWithSchema'; import { FORM_FIELDS, STEP_FORM_SCHEMA } from './schema'; -const BasicConfiguration = () => { - const STEP_ID = 'role'; +export const STEP_ID = 'role'; - return { - id: STEP_ID, - label: 'Configuration', - content: FormStep, - resolver: STEP_FORM_SCHEMA, - FormComponent: () => ( - - ) - }; -}; +const BasicConfiguration = () => ({ + id: STEP_ID, + label: 'Configuration', + resolver: STEP_FORM_SCHEMA, + content: useCallback( + () => , + [] + ) +}); export default BasicConfiguration; diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Networks/index.js b/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Networks/index.js new file mode 100644 index 0000000000..7e10120cf6 --- /dev/null +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Networks/index.js @@ -0,0 +1,44 @@ +import React, { useCallback, useContext } from 'react'; + +import useListForm from 'client/hooks/useListForm'; +import ListCards from 'client/components/List/ListCards'; + +import { STEP_ID as NETWORKING } from 'client/containers/Application/Create/Steps/Networking'; +import { Context } from 'client/containers/Application/Create/Steps/Roles'; +import { STEP_FORM_SCHEMA } from './schema'; + +export const STEP_ID = 'networks'; + +const Networks = () => ({ + id: STEP_ID, + label: 'Networks', + resolver: STEP_FORM_SCHEMA, + content: useCallback(({ data, setFormData }) => { + const { nestedForm: list } = useContext(Context); + const { handleSelect, handleUnselect } = useListForm({ + key: STEP_ID, + multiple: true, + setList: setFormData + }); + + console.log('list', list); + + return ( +

hi

} + cardsProps={({ value }) => { + const { ID } = value; + + return { + isSelected: data?.some(selected => selected === ID), + handleSelect: () => handleSelect(ID), + handleUnselect: () => handleUnselect(ID) + }; + }} + /> + ); + }, []) +}); + +export default Networks; diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Networks/schema.js b/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Networks/schema.js new file mode 100644 index 0000000000..cff9aa26b9 --- /dev/null +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Networks/schema.js @@ -0,0 +1,6 @@ +import * as yup from 'yup'; + +export const STEP_FORM_SCHEMA = yup + .array() + .of(yup.string().trim()) + .default(undefined); diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/index.js b/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/index.js index 6d9edd92df..259291748d 100644 --- a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/index.js +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/index.js @@ -1,44 +1,50 @@ -import React from 'react'; +import React, { useCallback } from 'react'; import TemplateIcon from '@material-ui/icons/InsertDriveFileOutlined'; import MarketplaceIcon from '@material-ui/icons/ShoppingCartOutlined'; import DockerLogo from 'client/icons/docker'; import ProcessScreen from 'client/components/ProcessScreen'; -import FormStep from 'client/components/FormStepper/FormStep'; import ListTemplates from './List/Templates'; import ListMarketApps from './List/MarketApps'; import DockerFile from './List/Docker'; import { STEP_FORM_SCHEMA } from './schema'; -const Template = () => { - const STEP_ID = 'template'; - const SCREENS = [ - { - id: 'template', - button: , - screen: ListTemplates - }, - { - id: 'app', - button: , - screen: ListMarketApps - }, - { - id: 'docker', - button: , - screen: DockerFile - } - ]; +export const STEP_ID = 'template'; - return { - id: STEP_ID, - label: 'Template', - content: FormStep, - resolver: STEP_FORM_SCHEMA, - FormComponent: props => ProcessScreen({ screens: SCREENS, ...props }) - }; -}; +const SCREENS = [ + { + id: 'template', + button: , + screen: ListTemplates + }, + { + id: 'app', + button: , + screen: ListMarketApps + }, + { + id: 'docker', + button: , + screen: DockerFile + } +]; + +const Template = () => ({ + id: STEP_ID, + label: 'Template', + resolver: STEP_FORM_SCHEMA, + content: useCallback( + ({ data, setFormData }) => + ProcessScreen({ + screens: SCREENS, + id: STEP_ID, + values: data ?? {}, + setFormData + }), + [] + ) +}); export default Template; diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/index.js b/src/fireedge/src/public/containers/Application/Create/Steps/Roles/index.js index ac6fc436e4..4b421d9f5d 100644 --- a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/index.js +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Roles/index.js @@ -1,61 +1,125 @@ -import React, { useMemo } from 'react'; -import * as yup from 'yup'; +import React, { useEffect, useState, useCallback } from 'react'; +import * as yup from 'yup'; +import { useWatch } from 'react-hook-form'; + +import useListForm from 'client/hooks/useListForm'; import FormStepper from 'client/components/FormStepper'; import { DialogForm } from 'client/components/Dialogs'; -import FormList from 'client/components/FormStepper/FormList'; import FlowWithFAB from 'client/components/Flows/FlowWithFAB'; import Steps from './Steps'; +export const Context = React.createContext({}); +export const STEP_ID = 'tiers'; + const Roles = () => { - const STEP_ID = 'tiers'; const { steps, defaultValues, resolvers } = Steps(); - return useMemo( - () => ({ - id: STEP_ID, - label: 'Tier Definition', - content: FormList, - DEFAULT_DATA: defaultValues, - resolver: yup - .array() - .of(resolvers) - .min(1) - .required() - .default([]), - ListComponent: ({ list, handleCreate }) => ( -
- -
{JSON.stringify(list)}
-
- ), - DialogComponent: ({ values, onSubmit, onCancel, ...props }) => ( - -
- + return { + id: STEP_ID, + label: 'Tier Definition', + DEFAULT_DATA: defaultValues, + resolver: yup + .array() + .of(resolvers) + .min(1) + .required() + .default([]), + content: useCallback(({ data, setFormData }) => { + const [showDialog, setShowDialog] = useState(false); + const [nestedForm, setNestedForm] = useState({}); + const form = useWatch({}); + + const { editingData, handleEdit, handleSave } = useListForm({ + key: STEP_ID, + list: data, + setList: setFormData, + defaultValue: defaultValues + }); + + useEffect(() => { + setNestedForm(form); + }, []); + + return ( + <> +
+ +
{JSON.stringify(data)}
- - ) - }), - [] - ); + {showDialog && ( + + setShowDialog(false)} + > +
+ { + handleSave(values); + setShowDialog(false); + }} + /> +
+
+
+ )} + + ); + }, []) + /* DialogComponent: ({ values, onSubmit, onCancel, ...props }) => { + const form = useWatch({}); + const [nestedForm, setNestedForm] = useState({}); + + useEffect(() => { + setNestedForm(form); + }, []); + + return ( + + +
+ +
+
+
+ ); */ + }; }; export default Roles; diff --git a/src/fireedge/src/public/hooks/useListForm.js b/src/fireedge/src/public/hooks/useListForm.js new file mode 100644 index 0000000000..1ec736e1e6 --- /dev/null +++ b/src/fireedge/src/public/hooks/useListForm.js @@ -0,0 +1,61 @@ +import { useState } from 'react'; + +const useListSelect = ({ multiple, key, list, setList, defaultValue }) => { + const [editingData, setEditingData] = useState({}); + + const handleSelect = index => + setList(prevData => ({ + ...prevData, + [key]: multiple ? [...prevData[key], index] : [index] + })); + + const handleUnselect = indexRemove => + setList(prevData => ({ + ...prevData, + [key]: prevData[key]?.filter(index => index !== indexRemove) + })); + + const handleSave = values => { + setList(prevData => ({ + ...prevData, + [key]: Object.assign(prevData[key], { + [editingData.index]: values + }) + })); + }; + + const handleEdit = (index = list?.length) => { + const openData = list[index] ?? defaultValue; + + setEditingData({ index, data: openData }); + }; + + const handleClone = index => { + const item = list[index]; + const cloneItem = { ...item, name: `${item?.name}_clone` }; + const cloneData = [...list]; + cloneData.splice(index + 1, 0, cloneItem); + + setList(prevData => ({ ...prevData, [key]: cloneData })); + }; + + const handleRemove = indexRemove => { + // TODO confirmation?? + setList(prevData => ({ + ...prevData, + [key]: prevData[key]?.filter((_, index) => index !== indexRemove) + })); + }; + + return { + editingData, + handleSelect, + handleUnselect, + handleSave, + handleEdit, + handleClone, + handleRemove + }; +}; + +export default useListSelect;