diff options
| author | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2021-05-13 17:42:53 +0200 |
|---|---|---|
| committer | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2021-05-17 17:06:50 +0200 |
| commit | 1edbae1a0224e30bafb98638f419e1f967a9286f (patch) | |
| tree | 2047c5a684379dfd395891e9447199f6001cef9b /opendc-web/opendc-web-ui/src/containers/app/sidebars | |
| parent | 1891a6f3963d3ddeae0ea093f9a7e3608a97b4d7 (diff) | |
ui: Move modal state outside of Redux
This change updates the frontend so that the modal state is not stored
inside Redux but instead is stored using the useState hook. This
simplifies the design of the modal components.
Diffstat (limited to 'opendc-web/opendc-web-ui/src/containers/app/sidebars')
10 files changed, 268 insertions, 116 deletions
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js index ce295f03..dca7d7db 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js @@ -1,34 +1,23 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useState } from 'react' +import { useDispatch } from 'react-redux' import { useRouter } from 'next/router' import PortfolioListComponent from '../../../../components/app/sidebars/project/PortfolioListComponent' -import { deletePortfolio, setCurrentPortfolio } from '../../../../actions/portfolios' -import { openNewPortfolioModal } from '../../../../actions/modals/portfolios' +import { addPortfolio, deletePortfolio, setCurrentPortfolio } from '../../../../actions/portfolios' import { getState } from '../../../../util/state-utils' import { setCurrentTopology } from '../../../../actions/topology/building' +import NewPortfolioModalComponent from '../../../../components/modals/custom-components/NewPortfolioModalComponent' +import { useActivePortfolio, useActiveProject, usePortfolios } from '../../../../store/hooks/project' -const PortfolioListContainer = (props) => { - const state = useSelector((state) => { - let portfolios = state.objects.project[state.currentProjectId] - ? state.objects.project[state.currentProjectId].portfolioIds.map((t) => state.objects.portfolio[t]) - : [] - if (portfolios.filter((t) => !t).length > 0) { - portfolios = [] - } - - return { - currentProjectId: state.currentProjectId, - currentPortfolioId: state.currentPortfolioId, - portfolios, - } - }) +const PortfolioListContainer = () => { + const currentProjectId = useActiveProject()?._id + const currentPortfolioId = useActivePortfolio()?._id + const portfolios = usePortfolios(currentProjectId) const dispatch = useDispatch() + const [isVisible, setVisible] = useState(false) const router = useRouter() const actions = { - onNewPortfolio: () => { - dispatch(openNewPortfolioModal()) - }, + onNewPortfolio: () => setVisible(true), onChoosePortfolio: (portfolioId) => { dispatch(setCurrentPortfolio(portfolioId)) }, @@ -41,7 +30,28 @@ const PortfolioListContainer = (props) => { } }, } - return <PortfolioListComponent {...props} {...state} {...actions} /> + const callback = (name, targets) => { + if (name) { + dispatch( + addPortfolio({ + name, + targets, + }) + ) + } + setVisible(false) + } + return ( + <> + <PortfolioListComponent + currentProjectId={currentProjectId} + currentPortfolioId={currentPortfolioId} + portfolios={portfolios} + {...actions} + /> + <NewPortfolioModalComponent callback={callback} show={isVisible} /> + </> + ) } export default PortfolioListContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js index 18d0735e..5d747820 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js @@ -1,28 +1,27 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useState } from 'react' +import { useDispatch } from 'react-redux' import ScenarioListComponent from '../../../../components/app/sidebars/project/ScenarioListComponent' -import { openNewScenarioModal } from '../../../../actions/modals/scenarios' -import { deleteScenario, setCurrentScenario } from '../../../../actions/scenarios' +import { addScenario, deleteScenario, setCurrentScenario } from '../../../../actions/scenarios' import { setCurrentPortfolio } from '../../../../actions/portfolios' +import NewScenarioModalComponent from '../../../../components/modals/custom-components/NewScenarioModalComponent' +import { useProjectTopologies } from '../../../../store/hooks/topology' +import { useActiveScenario, useActiveProject, useScenarios } from '../../../../store/hooks/project' +import { useSchedulers, useTraces } from '../../../../store/hooks/experiments' -const ScenarioListContainer = ({ portfolioId, children }) => { - const currentProjectId = useSelector((state) => state.currentProjectId) - const currentScenarioId = useSelector((state) => state.currentScenarioId) - const scenarios = useSelector((state) => { - let scenarios = state.objects.portfolio[portfolioId] - ? state.objects.portfolio[portfolioId].scenarioIds.map((t) => state.objects.scenario[t]) - : [] - if (scenarios.filter((t) => !t).length > 0) { - scenarios = [] - } - - return scenarios - }) +const ScenarioListContainer = ({ portfolioId }) => { + const currentProjectId = useActiveProject()?._id + const currentScenarioId = useActiveScenario()?._id + const scenarios = useScenarios(portfolioId) + const topologies = useProjectTopologies() + const traces = useTraces() + const schedulers = useSchedulers() const dispatch = useDispatch() + const [isVisible, setVisible] = useState(false) + const onNewScenario = (currentPortfolioId) => { dispatch(setCurrentPortfolio(currentPortfolioId)) - dispatch(openNewScenarioModal()) + setVisible(true) } const onChooseScenario = (portfolioId, scenarioId) => { dispatch(setCurrentScenario(portfolioId, scenarioId)) @@ -32,17 +31,43 @@ const ScenarioListContainer = ({ portfolioId, children }) => { dispatch(deleteScenario(id)) } } + const callback = (name, portfolioId, trace, topology, operational) => { + if (name) { + dispatch( + addScenario({ + portfolioId, + name, + trace, + topology, + operational, + }) + ) + } + + setVisible(false) + } return ( - <ScenarioListComponent - portfolioId={portfolioId} - currentProjectId={currentProjectId} - currentScenarioId={currentScenarioId} - scenarios={scenarios} - onNewScenario={onNewScenario} - onChooseScenario={onChooseScenario} - onDeleteScenario={onDeleteScenario} - /> + <> + <ScenarioListComponent + portfolioId={portfolioId} + currentProjectId={currentProjectId} + currentScenarioId={currentScenarioId} + scenarios={scenarios} + onNewScenario={onNewScenario} + onChooseScenario={onChooseScenario} + onDeleteScenario={onDeleteScenario} + /> + <NewScenarioModalComponent + show={isVisible} + currentPortfolioId={currentProjectId} + currentPortfolioScenarioIds={scenarios.map((s) => s._id)} + traces={traces} + schedulers={schedulers} + topologies={topologies} + callback={callback} + /> + </> ) } diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js index e9c05f17..3779705a 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js @@ -1,36 +1,25 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useState } from 'react' +import { useDispatch } from 'react-redux' import TopologyListComponent from '../../../../components/app/sidebars/project/TopologyListComponent' import { setCurrentTopology } from '../../../../actions/topology/building' -import { openNewTopologyModal } from '../../../../actions/modals/topology' import { useRouter } from 'next/router' import { getState } from '../../../../util/state-utils' -import { deleteTopology } from '../../../../actions/topologies' +import { addTopology, deleteTopology } from '../../../../actions/topologies' +import NewTopologyModalComponent from '../../../../components/modals/custom-components/NewTopologyModalComponent' +import { useActiveTopology, useProjectTopologies } from '../../../../store/hooks/topology' const TopologyListContainer = () => { const dispatch = useDispatch() const router = useRouter() - - const topologies = useSelector((state) => { - let topologies = state.objects.project[state.currentProjectId] - ? state.objects.project[state.currentProjectId].topologyIds.map((t) => state.objects.topology[t]) - : [] - if (topologies.filter((t) => !t).length > 0) { - topologies = [] - } - - return topologies - }) - const currentTopologyId = useSelector((state) => state.currentTopologyId) + const topologies = useProjectTopologies() + const currentTopologyId = useActiveTopology()?._id + const [isVisible, setVisible] = useState(false) const onChooseTopology = async (id) => { dispatch(setCurrentTopology(id)) const state = await getState(dispatch) router.push(`/projects/${state.currentProjectId}`) } - const onNewTopology = () => { - dispatch(openNewTopologyModal()) - } const onDeleteTopology = async (id) => { if (id) { const state = await getState(dispatch) @@ -39,15 +28,37 @@ const TopologyListContainer = () => { router.push(`/projects/${state.currentProjectId}`) } } + const onCreateTopology = (name) => { + if (name) { + dispatch(addTopology(name, undefined)) + } + setVisible(false) + } + const onDuplicateTopology = (name, id) => { + if (name) { + dispatch(addTopology(name, id)) + } + setVisible(false) + } + const onCancel = () => setVisible(false) return ( - <TopologyListComponent - topologies={topologies} - currentTopologyId={currentTopologyId} - onChooseTopology={onChooseTopology} - onNewTopology={onNewTopology} - onDeleteTopology={onDeleteTopology} - /> + <> + <TopologyListComponent + topologies={topologies} + currentTopologyId={currentTopologyId} + onChooseTopology={onChooseTopology} + onNewTopology={() => setVisible(true)} + onDeleteTopology={onDeleteTopology} + /> + <NewTopologyModalComponent + show={isVisible} + topologies={topologies} + onCreateTopology={onCreateTopology} + onDuplicateTopology={onDuplicateTopology} + onCancel={onCancel} + /> + </> ) } diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/DeleteMachineContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/DeleteMachineContainer.js index 1510a436..2f171ce8 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/DeleteMachineContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/DeleteMachineContainer.js @@ -1,11 +1,32 @@ -import React from 'react' +import React, { useState } from 'react' import { useDispatch } from 'react-redux' -import { openDeleteMachineModal } from '../../../../../actions/modals/topology' -import DeleteMachineComponent from '../../../../../components/app/sidebars/topology/machine/DeleteMachineComponent' +import ConfirmationModal from '../../../../../components/modals/ConfirmationModal' +import { deleteMachine } from '../../../../../actions/topology/machine' +import { Button } from 'reactstrap' -const DeleteMachineContainer = (props) => { +const DeleteMachineContainer = () => { const dispatch = useDispatch() - return <DeleteMachineComponent {...props} onClick={() => dispatch(openDeleteMachineModal())} /> + const [isVisible, setVisible] = useState(false) + const callback = (isConfirmed) => { + if (isConfirmed) { + dispatch(deleteMachine()) + } + setVisible(false) + } + return ( + <> + <Button color="danger" outline block onClick={() => setVisible(true)}> + <span className="fa fa-trash mr-2" /> + Delete this machine + </Button> + <ConfirmationModal + title="Delete this machine" + message="Are you sure you want to delete this machine?" + show={isVisible} + callback={callback} + /> + </> + ) } export default DeleteMachineContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineNameContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineNameContainer.js index 6f4285b2..9cbb32c5 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineNameContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineNameContainer.js @@ -1,10 +1,9 @@ import React from 'react' import { useSelector } from 'react-redux' -import MachineNameComponent from '../../../../../components/app/sidebars/topology/machine/MachineNameComponent' -const MachineNameContainer = (props) => { +const MachineNameContainer = () => { const position = useSelector((state) => state.interactionLevel.position) - return <MachineNameComponent {...props} position={position} /> + return <h2>Machine at slot {position}</h2> } export default MachineNameContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/DeleteRackContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/DeleteRackContainer.js index de46e491..4c7fd8bf 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/DeleteRackContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/DeleteRackContainer.js @@ -1,11 +1,32 @@ -import React from 'react' +import React, { useState } from 'react' import { useDispatch } from 'react-redux' -import { openDeleteRackModal } from '../../../../../actions/modals/topology' -import DeleteRackComponent from '../../../../../components/app/sidebars/topology/rack/DeleteRackComponent' +import ConfirmationModal from '../../../../../components/modals/ConfirmationModal' +import { deleteRack } from '../../../../../actions/topology/rack' +import { Button } from 'reactstrap' -const DeleteRackContainer = (props) => { +const DeleteRackContainer = () => { const dispatch = useDispatch() - return <DeleteRackComponent {...props} onClick={() => dispatch(openDeleteRackModal())} /> + const [isVisible, setVisible] = useState(false) + const callback = (isConfirmed) => { + if (isConfirmed) { + dispatch(deleteRack()) + } + setVisible(false) + } + return ( + <> + <Button color="danger" outline block onClick={() => setVisible(true)}> + <span className="fa fa-trash mr-2" /> + Delete this rack + </Button> + <ConfirmationModal + title="Delete this rack" + message="Are you sure you want to delete this rack?" + show={isVisible} + callback={callback} + /> + </> + ) } export default DeleteRackContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackNameContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackNameContainer.js index 7dfdb473..dd9b05fc 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackNameContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackNameContainer.js @@ -1,14 +1,33 @@ -import React from 'react' +import React, { useState } from 'react' import { useDispatch, useSelector } from 'react-redux' -import { openEditRackNameModal } from '../../../../../actions/modals/topology' -import RackNameComponent from '../../../../../components/app/sidebars/topology/rack/RackNameComponent' +import NameComponent from '../../../../../components/app/sidebars/topology/NameComponent' +import TextInputModal from '../../../../../components/modals/TextInputModal' +import { editRackName } from '../../../../../actions/topology/rack' -const RackNameContainer = (props) => { +const RackNameContainer = () => { + const [isVisible, setVisible] = useState(false) const rackName = useSelector( (state) => state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rackId].name ) const dispatch = useDispatch() - return <RackNameComponent {...props} rackName={rackName} onEdit={() => dispatch(openEditRackNameModal())} /> + const callback = (name) => { + if (name) { + dispatch(editRackName(name)) + } + setVisible(false) + } + return ( + <> + <NameComponent name={rackName} onEdit={() => setVisible(true)} /> + <TextInputModal + title="Edit rack name" + label="Rack name" + show={isVisible} + initialValue={rackName} + callback={callback} + /> + </> + ) } export default RackNameContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/DeleteRoomContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/DeleteRoomContainer.js index 6a80e9b0..2062462f 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/DeleteRoomContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/DeleteRoomContainer.js @@ -1,12 +1,32 @@ -import React from 'react' +import React, { useState } from 'react' import { useDispatch } from 'react-redux' -import { openDeleteRoomModal } from '../../../../../actions/modals/topology' -import DeleteRoomComponent from '../../../../../components/app/sidebars/topology/room/DeleteRoomComponent' +import { Button } from 'reactstrap' +import ConfirmationModal from '../../../../../components/modals/ConfirmationModal' +import { deleteRoom } from '../../../../../actions/topology/room' -const DeleteRoomContainer = (props) => { +const DeleteRoomContainer = () => { const dispatch = useDispatch() - const onClick = () => dispatch(openDeleteRoomModal()) - return <DeleteRoomComponent {...props} onClick={onClick} /> + const [isVisible, setVisible] = useState(false) + const callback = (isConfirmed) => { + if (isConfirmed) { + dispatch(deleteRoom()) + } + setVisible(false) + } + return ( + <> + <Button color="danger" outline block onClick={() => setVisible(true)}> + <span className="fa fa-trash mr-2" /> + Delete this room + </Button> + <ConfirmationModal + title="Delete this room" + message="Are you sure you want to delete this room?" + show={isVisible} + callback={callback} + /> + </> + ) } export default DeleteRoomContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/EditRoomContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/EditRoomContainer.js index 37027fc5..f03b0efb 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/EditRoomContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/EditRoomContainer.js @@ -1,9 +1,9 @@ import React from 'react' import { useDispatch, useSelector } from 'react-redux' import { finishRoomEdit, startRoomEdit } from '../../../../../actions/topology/building' -import EditRoomComponent from '../../../../../components/app/sidebars/topology/room/EditRoomComponent' +import { Button } from 'reactstrap' -const EditRoomContainer = (props) => { +const EditRoomContainer = () => { const isEditing = useSelector((state) => state.construction.currentRoomInConstruction !== '-1') const isInRackConstructionMode = useSelector((state) => state.construction.inRackConstructionMode) @@ -11,14 +11,22 @@ const EditRoomContainer = (props) => { const onEdit = () => dispatch(startRoomEdit()) const onFinish = () => dispatch(finishRoomEdit()) - return ( - <EditRoomComponent - {...props} - onEdit={onEdit} - onFinish={onFinish} - isEditing={isEditing} - isInRackConstructionMode={isInRackConstructionMode} - /> + return isEditing ? ( + <Button color="info" outline block onClick={onFinish}> + <span className="fa fa-check mr-2" /> + Finish editing room + </Button> + ) : ( + <Button + color="info" + outline + block + disabled={isInRackConstructionMode} + onClick={() => (isInRackConstructionMode ? undefined : onEdit())} + > + <span className="fa fa-pencil mr-2" /> + Edit the tiles of this room + </Button> ) } diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomNameContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomNameContainer.js index 1f53aeb6..946bb864 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomNameContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomNameContainer.js @@ -1,13 +1,31 @@ -import React from 'react' +import React, { useState } from 'react' import { useDispatch, useSelector } from 'react-redux' -import { openEditRoomNameModal } from '../../../../../actions/modals/topology' -import RoomNameComponent from '../../../../../components/app/sidebars/topology/room/RoomNameComponent' +import NameComponent from '../../../../../components/app/sidebars/topology/NameComponent' +import TextInputModal from '../../../../../components/modals/TextInputModal' +import { editRoomName } from '../../../../../actions/topology/room' -const RoomNameContainer = (props) => { +const RoomNameContainer = () => { + const [isVisible, setVisible] = useState(false) const roomName = useSelector((state) => state.objects.room[state.interactionLevel.roomId].name) const dispatch = useDispatch() - const onEdit = () => dispatch(openEditRoomNameModal()) - return <RoomNameComponent {...props} onEdit={onEdit} roomName={roomName} /> + const callback = (name) => { + if (name) { + dispatch(editRoomName(name)) + } + setVisible(false) + } + return ( + <> + <NameComponent name={roomName} onEdit={() => setVisible(true)} /> + <TextInputModal + title="Edit room name" + label="Room name" + show={isVisible} + initialValue={roomName} + callback={callback} + /> + </> + ) } export default RoomNameContainer |
