From fe8bfcaf0b0c00fb4563d9811ab040dfb4444a4f Mon Sep 17 00:00:00 2001 From: Sergio Betanzos Date: Mon, 21 Sep 2020 18:52:28 +0200 Subject: [PATCH] F #3951: Add tiers flow graph (#234) --- src/fireedge/package.json | 1 + .../src/public/components/Cards/RoleCard.js | 98 ----------- .../src/public/components/Cards/TierCard.js | 89 ++++++++++ .../src/public/components/Cards/index.js | 4 +- .../src/public/components/SpeedDials/index.js | 72 ++++++++ .../Application/Create/Steps/Roles/index.js | 125 -------------- .../Create/Steps/Tiers/Flow/CustomNode.js | 46 +++++ .../Steps/BasicConfiguration/index.js | 2 +- .../Steps/BasicConfiguration/schema.js | 0 .../{Roles => Tiers}/Steps/Networks/index.js | 2 +- .../{Roles => Tiers}/Steps/Networks/schema.js | 0 .../{Roles => Tiers}/Steps/Policies/index.js | 0 .../{Roles => Tiers}/Steps/Policies/schema.js | 0 .../Steps/Template/List/Docker.js | 0 .../Steps/Template/List/MarketApps.js | 0 .../Steps/Template/List/Templates.js | 0 .../{Roles => Tiers}/Steps/Template/index.js | 2 +- .../{Roles => Tiers}/Steps/Template/schema.js | 0 .../Steps/{Roles => Tiers}/Steps/index.js | 0 .../Application/Create/Steps/Tiers/index.js | 158 ++++++++++++++++++ .../Application/Create/Steps/index.js | 8 +- src/fireedge/src/public/hooks/useList.js | 8 +- src/fireedge/src/public/hooks/useListForm.js | 92 ++++++---- src/fireedge/src/public/utils/flow.js | 44 +++++ 24 files changed, 477 insertions(+), 274 deletions(-) delete mode 100644 src/fireedge/src/public/components/Cards/RoleCard.js create mode 100644 src/fireedge/src/public/components/Cards/TierCard.js create mode 100644 src/fireedge/src/public/components/SpeedDials/index.js delete mode 100644 src/fireedge/src/public/containers/Application/Create/Steps/Roles/index.js create mode 100644 src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Flow/CustomNode.js rename src/fireedge/src/public/containers/Application/Create/Steps/{Roles => Tiers}/Steps/BasicConfiguration/index.js (93%) rename src/fireedge/src/public/containers/Application/Create/Steps/{Roles => Tiers}/Steps/BasicConfiguration/schema.js (100%) rename src/fireedge/src/public/containers/Application/Create/Steps/{Roles => Tiers}/Steps/Networks/index.js (99%) rename src/fireedge/src/public/containers/Application/Create/Steps/{Roles => Tiers}/Steps/Networks/schema.js (100%) rename src/fireedge/src/public/containers/Application/Create/Steps/{Roles => Tiers}/Steps/Policies/index.js (100%) rename src/fireedge/src/public/containers/Application/Create/Steps/{Roles => Tiers}/Steps/Policies/schema.js (100%) rename src/fireedge/src/public/containers/Application/Create/Steps/{Roles => Tiers}/Steps/Template/List/Docker.js (100%) rename src/fireedge/src/public/containers/Application/Create/Steps/{Roles => Tiers}/Steps/Template/List/MarketApps.js (100%) rename src/fireedge/src/public/containers/Application/Create/Steps/{Roles => Tiers}/Steps/Template/List/Templates.js (100%) rename src/fireedge/src/public/containers/Application/Create/Steps/{Roles => Tiers}/Steps/Template/index.js (97%) rename src/fireedge/src/public/containers/Application/Create/Steps/{Roles => Tiers}/Steps/Template/schema.js (100%) rename src/fireedge/src/public/containers/Application/Create/Steps/{Roles => Tiers}/Steps/index.js (100%) create mode 100644 src/fireedge/src/public/containers/Application/Create/Steps/Tiers/index.js create mode 100644 src/fireedge/src/public/utils/flow.js diff --git a/src/fireedge/package.json b/src/fireedge/package.json index 8a1ccbf9b9..f04f30550d 100644 --- a/src/fireedge/package.json +++ b/src/fireedge/package.json @@ -41,6 +41,7 @@ "compression": "^1.7.4", "concurrently": "^5.2.0", "cors": "^2.8.5", + "dagre": "^0.8.5", "express": "^4.17.1", "fs-extra": "^9.0.1", "fuse.js": "^6.4.1", diff --git a/src/fireedge/src/public/components/Cards/RoleCard.js b/src/fireedge/src/public/components/Cards/RoleCard.js deleted file mode 100644 index d6209ea453..0000000000 --- a/src/fireedge/src/public/components/Cards/RoleCard.js +++ /dev/null @@ -1,98 +0,0 @@ -import React from 'react'; - -import { - makeStyles, - Card, - Button, - CardHeader, - CardActions, - Badge, - Fade -} from '@material-ui/core'; -import DesktopWindowsIcon from '@material-ui/icons/DesktopWindows'; - -import { Tr } from 'client/components/HOC'; - -const useStyles = makeStyles(theme => ({ - root: { - height: '100%', - minHeight: 140, - display: 'flex', - flexDirection: 'column' - }, - header: { - overflowX: 'hidden', - flexGrow: 1 - }, - headerContent: {}, - title: { - overflow: 'hidden', - textOverflow: 'ellipsis', - whiteSpace: 'initial', - display: '-webkit-box', - lineClamp: 2, - boxOrient: 'vertical' - } -})); - -const RoleCard = React.memo( - ({ info, handleEdit, handleClone, handleRemove }) => { - const classes = useStyles(); - const { - name = 'Role name', - cardinality, - vm_template = 0, - elasticity_policies, - scheduled_policies - } = info; - - return ( - - - - - - } - className={classes.header} - classes={{ content: classes.headerContent }} - title={name} - titleTypographyProps={{ - variant: 'body2', - noWrap: true, - className: classes.title, - title: name - }} - subheader={`Template id: ${vm_template}`} - subheaderTypographyProps={{ - variant: 'body2', - noWrap: true, - title: `Template id: ${vm_template}` - }} - /> - - - - - - - - ); - } -); - -export default RoleCard; diff --git a/src/fireedge/src/public/components/Cards/TierCard.js b/src/fireedge/src/public/components/Cards/TierCard.js new file mode 100644 index 0000000000..4d982b872c --- /dev/null +++ b/src/fireedge/src/public/components/Cards/TierCard.js @@ -0,0 +1,89 @@ +import React from 'react'; + +import { + makeStyles, + Card, + Button, + CardHeader, + CardActions, + Badge +} from '@material-ui/core'; +import DesktopWindowsIcon from '@material-ui/icons/DesktopWindows'; + +import { Tr } from 'client/components/HOC'; + +const useStyles = makeStyles(() => ({ + root: { + height: '100%', + minHeight: 140, + display: 'flex', + flexDirection: 'column' + }, + header: { + overflowX: 'hidden', + flexGrow: 1 + }, + headerContent: {}, + title: { + overflow: 'hidden', + textOverflow: 'ellipsis', + whiteSpace: 'initial', + display: '-webkit-box', + lineClamp: 2, + boxOrient: 'vertical' + } +})); + +const TierCard = React.memo( + ({ values, handleEdit, handleClone, handleRemove, cardProps }) => { + const classes = useStyles(); + const { name = 'Tier name', cardinality } = values; + + return ( + + + + + } + className={classes.header} + classes={{ content: classes.headerContent }} + title={name} + titleTypographyProps={{ + variant: 'body2', + noWrap: true, + className: classes.title, + title: name + }} + /> + + {handleEdit && ( + + )} + {handleClone && ( + + )} + {handleRemove && ( + + )} + + + ); + } +); + +export default TierCard; diff --git a/src/fireedge/src/public/components/Cards/index.js b/src/fireedge/src/public/components/Cards/index.js index 0b168c2cd4..b6caee1ede 100644 --- a/src/fireedge/src/public/components/Cards/index.js +++ b/src/fireedge/src/public/components/Cards/index.js @@ -1,7 +1,7 @@ import ClusterCard from 'client/components/Cards/ClusterCard'; import NetworkCard from 'client/components/Cards/NetworkCard'; -import RoleCard from 'client/components/Cards/RoleCard'; +import TierCard from 'client/components/Cards/TierCard'; import EmptyCard from 'client/components/Cards/EmptyCard'; import SelectCard from 'client/components/Cards/SelectCard'; -export { ClusterCard, NetworkCard, RoleCard, EmptyCard, SelectCard }; +export { ClusterCard, NetworkCard, TierCard, EmptyCard, SelectCard }; diff --git a/src/fireedge/src/public/components/SpeedDials/index.js b/src/fireedge/src/public/components/SpeedDials/index.js new file mode 100644 index 0000000000..5acecdbe0b --- /dev/null +++ b/src/fireedge/src/public/components/SpeedDials/index.js @@ -0,0 +1,72 @@ +import React from 'react'; +import PropTypes from 'prop-types'; + +import { SpeedDial, SpeedDialIcon, SpeedDialAction } from '@material-ui/lab'; +import { makeStyles } from '@material-ui/core/styles'; + +const useStyles = makeStyles(theme => ({ + root: { + position: 'absolute', + '&.MuiSpeedDial-directionUp, &.MuiSpeedDial-directionLeft': { + bottom: theme.spacing(2), + right: theme.spacing(2) + }, + '&.MuiSpeedDial-directionDown, &.MuiSpeedDial-directionRight': { + top: theme.spacing(2), + left: theme.spacing(2) + } + } +})); + +const SpeedDials = ({ hidden = false, actions = [] }) => { + const classes = useStyles(); + const [open, setOpen] = React.useState(false); + + const handleClose = () => { + setOpen(false); + }; + + const handleOpen = () => { + setOpen(true); + }; + + return ( + + ); +}; + +SpeedDials.propTypes = { + hidden: PropTypes.bool, + actions: PropTypes.arrayOf( + PropTypes.shape({ + name: PropTypes.string.isRequired, + icon: PropTypes.node.isRequired, + handleClick: PropTypes.func + }) + ) +}; + +SpeedDials.defaultProps = { + hidden: false, + actions: [] +}; + +export default SpeedDials; 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 deleted file mode 100644 index 4b421d9f5d..0000000000 --- a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/index.js +++ /dev/null @@ -1,125 +0,0 @@ -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 FlowWithFAB from 'client/components/Flows/FlowWithFAB'; - -import Steps from './Steps'; - -export const Context = React.createContext({}); -export const STEP_ID = 'tiers'; - -const Roles = () => { - const { steps, defaultValues, resolvers } = Steps(); - - 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/containers/Application/Create/Steps/Tiers/Flow/CustomNode.js b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Flow/CustomNode.js new file mode 100644 index 0000000000..2911378328 --- /dev/null +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Flow/CustomNode.js @@ -0,0 +1,46 @@ +import React, { memo } from 'react'; +import PropTypes from 'prop-types'; + +import { Handle } from 'react-flow-renderer'; + +import { TierCard } from 'client/components/Cards'; + +const CustomNode = memo(({ data }) => { + const { tier, handleEdit } = data; + + const isValidConnection = ({ target }) => !tier?.parents?.includes(target); + + return ( + <> + + + + + ); +}); + +CustomNode.propTypes = { + data: PropTypes.objectOf(PropTypes.any) +}; + +CustomNode.defaultProps = { + data: {} +}; + +export default CustomNode; 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/Tiers/Steps/BasicConfiguration/index.js similarity index 93% rename from src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/BasicConfiguration/index.js rename to src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/BasicConfiguration/index.js index bf704fde68..44b2dba61b 100644 --- a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/BasicConfiguration/index.js +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/BasicConfiguration/index.js @@ -4,7 +4,7 @@ import FormWithSchema from 'client/components/Forms/FormWithSchema'; import { FORM_FIELDS, STEP_FORM_SCHEMA } from './schema'; -export const STEP_ID = 'role'; +export const STEP_ID = 'tier'; const BasicConfiguration = () => ({ id: STEP_ID, diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/BasicConfiguration/schema.js b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/BasicConfiguration/schema.js similarity index 100% rename from src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/BasicConfiguration/schema.js rename to src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/BasicConfiguration/schema.js 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/Tiers/Steps/Networks/index.js similarity index 99% rename from src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Networks/index.js rename to src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Networks/index.js index bb67ae262f..1c81536dc4 100644 --- a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Networks/index.js +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Networks/index.js @@ -5,7 +5,7 @@ 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'; +import { Context } from 'client/containers/Application/Create/Steps/Tiers'; import { STEP_FORM_SCHEMA } from './schema'; export const STEP_ID = '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/Tiers/Steps/Networks/schema.js similarity index 100% rename from src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Networks/schema.js rename to src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Networks/schema.js diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Policies/index.js b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Policies/index.js similarity index 100% rename from src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Policies/index.js rename to src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Policies/index.js diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Policies/schema.js b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Policies/schema.js similarity index 100% rename from src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Policies/schema.js rename to src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Policies/schema.js diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/List/Docker.js b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Template/List/Docker.js similarity index 100% rename from src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/List/Docker.js rename to src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Template/List/Docker.js diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/List/MarketApps.js b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Template/List/MarketApps.js similarity index 100% rename from src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/List/MarketApps.js rename to src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Template/List/MarketApps.js diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/List/Templates.js b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Template/List/Templates.js similarity index 100% rename from src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/List/Templates.js rename to src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Template/List/Templates.js 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/Tiers/Steps/Template/index.js similarity index 97% rename from src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/index.js rename to src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Template/index.js index 2b68f3fefb..bd56a56556 100644 --- a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/index.js +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Template/index.js @@ -58,7 +58,7 @@ const Template = () => ({ useEffect(() => { if (Object.keys(data).length > 0) { const currentScreen = Object.keys(data)[0]; - setScreen(SCREENS.find(src => src.id === currentScreen.id)); + setScreen(SCREENS.find(src => src.id === currentScreen)); } }, []); diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/schema.js b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Template/schema.js similarity index 100% rename from src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/Template/schema.js rename to src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/Template/schema.js diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/index.js b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/index.js similarity index 100% rename from src/fireedge/src/public/containers/Application/Create/Steps/Roles/Steps/index.js rename to src/fireedge/src/public/containers/Application/Create/Steps/Tiers/Steps/index.js diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/index.js b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/index.js new file mode 100644 index 0000000000..e6ece56aa7 --- /dev/null +++ b/src/fireedge/src/public/containers/Application/Create/Steps/Tiers/index.js @@ -0,0 +1,158 @@ +import React, { useEffect, useState, useCallback, createContext } from 'react'; + +import * as yup from 'yup'; +import { useWatch } from 'react-hook-form'; + +import { Add as AddIcon, Refresh as RefreshIcon } from '@material-ui/icons'; +import ReactFlow, { + ReactFlowProvider, + Background, + addEdge +} from 'react-flow-renderer'; +import dagre from 'dagre'; + +import useListForm from 'client/hooks/useListForm'; +import FormStepper from 'client/components/FormStepper'; +import { DialogForm } from 'client/components/Dialogs'; +import { generateFlow } from 'client/utils/flow'; +import SpeedDials from 'client/components/SpeedDials'; + +import Steps from './Steps'; +import CustomNode from './Flow/CustomNode'; + +export const Context = createContext({}); +export const STEP_ID = 'tiers'; + +const Tiers = () => { + const { steps, defaultValues, resolvers } = Steps(); + + return { + id: STEP_ID, + label: 'Tier Definition', + resolver: yup + .array() + .of(resolvers) + .min(1) + .required() + .default([]), + content: useCallback(({ data, setFormData }) => { + const [flow, setFlow] = useState([]); + 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 + }); + + const graph = new dagre.graphlib.Graph(); + graph.setGraph({}); + graph.setDefaultEdgeLabel(() => ({})); + + const reDrawFlow = () => { + setFlow( + generateFlow( + graph, + data?.map((item, index) => ({ + id: item.tier.name, + type: 'tier', + data: { + ...item, + handleEdit: () => { + handleEdit(index); + setShowDialog(true); + } + }, + parents: item.tier.parents ?? [] + })) ?? [] + ) + ); + }; + + useEffect(() => { + setNestedForm(form); + }, []); + + const actions = [ + { + icon: , + name: 'Add', + handleClick: () => { + handleEdit(); + setShowDialog(true); + } + }, + { + icon: , + name: 'Refresh', + handleClick: () => reDrawFlow() + } + ]; + + return ( + +
+ { + const indexChild = data?.findIndex( + item => item?.tier?.name === target + ); + const child = { ...data[indexChild] }; + child.tier.parents = [...(child?.tier?.parents ?? []), source]; + + handleEdit(indexChild); + handleSave(child); + setFlow(prevFlow => + addEdge({ source, target, animated: true }, prevFlow) + ); + }} + onLoad={reactFlowInstance => { + reDrawFlow(); + reactFlowInstance.fitView(); + }} + > + + + +
+ {showDialog && ( + + setShowDialog(false)} + > +
+ { + handleSave(values); + setShowDialog(false); + reDrawFlow(); + }} + /> +
+
+
+ )} +
+ ); + }, []) + }; +}; + +export default Tiers; diff --git a/src/fireedge/src/public/containers/Application/Create/Steps/index.js b/src/fireedge/src/public/containers/Application/Create/Steps/index.js index 70d810977b..88d4cb40cf 100644 --- a/src/fireedge/src/public/containers/Application/Create/Steps/index.js +++ b/src/fireedge/src/public/containers/Application/Create/Steps/index.js @@ -3,21 +3,21 @@ import * as yup from 'yup'; import BasicConfiguration from './BasicConfiguration'; import Clusters from './Clusters'; import Networking from './Networking'; -import Roles from './Roles'; +import Tiers from './Tiers'; const Steps = () => { const basic = BasicConfiguration(); const clusters = Clusters(); const networking = Networking(); - const roles = Roles(); + const tiers = Tiers(); - const steps = [basic, clusters, networking, roles]; + const steps = [basic, clusters, networking, tiers]; const resolvers = yup.object({ [basic.id]: basic.resolver, [clusters.id]: clusters.resolver, [networking.id]: networking.resolver, - [roles.id]: roles.resolver + [tiers.id]: tiers.resolver }); const defaultValues = resolvers.default(); diff --git a/src/fireedge/src/public/hooks/useList.js b/src/fireedge/src/public/hooks/useList.js index 7e9bb0963f..7d6aa4ce33 100644 --- a/src/fireedge/src/public/hooks/useList.js +++ b/src/fireedge/src/public/hooks/useList.js @@ -16,11 +16,9 @@ function useList({ list, initLength }) { if (list?.length === 0 || shortList.length !== 0) return; setLoading(true); - - fakeDelay(200) - .then(() => setFullList(list)) - .then(() => setShortList(list.slice(0, initLength))) - .then(() => setLoading(false)); + setFullList(list); + setShortList(list.slice(0, initLength)); + setLoading(false); }, [list]); useEffect(() => { diff --git a/src/fireedge/src/public/hooks/useListForm.js b/src/fireedge/src/public/hooks/useListForm.js index 49bcfbf356..0f141f13a3 100644 --- a/src/fireedge/src/public/hooks/useListForm.js +++ b/src/fireedge/src/public/hooks/useListForm.js @@ -1,51 +1,69 @@ -import { useState } from 'react'; +import { useCallback, 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 handleSelect = useCallback( + index => + setList(prevData => ({ + ...prevData, + [key]: multiple ? [...(prevData[key] ?? []), index] : [index] + })), + [key, list, multiple] + ); - const handleUnselect = indexRemove => - setList(prevData => ({ - ...prevData, - [key]: prevData[key]?.filter(index => index !== indexRemove) - })); + const handleUnselect = useCallback( + indexRemove => + setList(prevData => ({ + ...prevData, + [key]: prevData[key]?.filter(index => index !== indexRemove) + })), + [key, list] + ); - const handleSave = values => { - setList(prevData => ({ - ...prevData, - [key]: Object.assign(prevData[key], { - [editingData.index]: values - }) - })); - }; + const handleSave = useCallback( + values => { + setList(prevData => ({ + ...prevData, + [key]: Object.assign(prevData[key], { + [editingData.index]: values + }) + })); + }, + [key, list, editingData] + ); - const handleEdit = (index = list?.length) => { - const openData = list[index] ?? defaultValue; + const handleEdit = useCallback( + (index = list?.length) => { + const openData = list[index] ?? defaultValue; - setEditingData({ index, data: openData }); - }; + setEditingData({ index, data: openData }); + }, + [list, defaultValue] + ); - const handleClone = index => { - const item = list[index]; - const cloneItem = { ...item, name: `${item?.name}_clone` }; - const cloneData = [...list]; - cloneData.splice(index + 1, 0, cloneItem); + const handleClone = useCallback( + 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 })); - }; + setList(prevData => ({ ...prevData, [key]: cloneData })); + }, + [list] + ); - const handleRemove = indexRemove => { - // TODO confirmation?? - setList(prevData => ({ - ...prevData, - [key]: prevData[key]?.filter((_, index) => index !== indexRemove) - })); - }; + const handleRemove = useCallback( + indexRemove => { + // TODO confirmation?? + setList(prevData => ({ + ...prevData, + [key]: prevData[key]?.filter((_, index) => index !== indexRemove) + })); + }, + [key, list] + ); return { editingData, diff --git a/src/fireedge/src/public/utils/flow.js b/src/fireedge/src/public/utils/flow.js new file mode 100644 index 0000000000..b39f148847 --- /dev/null +++ b/src/fireedge/src/public/utils/flow.js @@ -0,0 +1,44 @@ +import dagre from 'dagre'; + +const generateFlow = (graph, elements = []) => { + const NODE_WIDTH = 400; + const NODE_HEIGHT = 200; + + elements.forEach(({ id, type, data, parents = [] }) => { + graph.setNode(id, { + data, + type: type ?? 'default', + width: NODE_WIDTH, + height: NODE_HEIGHT + }); + parents.forEach(parent => { + graph.setEdge(parent, id); + }); + }); + + dagre.layout(graph); + + const nodes = graph.nodes().map(id => { + const node = graph.node(id); + return { + id, + type: node?.type, + data: node?.data, + position: { + x: node.x - node.width / 2, + y: node.y - node.height / 2 + } + }; + }); + + const edges = graph.edges().map(({ v: source, w: target }) => ({ + id: `__${source}__${target}`, + source, + target, + animated: true + })); + + return [...nodes, ...edges]; +}; + +export { generateFlow };