diff options
| author | Fabian Mastenbroek <mail.fabianm@gmail.com> | 2021-07-08 16:08:02 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-07-08 16:08:02 +0200 |
| commit | 1a2416043f0b877f570e89da74e0d0a4aff1d8ae (patch) | |
| tree | 1bed18bb62d223be954faca87b0736d2a571b443 /opendc-web/opendc-web-ui/src/containers/app | |
| parent | dfd2ded56780995cec6d91af37443b710d4ddb3b (diff) | |
| parent | 2c8d675c2cf140eac05988065a9d20fd2773399a (diff) | |
ui: Simplify data fetching in frontend
This pull request aims to simplify the data fetching logic in the OpenDC frontend.
Previously, the frontend used Redux extensively to sync the server state with the
client state, which introduced a lot of unnecessary complexity.
With this pull request, we move most of the data fetching logic out of Redux and
instead use React Query to perform the logic for fetching and caching API requests.
* Move all server data except topologies outside Redux
* Use React Query for fetching server data
* (De)normalize topology using Normalizr
* Remove current ids state from Redux
* Combine fetching of project relations
Diffstat (limited to 'opendc-web/opendc-web-ui/src/containers/app')
20 files changed, 129 insertions, 272 deletions
diff --git a/opendc-web/opendc-web-ui/src/containers/app/App.js b/opendc-web/opendc-web-ui/src/containers/app/App.js deleted file mode 100644 index ec9714ce..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/App.js +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2021 AtLarge Research - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -import PropTypes from 'prop-types' -import React, { useEffect } from 'react' -import Head from 'next/head' -import { HotKeys } from 'react-hotkeys' -import { useDispatch, useSelector } from 'react-redux' -import { openPortfolioSucceeded } from '../../redux/actions/portfolios' -import { openProjectSucceeded } from '../../redux/actions/projects' -import ToolPanelComponent from '../../components/app/map/controls/ToolPanelComponent' -import LoadingScreen from '../../components/app/map/LoadingScreen' -import ScaleIndicatorContainer from '../../containers/app/map/controls/ScaleIndicatorContainer' -import MapStage from '../../containers/app/map/MapStage' -import TopologySidebarContainer from '../../containers/app/sidebars/topology/TopologySidebarContainer' -import AppNavbarContainer from '../../containers/navigation/AppNavbarContainer' -import ProjectSidebarContainer from '../../containers/app/sidebars/project/ProjectSidebarContainer' -import { openScenarioSucceeded } from '../../redux/actions/scenarios' -import PortfolioResultsContainer from '../../containers/app/results/PortfolioResultsContainer' -import { KeymapConfiguration } from '../../hotkeys' -import { useRequireAuth } from '../../auth' -import { useActiveProject } from '../../data/project' - -const App = ({ projectId, portfolioId, scenarioId }) => { - useRequireAuth() - - const projectName = useActiveProject()?.name - const topologyIsLoading = useSelector((state) => state.currentTopologyId === '-1') - - const dispatch = useDispatch() - useEffect(() => { - if (scenarioId) { - dispatch(openScenarioSucceeded(projectId, portfolioId, scenarioId)) - } else if (portfolioId) { - dispatch(openPortfolioSucceeded(projectId, portfolioId)) - } else { - dispatch(openProjectSucceeded(projectId)) - } - }, [projectId, portfolioId, scenarioId, dispatch]) - - const constructionElements = topologyIsLoading ? ( - <div className="full-height d-flex align-items-center justify-content-center"> - <LoadingScreen /> - </div> - ) : ( - <div className="full-height"> - <MapStage /> - <ScaleIndicatorContainer /> - <ToolPanelComponent /> - <ProjectSidebarContainer /> - <TopologySidebarContainer /> - </div> - ) - - const portfolioElements = ( - <div className="full-height app-page-container"> - <ProjectSidebarContainer /> - <div className="container-fluid full-height"> - <PortfolioResultsContainer /> - </div> - </div> - ) - - const scenarioElements = ( - <div className="full-height app-page-container"> - <ProjectSidebarContainer /> - <div className="container-fluid full-height"> - <h2>Scenario loading</h2> - </div> - </div> - ) - - const title = projectName ? projectName + ' - OpenDC' : 'Simulation - OpenDC' - - return ( - <HotKeys keyMap={KeymapConfiguration} allowChanges={true} className="page-container full-height"> - <Head> - <title>{title}</title> - </Head> - <AppNavbarContainer fullWidth={true} /> - {scenarioId ? scenarioElements : portfolioId ? portfolioElements : constructionElements} - </HotKeys> - ) -} - -App.propTypes = { - projectId: PropTypes.string.isRequired, - portfolioId: PropTypes.string, - scenarioId: PropTypes.string, -} - -export default App diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/RackEnergyFillContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/RackEnergyFillContainer.js index 00d3152f..d22317a5 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/map/RackEnergyFillContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/map/RackEnergyFillContainer.js @@ -5,17 +5,17 @@ import RackFillBar from '../../../components/app/map/elements/RackFillBar' const RackSpaceFillContainer = (props) => { const state = useSelector((state) => { let energyConsumptionTotal = 0 - const rack = state.objects.rack[state.objects.tile[props.tileId].rackId] - const machineIds = rack.machineIds + const rack = state.objects.rack[state.objects.tile[props.tileId].rack] + const machineIds = rack.machines machineIds.forEach((machineId) => { if (machineId !== null) { const machine = state.objects.machine[machineId] - machine.cpuIds.forEach((id) => (energyConsumptionTotal += state.objects.cpu[id].energyConsumptionW)) - machine.gpuIds.forEach((id) => (energyConsumptionTotal += state.objects.gpu[id].energyConsumptionW)) - machine.memoryIds.forEach( + machine.cpus.forEach((id) => (energyConsumptionTotal += state.objects.cpu[id].energyConsumptionW)) + machine.gpus.forEach((id) => (energyConsumptionTotal += state.objects.gpu[id].energyConsumptionW)) + machine.memories.forEach( (id) => (energyConsumptionTotal += state.objects.memory[id].energyConsumptionW) ) - machine.storageIds.forEach( + machine.storages.forEach( (id) => (energyConsumptionTotal += state.objects.storage[id].energyConsumptionW) ) } diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/RackSpaceFillContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/RackSpaceFillContainer.js index dc5119fd..8d6f61e0 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/map/RackSpaceFillContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/map/RackSpaceFillContainer.js @@ -4,7 +4,7 @@ import RackFillBar from '../../../components/app/map/elements/RackFillBar' const RackSpaceFillContainer = (props) => { const state = useSelector((state) => { - const machineIds = state.objects.rack[state.objects.tile[props.tileId].rackId].machineIds + const machineIds = state.objects.rack[state.objects.tile[props.tileId].rack].machines return { type: 'space', fillFraction: machineIds.filter((id) => id !== null).length / machineIds.length, diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/RoomContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/RoomContainer.js index 52d48317..0a9e1503 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/map/RoomContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/map/RoomContainer.js @@ -1,3 +1,4 @@ +import PropTypes from 'prop-types' import React from 'react' import { useDispatch, useSelector } from 'react-redux' import { goFromBuildingToRoom } from '../../../redux/actions/interaction-level' @@ -15,4 +16,8 @@ const RoomContainer = (props) => { return <RoomGroup {...props} {...state} onClick={() => dispatch(goFromBuildingToRoom(props.roomId))} /> } +RoomContainer.propTypes = { + roomId: PropTypes.string, +} + export default RoomContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/TileContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/TileContainer.js index f97e89a1..50a2abfd 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/map/TileContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/map/TileContainer.js @@ -9,7 +9,7 @@ const TileContainer = (props) => { const dispatch = useDispatch() const onClick = (tile) => { - if (tile.rackId) { + if (tile.rack) { dispatch(goFromRoomToRack(tile._id)) } } diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/WallContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/WallContainer.js index 2a469860..67f36396 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/map/WallContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/map/WallContainer.js @@ -4,7 +4,7 @@ import WallGroup from '../../../components/app/map/groups/WallGroup' const WallContainer = (props) => { const tiles = useSelector((state) => - state.objects.room[props.roomId].tileIds.map((tileId) => state.objects.tile[tileId]) + state.objects.room[props.roomId].tiles.map((tileId) => state.objects.tile[tileId]) ) return <WallGroup {...props} tiles={tiles} /> } diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/layers/ObjectHoverLayer.js b/opendc-web/opendc-web-ui/src/containers/app/map/layers/ObjectHoverLayer.js index 8e934a01..e9a64545 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/map/layers/ObjectHoverLayer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/map/layers/ObjectHoverLayer.js @@ -16,10 +16,10 @@ const ObjectHoverLayer = (props) => { } const currentRoom = state.objects.room[state.interactionLevel.roomId] - const tiles = currentRoom.tileIds.map((tileId) => state.objects.tile[tileId]) + const tiles = currentRoom.tiles.map((tileId) => state.objects.tile[tileId]) const tile = findTileWithPosition(tiles, x, y) - return !(tile === null || tile.rackId) + return !(tile === null || tile.rack) }, } }) diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/layers/RoomHoverLayer.js b/opendc-web/opendc-web-ui/src/containers/app/map/layers/RoomHoverLayer.js index 1bfadb6d..4070c766 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/map/layers/RoomHoverLayer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/map/layers/RoomHoverLayer.js @@ -23,14 +23,14 @@ const RoomHoverLayer = (props) => { .map((id) => Object.assign({}, state.objects.room[id])) .filter( (room) => - state.objects.topology[state.currentTopologyId].roomIds.indexOf(room._id) !== -1 && + state.objects.topology[state.currentTopologyId].rooms.indexOf(room._id) !== -1 && room._id !== state.construction.currentRoomInConstruction ) ;[...oldRooms, newRoom].forEach((room) => { - room.tiles = room.tileIds.map((tileId) => state.objects.tile[tileId]) + room.tiles = room.tiles.map((tileId) => state.objects.tile[tileId]) }) - if (newRoom.tileIds.length === 0) { + if (newRoom.tiles.length === 0) { return findPositionInRooms(oldRooms, x, y) === -1 } diff --git a/opendc-web/opendc-web-ui/src/containers/app/results/PortfolioResultsContainer.js b/opendc-web/opendc-web-ui/src/containers/app/results/PortfolioResultsContainer.js index e60abe18..a75f15ae 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/results/PortfolioResultsContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/results/PortfolioResultsContainer.js @@ -1,30 +1,13 @@ import React from 'react' -import { useSelector } from 'react-redux' import PortfolioResultsComponent from '../../../components/app/results/PortfolioResultsComponent' +import { useRouter } from 'next/router' +import { usePortfolio, usePortfolioScenarios } from '../../../data/project' const PortfolioResultsContainer = (props) => { - const { scenarios, portfolio } = useSelector((state) => { - if ( - state.currentPortfolioId === '-1' || - !state.objects.portfolio[state.currentPortfolioId] || - state.objects.portfolio[state.currentPortfolioId].scenarioIds - .map((scenarioId) => state.objects.scenario[scenarioId]) - .some((s) => s === undefined) - ) { - return { - portfolio: undefined, - scenarios: [], - } - } - - return { - portfolio: state.objects.portfolio[state.currentPortfolioId], - scenarios: state.objects.portfolio[state.currentPortfolioId].scenarioIds.map( - (scenarioId) => state.objects.scenario[scenarioId] - ), - } - }) - + const router = useRouter() + const { portfolio: currentPortfolioId } = router.query + const { data: portfolio } = usePortfolio(currentPortfolioId) + const scenarios = usePortfolioScenarios(currentPortfolioId).data ?? [] return <PortfolioResultsComponent {...props} scenarios={scenarios} portfolio={portfolio} /> } 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 a36997ff..60ac666c 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,43 +1,34 @@ import React, { useState } from 'react' -import { useDispatch } from 'react-redux' import { useRouter } from 'next/router' import PortfolioListComponent from '../../../../components/app/sidebars/project/PortfolioListComponent' -import { addPortfolio, deletePortfolio, setCurrentPortfolio } from '../../../../redux/actions/portfolios' -import { getState } from '../../../../util/state-utils' -import { setCurrentTopology } from '../../../../redux/actions/topology/building' import NewPortfolioModalComponent from '../../../../components/modals/custom-components/NewPortfolioModalComponent' -import { useActivePortfolio, useActiveProject, usePortfolios } from '../../../../data/project' +import { useProjectPortfolios } from '../../../../data/project' +import { useMutation } from 'react-query' const PortfolioListContainer = () => { - const currentProjectId = useActiveProject()?._id - const currentPortfolioId = useActivePortfolio()?._id - const portfolios = usePortfolios(currentProjectId) + const router = useRouter() + const { project: currentProjectId, portfolio: currentPortfolioId } = router.query + const portfolios = useProjectPortfolios(currentProjectId).data ?? [] + + const { mutate: addPortfolio } = useMutation('addPortfolio') + const { mutateAsync: deletePortfolio } = useMutation('deletePortfolio') - const dispatch = useDispatch() const [isVisible, setVisible] = useState(false) - const router = useRouter() const actions = { onNewPortfolio: () => setVisible(true), - onChoosePortfolio: (portfolioId) => { - dispatch(setCurrentPortfolio(portfolioId)) + onChoosePortfolio: async (portfolioId) => { + await router.push(`/projects/${currentProjectId}/portfolios/${portfolioId}`) }, onDeletePortfolio: async (id) => { if (id) { - const state = await getState(dispatch) - dispatch(deletePortfolio(id)) - dispatch(setCurrentTopology(state.objects.project[state.currentProjectId].topologyIds[0])) - router.push(`/projects/${state.currentProjectId}`) + await deletePortfolio(id) + await router.push(`/projects/${currentProjectId}`) } }, } const callback = (name, targets) => { if (name) { - dispatch( - addPortfolio({ - name, - targets, - }) - ) + addPortfolio({ projectId: currentProjectId, name, targets }) } setVisible(false) } 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 e1be51dc..3b68df38 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,47 +1,39 @@ +import PropTypes from 'prop-types' import React, { useState } from 'react' -import { useDispatch } from 'react-redux' import ScenarioListComponent from '../../../../components/app/sidebars/project/ScenarioListComponent' -import { addScenario, deleteScenario, setCurrentScenario } from '../../../../redux/actions/scenarios' -import { setCurrentPortfolio } from '../../../../redux/actions/portfolios' import NewScenarioModalComponent from '../../../../components/modals/custom-components/NewScenarioModalComponent' import { useProjectTopologies } from '../../../../data/topology' -import { useActiveScenario, useActiveProject, useScenarios } from '../../../../data/project' +import { usePortfolio, usePortfolioScenarios } from '../../../../data/project' import { useSchedulers, useTraces } from '../../../../data/experiments' +import { useMutation } from 'react-query' 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 { data: portfolio } = usePortfolio(portfolioId) + const scenarios = usePortfolioScenarios(portfolioId).data ?? [] + const topologies = + useProjectTopologies(portfolio?.projectId).data?.map((topology) => ({ + _id: topology._id, + name: topology.name, + })) ?? [] + const traces = useTraces().data ?? [] + const schedulers = useSchedulers().data ?? [] + + const { mutate: addScenario } = useMutation('addScenario') + const { mutate: deleteScenario } = useMutation('deleteScenario') - const dispatch = useDispatch() const [isVisible, setVisible] = useState(false) - const onNewScenario = (currentPortfolioId) => { - dispatch(setCurrentPortfolio(currentPortfolioId)) - setVisible(true) - } - const onChooseScenario = (portfolioId, scenarioId) => { - dispatch(setCurrentScenario(portfolioId, scenarioId)) - } - const onDeleteScenario = (id) => { - if (id) { - dispatch(deleteScenario(id)) - } - } + const onNewScenario = () => setVisible(true) + const onDeleteScenario = (id) => id && deleteScenario(id) const callback = (name, portfolioId, trace, topology, operational) => { if (name) { - dispatch( - addScenario({ - portfolioId, - name, - trace, - topology, - operational, - }) - ) + addScenario({ + portfolioId, + name, + trace, + topology, + operational, + }) } setVisible(false) @@ -51,11 +43,8 @@ const ScenarioListContainer = ({ portfolioId }) => { <> <ScenarioListComponent portfolioId={portfolioId} - currentProjectId={currentProjectId} - currentScenarioId={currentScenarioId} scenarios={scenarios} onNewScenario={onNewScenario} - onChooseScenario={onChooseScenario} onDeleteScenario={onDeleteScenario} /> <NewScenarioModalComponent @@ -71,4 +60,8 @@ const ScenarioListContainer = ({ portfolioId }) => { ) } +ScenarioListContainer.propTypes = { + portfolioId: PropTypes.string, +} + export default ScenarioListContainer 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 266ca495..a2244a30 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 @@ -3,40 +3,42 @@ import { useDispatch } from 'react-redux' import TopologyListComponent from '../../../../components/app/sidebars/project/TopologyListComponent' import { setCurrentTopology } from '../../../../redux/actions/topology/building' import { useRouter } from 'next/router' -import { getState } from '../../../../util/state-utils' -import { addTopology, deleteTopology } from '../../../../redux/actions/topologies' +import { addTopology } from '../../../../redux/actions/topologies' import NewTopologyModalComponent from '../../../../components/modals/custom-components/NewTopologyModalComponent' import { useActiveTopology, useProjectTopologies } from '../../../../data/topology' +import { useMutation } from 'react-query' const TopologyListContainer = () => { const dispatch = useDispatch() const router = useRouter() - const topologies = useProjectTopologies() + const { project: currentProjectId } = router.query + const topologies = + useProjectTopologies(currentProjectId).data?.map((topology) => ({ _id: topology._id, name: topology.name })) ?? + [] const currentTopologyId = useActiveTopology()?._id const [isVisible, setVisible] = useState(false) + const { mutate: deleteTopology } = useMutation('deleteTopology') + const onChooseTopology = async (id) => { dispatch(setCurrentTopology(id)) - const state = await getState(dispatch) - router.push(`/projects/${state.currentProjectId}`) + await router.push(`/projects/${currentProjectId}/topologies/${id}`) } const onDeleteTopology = async (id) => { if (id) { - const state = await getState(dispatch) - dispatch(deleteTopology(id)) - dispatch(setCurrentTopology(state.objects.project[state.currentProjectId].topologyIds[0])) - router.push(`/projects/${state.currentProjectId}`) + deleteTopology(id) + await router.push(`/projects/${currentProjectId}`) } } const onCreateTopology = (name) => { if (name) { - dispatch(addTopology(name, undefined)) + dispatch(addTopology(currentProjectId, name, undefined)) } setVisible(false) } const onDuplicateTopology = (name, id) => { if (name) { - dispatch(addTopology(name, id)) + dispatch(addTopology(currentProjectId, name, id)) } setVisible(false) } diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineSidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineSidebarContainer.js index cb7ec8f9..7553c2fe 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineSidebarContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineSidebarContainer.js @@ -5,7 +5,7 @@ import MachineSidebarComponent from '../../../../../components/app/sidebars/topo const MachineSidebarContainer = (props) => { const machineId = useSelector( (state) => - state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rackId].machineIds[ + state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rack].machines[ state.interactionLevel.position - 1 ] ) diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitContainer.js deleted file mode 100644 index acb16a21..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitContainer.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { deleteUnit } from '../../../../../redux/actions/topology/machine' -import UnitComponent from '../../../../../components/app/sidebars/topology/machine/UnitComponent' - -const UnitContainer = ({ unitId, unitType }) => { - const dispatch = useDispatch() - const unit = useSelector((state) => state.objects[unitType][unitId]) - const onDelete = () => dispatch(deleteUnit(unitType, unitId)) - - return <UnitComponent index={unitId} unit={unit} unitType={unitType} onDelete={onDelete} /> -} - -export default UnitContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitListContainer.js index c5c9444d..cdd7e268 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitListContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitListContainer.js @@ -1,17 +1,34 @@ +import PropTypes from 'prop-types' import React from 'react' -import { useSelector } from 'react-redux' +import { useDispatch, useSelector } from 'react-redux' import UnitListComponent from '../../../../../components/app/sidebars/topology/machine/UnitListComponent' +import { deleteUnit } from '../../../../../redux/actions/topology/machine' -const UnitListContainer = (props) => { - const unitIds = useSelector( - (state) => +const unitMapping = { + cpu: 'cpus', + gpu: 'gpus', + memory: 'memories', + storage: 'storages', +} + +const UnitListContainer = ({ unitType, ...props }) => { + const dispatch = useDispatch() + const units = useSelector((state) => { + const machine = state.objects.machine[ - state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rackId].machineIds[ + state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rack].machines[ state.interactionLevel.position - 1 ] - ][props.unitType + 'Ids'] - ) - return <UnitListComponent {...props} unitIds={unitIds} /> + ] + return machine[unitMapping[unitType]].map((id) => state.objects[unitType][id]) + }) + const onDelete = (unit, unitType) => dispatch(deleteUnit(unitType, unit._id)) + + return <UnitListComponent {...props} units={units} unitType={unitType} onDelete={onDelete} /> +} + +UnitListContainer.propTypes = { + unitType: PropTypes.string.isRequired, } export default UnitListContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/EmptySlotContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/EmptySlotContainer.js deleted file mode 100644 index 2134e411..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/EmptySlotContainer.js +++ /dev/null @@ -1,12 +0,0 @@ -import React from 'react' -import { useDispatch } from 'react-redux' -import { addMachine } from '../../../../../redux/actions/topology/rack' -import EmptySlotComponent from '../../../../../components/app/sidebars/topology/rack/EmptySlotComponent' - -const EmptySlotContainer = (props) => { - const dispatch = useDispatch() - const onAdd = () => dispatch(addMachine(props.position)) - return <EmptySlotComponent {...props} onAdd={onAdd} /> -} - -export default EmptySlotContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineContainer.js deleted file mode 100644 index 7d8e32c1..00000000 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineContainer.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { goFromRackToMachine } from '../../../../../redux/actions/interaction-level' -import MachineComponent from '../../../../../components/app/sidebars/topology/rack/MachineComponent' - -const MachineContainer = (props) => { - const machine = useSelector((state) => state.objects.machine[props.machineId]) - const dispatch = useDispatch() - return ( - <MachineComponent {...props} onClick={() => dispatch(goFromRackToMachine(props.position))} machine={machine} /> - ) -} - -export default MachineContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineListContainer.js index b45300fc..2118d915 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineListContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineListContainer.js @@ -1,12 +1,29 @@ -import React from 'react' -import { useSelector } from 'react-redux' +import React, { useMemo } from 'react' +import { useDispatch, useSelector } from 'react-redux' import MachineListComponent from '../../../../../components/app/sidebars/topology/rack/MachineListComponent' +import { goFromRackToMachine } from '../../../../../redux/actions/interaction-level' +import { addMachine } from '../../../../../redux/actions/topology/rack' const MachineListContainer = (props) => { - const machineIds = useSelector( - (state) => state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rackId].machineIds + const rack = useSelector((state) => state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rack]) + const machines = useSelector((state) => rack.machines.map((id) => state.objects.machine[id])) + const machinesNull = useMemo(() => { + const res = Array(rack.capacity).fill(null) + for (const machine of machines) { + res[machine.position - 1] = machine + } + return res + }, [rack, machines]) + const dispatch = useDispatch() + + return ( + <MachineListComponent + {...props} + machines={machinesNull} + onAdd={(index) => dispatch(addMachine(index))} + onSelect={(index) => dispatch(goFromRackToMachine(index))} + /> ) - return <MachineListComponent {...props} machineIds={machineIds} /> } export default MachineListContainer 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 eaa1e78e..2c39cf9f 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 @@ -7,7 +7,7 @@ import { editRackName } from '../../../../../redux/actions/topology/rack' const RackNameContainer = () => { const [isVisible, setVisible] = useState(false) const rackName = useSelector( - (state) => state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rackId].name + (state) => state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rack].name ) const dispatch = useDispatch() const callback = (name) => { diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackSidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackSidebarContainer.js index b8fc3bfb..34777125 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackSidebarContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackSidebarContainer.js @@ -3,7 +3,7 @@ import { useSelector } from 'react-redux' import RackSidebarComponent from '../../../../../components/app/sidebars/topology/rack/RackSidebarComponent' const RackSidebarContainer = (props) => { - const rackId = useSelector((state) => state.objects.tile[state.interactionLevel.tileId].rackId) + const rackId = useSelector((state) => state.objects.tile[state.interactionLevel.tileId].rack) return <RackSidebarComponent {...props} rackId={rackId} /> } |
