summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-ui/src/containers/app
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-web/opendc-web-ui/src/containers/app')
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/GrayContainer.js13
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js22
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/RackContainer.js12
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/RackEnergyFillContainer.js26
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/RackSpaceFillContainer.js14
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/RoomContainer.js21
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/TileContainer.js26
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/TopologyContainer.js17
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/WallContainer.js12
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/controls/ScaleIndicatorContainer.js12
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/controls/ZoomControlContainer.js19
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/layers/MapLayer.js13
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/layers/ObjectHoverLayer.js33
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/layers/RoomHoverLayer.js46
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/results/PortfolioResultsContainer.js28
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js45
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js10
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js41
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js46
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/TopologySidebarContainer.js12
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/BuildingSidebarContainer.js5
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/NewRoomConstructionContainer.js25
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/BackToRackContainer.js13
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/DeleteMachineContainer.js13
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineNameContainer.js12
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineSidebarContainer.js15
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitAddContainer.js19
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitContainer.js20
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitListContainer.js17
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitTabsContainer.js5
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/AddPrefabContainer.js13
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/BackToRoomContainer.js13
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/DeleteRackContainer.js13
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/EmptySlotContainer.js13
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineContainer.js19
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineListContainer.js12
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackNameContainer.js19
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackSidebarContainer.js12
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/BackToBuildingContainer.js13
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/DeleteRoomContainer.js13
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/EditRoomContainer.js21
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RackConstructionContainer.js21
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomNameContainer.js19
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomSidebarContainer.js12
44 files changed, 825 insertions, 0 deletions
diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/GrayContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/GrayContainer.js
new file mode 100644
index 00000000..9e4a6969
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/GrayContainer.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import { goDownOneInteractionLevel } from '../../../actions/interaction-level'
+import GrayLayer from '../../../components/app/map/elements/GrayLayer'
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onClick: () => dispatch(goDownOneInteractionLevel()),
+ }
+}
+
+const GrayContainer = connect(undefined, mapDispatchToProps)(GrayLayer)
+
+export default GrayContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js b/opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js
new file mode 100644
index 00000000..23c920b6
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js
@@ -0,0 +1,22 @@
+import { connect } from 'react-redux'
+import { setMapDimensions, setMapPositionWithBoundsCheck, zoomInOnPosition } from '../../../actions/map'
+import MapStageComponent from '../../../components/app/map/MapStageComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ mapPosition: state.map.position,
+ mapDimensions: state.map.dimensions,
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ zoomInOnPosition: (zoomIn, x, y) => dispatch(zoomInOnPosition(zoomIn, x, y)),
+ setMapPositionWithBoundsCheck: (x, y) => dispatch(setMapPositionWithBoundsCheck(x, y)),
+ setMapDimensions: (width, height) => dispatch(setMapDimensions(width, height)),
+ }
+}
+
+const MapStage = connect(mapStateToProps, mapDispatchToProps)(MapStageComponent)
+
+export default MapStage
diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/RackContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/RackContainer.js
new file mode 100644
index 00000000..40077608
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/RackContainer.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import RackGroup from '../../../components/app/map/groups/RackGroup'
+
+const mapStateToProps = (state) => {
+ return {
+ interactionLevel: state.interactionLevel,
+ }
+}
+
+const RackContainer = connect(mapStateToProps)(RackGroup)
+
+export default RackContainer
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
new file mode 100644
index 00000000..53746271
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/RackEnergyFillContainer.js
@@ -0,0 +1,26 @@
+import { connect } from 'react-redux'
+import RackFillBar from '../../../components/app/map/elements/RackFillBar'
+
+const mapStateToProps = (state, ownProps) => {
+ let energyConsumptionTotal = 0
+ const rack = state.objects.rack[state.objects.tile[ownProps.tileId].rackId]
+ const machineIds = rack.machineIds
+ 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((id) => (energyConsumptionTotal += state.objects.memory[id].energyConsumptionW))
+ machine.storageIds.forEach((id) => (energyConsumptionTotal += state.objects.storage[id].energyConsumptionW))
+ }
+ })
+
+ return {
+ type: 'energy',
+ fillFraction: Math.min(1, energyConsumptionTotal / rack.powerCapacityW),
+ }
+}
+
+const RackSpaceFillContainer = connect(mapStateToProps)(RackFillBar)
+
+export default RackSpaceFillContainer
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
new file mode 100644
index 00000000..0509a5a5
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/RackSpaceFillContainer.js
@@ -0,0 +1,14 @@
+import { connect } from 'react-redux'
+import RackFillBar from '../../../components/app/map/elements/RackFillBar'
+
+const mapStateToProps = (state, ownProps) => {
+ const machineIds = state.objects.rack[state.objects.tile[ownProps.tileId].rackId].machineIds
+ return {
+ type: 'space',
+ fillFraction: machineIds.filter((id) => id !== null).length / machineIds.length,
+ }
+}
+
+const RackSpaceFillContainer = connect(mapStateToProps)(RackFillBar)
+
+export default RackSpaceFillContainer
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
new file mode 100644
index 00000000..91bf4e5d
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/RoomContainer.js
@@ -0,0 +1,21 @@
+import { connect } from 'react-redux'
+import { goFromBuildingToRoom } from '../../../actions/interaction-level'
+import RoomGroup from '../../../components/app/map/groups/RoomGroup'
+
+const mapStateToProps = (state, ownProps) => {
+ return {
+ interactionLevel: state.interactionLevel,
+ currentRoomInConstruction: state.construction.currentRoomInConstruction,
+ room: state.objects.room[ownProps.roomId],
+ }
+}
+
+const mapDispatchToProps = (dispatch, ownProps) => {
+ return {
+ onClick: () => dispatch(goFromBuildingToRoom(ownProps.roomId)),
+ }
+}
+
+const RoomContainer = connect(mapStateToProps, mapDispatchToProps)(RoomGroup)
+
+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
new file mode 100644
index 00000000..04d6c8d6
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/TileContainer.js
@@ -0,0 +1,26 @@
+import { connect } from 'react-redux'
+import { goFromRoomToRack } from '../../../actions/interaction-level'
+import TileGroup from '../../../components/app/map/groups/TileGroup'
+
+const mapStateToProps = (state, ownProps) => {
+ const tile = state.objects.tile[ownProps.tileId]
+
+ return {
+ interactionLevel: state.interactionLevel,
+ tile,
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onClick: (tile) => {
+ if (tile.rackId) {
+ dispatch(goFromRoomToRack(tile._id))
+ }
+ },
+ }
+}
+
+const TileContainer = connect(mapStateToProps, mapDispatchToProps)(TileGroup)
+
+export default TileContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/TopologyContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/TopologyContainer.js
new file mode 100644
index 00000000..de43a151
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/TopologyContainer.js
@@ -0,0 +1,17 @@
+import { connect } from 'react-redux'
+import TopologyGroup from '../../../components/app/map/groups/TopologyGroup'
+
+const mapStateToProps = (state) => {
+ if (state.currentTopologyId === '-1') {
+ return {}
+ }
+
+ return {
+ topology: state.objects.topology[state.currentTopologyId],
+ interactionLevel: state.interactionLevel,
+ }
+}
+
+const TopologyContainer = connect(mapStateToProps)(TopologyGroup)
+
+export default TopologyContainer
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
new file mode 100644
index 00000000..67f8a242
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/WallContainer.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import WallGroup from '../../../components/app/map/groups/WallGroup'
+
+const mapStateToProps = (state, ownProps) => {
+ return {
+ tiles: state.objects.room[ownProps.roomId].tileIds.map((tileId) => state.objects.tile[tileId]),
+ }
+}
+
+const WallContainer = connect(mapStateToProps)(WallGroup)
+
+export default WallContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/controls/ScaleIndicatorContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/controls/ScaleIndicatorContainer.js
new file mode 100644
index 00000000..fa3b9d22
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/controls/ScaleIndicatorContainer.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import ScaleIndicatorComponent from '../../../../components/app/map/controls/ScaleIndicatorComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ scale: state.map.scale,
+ }
+}
+
+const ScaleIndicatorContainer = connect(mapStateToProps)(ScaleIndicatorComponent)
+
+export default ScaleIndicatorContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/controls/ZoomControlContainer.js b/opendc-web/opendc-web-ui/src/containers/app/map/controls/ZoomControlContainer.js
new file mode 100644
index 00000000..ddc68cc7
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/controls/ZoomControlContainer.js
@@ -0,0 +1,19 @@
+import { connect } from 'react-redux'
+import { zoomInOnCenter } from '../../../../actions/map'
+import ZoomControlComponent from '../../../../components/app/map/controls/ZoomControlComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ mapScale: state.map.scale,
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ zoomInOnCenter: (zoomIn) => dispatch(zoomInOnCenter(zoomIn)),
+ }
+}
+
+const ZoomControlContainer = connect(mapStateToProps, mapDispatchToProps)(ZoomControlComponent)
+
+export default ZoomControlContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/layers/MapLayer.js b/opendc-web/opendc-web-ui/src/containers/app/map/layers/MapLayer.js
new file mode 100644
index 00000000..8596cb9c
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/layers/MapLayer.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import MapLayerComponent from '../../../../components/app/map/layers/MapLayerComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ mapPosition: state.map.position,
+ mapScale: state.map.scale,
+ }
+}
+
+const MapLayer = connect(mapStateToProps)(MapLayerComponent)
+
+export default MapLayer
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
new file mode 100644
index 00000000..a4927862
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/layers/ObjectHoverLayer.js
@@ -0,0 +1,33 @@
+import { connect } from 'react-redux'
+import { addRackToTile } from '../../../../actions/topology/room'
+import ObjectHoverLayerComponent from '../../../../components/app/map/layers/ObjectHoverLayerComponent'
+import { findTileWithPosition } from '../../../../util/tile-calculations'
+
+const mapStateToProps = (state) => {
+ return {
+ mapPosition: state.map.position,
+ mapScale: state.map.scale,
+ isEnabled: () => state.construction.inRackConstructionMode,
+ isValid: (x, y) => {
+ if (state.interactionLevel.mode !== 'ROOM') {
+ return false
+ }
+
+ const currentRoom = state.objects.room[state.interactionLevel.roomId]
+ const tiles = currentRoom.tileIds.map((tileId) => state.objects.tile[tileId])
+ const tile = findTileWithPosition(tiles, x, y)
+
+ return !(tile === null || tile.rackId)
+ },
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onClick: (x, y) => dispatch(addRackToTile(x, y)),
+ }
+}
+
+const ObjectHoverLayer = connect(mapStateToProps, mapDispatchToProps)(ObjectHoverLayerComponent)
+
+export default ObjectHoverLayer
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
new file mode 100644
index 00000000..66404f9e
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/layers/RoomHoverLayer.js
@@ -0,0 +1,46 @@
+import { connect } from 'react-redux'
+import { toggleTileAtLocation } from '../../../../actions/topology/building'
+import RoomHoverLayerComponent from '../../../../components/app/map/layers/RoomHoverLayerComponent'
+import {
+ deriveValidNextTilePositions,
+ findPositionInPositions,
+ findPositionInRooms,
+} from '../../../../util/tile-calculations'
+
+const mapStateToProps = (state) => {
+ return {
+ mapPosition: state.map.position,
+ mapScale: state.map.scale,
+ isEnabled: () => state.construction.currentRoomInConstruction !== '-1',
+ isValid: (x, y) => {
+ const newRoom = Object.assign({}, state.objects.room[state.construction.currentRoomInConstruction])
+ const oldRooms = Object.keys(state.objects.room)
+ .map((id) => Object.assign({}, state.objects.room[id]))
+ .filter(
+ (room) =>
+ state.objects.topology[state.currentTopologyId].roomIds.indexOf(room._id) !== -1 &&
+ room._id !== state.construction.currentRoomInConstruction
+ )
+
+ ;[...oldRooms, newRoom].forEach((room) => {
+ room.tiles = room.tileIds.map((tileId) => state.objects.tile[tileId])
+ })
+ if (newRoom.tileIds.length === 0) {
+ return findPositionInRooms(oldRooms, x, y) === -1
+ }
+
+ const validNextPositions = deriveValidNextTilePositions(oldRooms, newRoom.tiles)
+ return findPositionInPositions(validNextPositions, x, y) !== -1
+ },
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onClick: (x, y) => dispatch(toggleTileAtLocation(x, y)),
+ }
+}
+
+const RoomHoverLayer = connect(mapStateToProps, mapDispatchToProps)(RoomHoverLayerComponent)
+
+export default RoomHoverLayer
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
new file mode 100644
index 00000000..4b430e54
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/results/PortfolioResultsContainer.js
@@ -0,0 +1,28 @@
+import { connect } from 'react-redux'
+import PortfolioResultsComponent from '../../../components/app/results/PortfolioResultsComponent'
+
+const mapStateToProps = (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 PortfolioResultsContainer = connect(mapStateToProps)(PortfolioResultsComponent)
+
+export default PortfolioResultsContainer
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
new file mode 100644
index 00000000..b32c8b1d
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js
@@ -0,0 +1,45 @@
+import { connect } from 'react-redux'
+import { withRouter } from 'react-router-dom'
+import PortfolioListComponent from '../../../../components/app/sidebars/project/PortfolioListComponent'
+import { deletePortfolio, setCurrentPortfolio } from '../../../../actions/portfolios'
+import { openNewPortfolioModal } from '../../../../actions/modals/portfolios'
+import { getState } from '../../../../util/state-utils'
+import { setCurrentTopology } from '../../../../actions/topology/building'
+
+const mapStateToProps = (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 mapDispatchToProps = (dispatch, ownProps) => {
+ return {
+ onNewPortfolio: () => {
+ dispatch(openNewPortfolioModal())
+ },
+ onChoosePortfolio: (portfolioId) => {
+ dispatch(setCurrentPortfolio(portfolioId))
+ },
+ onDeletePortfolio: async (id) => {
+ if (id) {
+ const state = await getState(dispatch)
+ dispatch(deletePortfolio(id))
+ dispatch(setCurrentTopology(state.objects.project[state.currentProjectId].topologyIds[0]))
+ ownProps.history.push(`/projects/${state.currentProjectId}`)
+ }
+ },
+ }
+}
+
+const PortfolioListContainer = withRouter(connect(mapStateToProps, mapDispatchToProps)(PortfolioListComponent))
+
+export default PortfolioListContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js
new file mode 100644
index 00000000..49001099
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js
@@ -0,0 +1,10 @@
+import React from 'react'
+import { withRouter } from 'react-router-dom'
+import ProjectSidebarComponent from '../../../../components/app/sidebars/project/ProjectSidebarComponent'
+import { isCollapsible } from '../../../../util/sidebar-space'
+
+const ProjectSidebarContainer = withRouter(({ location, ...props }) => (
+ <ProjectSidebarComponent collapsible={isCollapsible(location)} {...props} />
+))
+
+export default ProjectSidebarContainer
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
new file mode 100644
index 00000000..415e2792
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js
@@ -0,0 +1,41 @@
+import { connect } from 'react-redux'
+import ScenarioListComponent from '../../../../components/app/sidebars/project/ScenarioListComponent'
+import { openNewScenarioModal } from '../../../../actions/modals/scenarios'
+import { deleteScenario, setCurrentScenario } from '../../../../actions/scenarios'
+import { setCurrentPortfolio } from '../../../../actions/portfolios'
+
+const mapStateToProps = (state, ownProps) => {
+ let scenarios = state.objects.portfolio[ownProps.portfolioId]
+ ? state.objects.portfolio[ownProps.portfolioId].scenarioIds.map((t) => state.objects.scenario[t])
+ : []
+ if (scenarios.filter((t) => !t).length > 0) {
+ scenarios = []
+ }
+
+ return {
+ currentProjectId: state.currentProjectId,
+ currentScenarioId: state.currentScenarioId,
+ scenarios,
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onNewScenario: (currentPortfolioId) => {
+ dispatch(setCurrentPortfolio(currentPortfolioId))
+ dispatch(openNewScenarioModal())
+ },
+ onChooseScenario: (portfolioId, scenarioId) => {
+ dispatch(setCurrentScenario(portfolioId, scenarioId))
+ },
+ onDeleteScenario: (id) => {
+ if (id) {
+ dispatch(deleteScenario(id))
+ }
+ },
+ }
+}
+
+const ScenarioListContainer = connect(mapStateToProps, mapDispatchToProps)(ScenarioListComponent)
+
+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
new file mode 100644
index 00000000..e1de18f9
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js
@@ -0,0 +1,46 @@
+import { connect } from 'react-redux'
+import TopologyListComponent from '../../../../components/app/sidebars/project/TopologyListComponent'
+import { setCurrentTopology } from '../../../../actions/topology/building'
+import { openNewTopologyModal } from '../../../../actions/modals/topology'
+import { withRouter } from 'react-router-dom'
+import { getState } from '../../../../util/state-utils'
+import { deleteTopology } from '../../../../actions/topologies'
+
+const mapStateToProps = (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 {
+ currentTopologyId: state.currentTopologyId,
+ topologies,
+ }
+}
+
+const mapDispatchToProps = (dispatch, ownProps) => {
+ return {
+ onChooseTopology: async (id) => {
+ dispatch(setCurrentTopology(id))
+ const state = await getState(dispatch)
+ ownProps.history.push(`/projects/${state.currentProjectId}`)
+ },
+ onNewTopology: () => {
+ dispatch(openNewTopologyModal())
+ },
+ onDeleteTopology: async (id) => {
+ if (id) {
+ const state = await getState(dispatch)
+ dispatch(deleteTopology(id))
+ dispatch(setCurrentTopology(state.objects.project[state.currentProjectId].topologyIds[0]))
+ ownProps.history.push(`/projects/${state.currentProjectId}`)
+ }
+ },
+ }
+}
+
+const TopologyListContainer = withRouter(connect(mapStateToProps, mapDispatchToProps)(TopologyListComponent))
+
+export default TopologyListContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/TopologySidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/TopologySidebarContainer.js
new file mode 100644
index 00000000..fe7c02fd
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/TopologySidebarContainer.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import TopologySidebarComponent from '../../../../components/app/sidebars/topology/TopologySidebarComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ interactionLevel: state.interactionLevel,
+ }
+}
+
+const TopologySidebarContainer = connect(mapStateToProps)(TopologySidebarComponent)
+
+export default TopologySidebarContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/BuildingSidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/BuildingSidebarContainer.js
new file mode 100644
index 00000000..a0b52e56
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/BuildingSidebarContainer.js
@@ -0,0 +1,5 @@
+import BuildingSidebarComponent from '../../../../../components/app/sidebars/topology/building/BuildingSidebarComponent'
+
+const BuildingSidebarContainer = BuildingSidebarComponent
+
+export default BuildingSidebarContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/NewRoomConstructionContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/NewRoomConstructionContainer.js
new file mode 100644
index 00000000..ea9e9e60
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/building/NewRoomConstructionContainer.js
@@ -0,0 +1,25 @@
+import { connect } from 'react-redux'
+import {
+ cancelNewRoomConstruction,
+ finishNewRoomConstruction,
+ startNewRoomConstruction,
+} from '../../../../../actions/topology/building'
+import StartNewRoomConstructionComponent from '../../../../../components/app/sidebars/topology/building/NewRoomConstructionComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ currentRoomInConstruction: state.construction.currentRoomInConstruction,
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onStart: () => dispatch(startNewRoomConstruction()),
+ onFinish: () => dispatch(finishNewRoomConstruction()),
+ onCancel: () => dispatch(cancelNewRoomConstruction()),
+ }
+}
+
+const NewRoomConstructionButton = connect(mapStateToProps, mapDispatchToProps)(StartNewRoomConstructionComponent)
+
+export default NewRoomConstructionButton
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/BackToRackContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/BackToRackContainer.js
new file mode 100644
index 00000000..24287ab0
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/BackToRackContainer.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import { goDownOneInteractionLevel } from '../../../../../actions/interaction-level'
+import BackToRackComponent from '../../../../../components/app/sidebars/topology/machine/BackToRackComponent'
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onClick: () => dispatch(goDownOneInteractionLevel()),
+ }
+}
+
+const BackToRackContainer = connect(undefined, mapDispatchToProps)(BackToRackComponent)
+
+export default BackToRackContainer
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
new file mode 100644
index 00000000..65e683e6
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/DeleteMachineContainer.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import { openDeleteMachineModal } from '../../../../../actions/modals/topology'
+import DeleteMachineComponent from '../../../../../components/app/sidebars/topology/machine/DeleteMachineComponent'
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onClick: () => dispatch(openDeleteMachineModal()),
+ }
+}
+
+const DeleteMachineContainer = connect(undefined, mapDispatchToProps)(DeleteMachineComponent)
+
+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
new file mode 100644
index 00000000..1cf35b05
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineNameContainer.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import MachineNameComponent from '../../../../../components/app/sidebars/topology/machine/MachineNameComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ position: state.interactionLevel.position,
+ }
+}
+
+const MachineNameContainer = connect(mapStateToProps)(MachineNameComponent)
+
+export default MachineNameContainer
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
new file mode 100644
index 00000000..b04e3118
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/MachineSidebarContainer.js
@@ -0,0 +1,15 @@
+import { connect } from 'react-redux'
+import MachineSidebarComponent from '../../../../../components/app/sidebars/topology/machine/MachineSidebarComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ machineId:
+ state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rackId].machineIds[
+ state.interactionLevel.position - 1
+ ],
+ }
+}
+
+const MachineSidebarContainer = connect(mapStateToProps)(MachineSidebarComponent)
+
+export default MachineSidebarContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitAddContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitAddContainer.js
new file mode 100644
index 00000000..29e48016
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitAddContainer.js
@@ -0,0 +1,19 @@
+import { connect } from 'react-redux'
+import { addUnit } from '../../../../../actions/topology/machine'
+import UnitAddComponent from '../../../../../components/app/sidebars/topology/machine/UnitAddComponent'
+
+const mapStateToProps = (state, ownProps) => {
+ return {
+ units: Object.values(state.objects[ownProps.unitType]),
+ }
+}
+
+const mapDispatchToProps = (dispatch, ownProps) => {
+ return {
+ onAdd: (id) => dispatch(addUnit(ownProps.unitType, id)),
+ }
+}
+
+const UnitAddContainer = connect(mapStateToProps, mapDispatchToProps)(UnitAddComponent)
+
+export default UnitAddContainer
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
new file mode 100644
index 00000000..f334f9f2
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitContainer.js
@@ -0,0 +1,20 @@
+import { connect } from 'react-redux'
+import { deleteUnit } from '../../../../../actions/topology/machine'
+import UnitComponent from '../../../../../components/app/sidebars/topology/machine/UnitComponent'
+
+const mapStateToProps = (state, ownProps) => {
+ return {
+ unit: state.objects[ownProps.unitType][ownProps.unitId],
+ index: ownProps.unitId,
+ }
+}
+
+const mapDispatchToProps = (dispatch, ownProps) => {
+ return {
+ onDelete: () => dispatch(deleteUnit(ownProps.unitType, ownProps.index)),
+ }
+}
+
+const UnitContainer = connect(mapStateToProps, mapDispatchToProps)(UnitComponent)
+
+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
new file mode 100644
index 00000000..f382ff74
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitListContainer.js
@@ -0,0 +1,17 @@
+import { connect } from 'react-redux'
+import UnitListComponent from '../../../../../components/app/sidebars/topology/machine/UnitListComponent'
+
+const mapStateToProps = (state, ownProps) => {
+ return {
+ unitIds:
+ state.objects.machine[
+ state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rackId].machineIds[
+ state.interactionLevel.position - 1
+ ]
+ ][ownProps.unitType + 'Ids'],
+ }
+}
+
+const UnitListContainer = connect(mapStateToProps)(UnitListComponent)
+
+export default UnitListContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitTabsContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitTabsContainer.js
new file mode 100644
index 00000000..00fe4067
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/machine/UnitTabsContainer.js
@@ -0,0 +1,5 @@
+import UnitTabsComponent from '../../../../../components/app/sidebars/topology/machine/UnitTabsComponent'
+
+const UnitTabsContainer = UnitTabsComponent
+
+export default UnitTabsContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/AddPrefabContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/AddPrefabContainer.js
new file mode 100644
index 00000000..c941e745
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/AddPrefabContainer.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import { addPrefab } from '../../../../../actions/prefabs'
+import AddPrefabComponent from '../../../../../components/app/sidebars/topology/rack/AddPrefabComponent'
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onClick: () => dispatch(addPrefab('name')),
+ }
+}
+
+const AddPrefabContainer = connect(undefined, mapDispatchToProps)(AddPrefabComponent)
+
+export default AddPrefabContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/BackToRoomContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/BackToRoomContainer.js
new file mode 100644
index 00000000..58c3b082
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/BackToRoomContainer.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import { goDownOneInteractionLevel } from '../../../../../actions/interaction-level'
+import BackToRoomComponent from '../../../../../components/app/sidebars/topology/rack/BackToRoomComponent'
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onClick: () => dispatch(goDownOneInteractionLevel()),
+ }
+}
+
+const BackToRoomContainer = connect(undefined, mapDispatchToProps)(BackToRoomComponent)
+
+export default BackToRoomContainer
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
new file mode 100644
index 00000000..8229a359
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/DeleteRackContainer.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import { openDeleteRackModal } from '../../../../../actions/modals/topology'
+import DeleteRackComponent from '../../../../../components/app/sidebars/topology/rack/DeleteRackComponent'
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onClick: () => dispatch(openDeleteRackModal()),
+ }
+}
+
+const DeleteRackContainer = connect(undefined, mapDispatchToProps)(DeleteRackComponent)
+
+export default DeleteRackContainer
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
new file mode 100644
index 00000000..cf341da9
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/EmptySlotContainer.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import { addMachine } from '../../../../../actions/topology/rack'
+import EmptySlotComponent from '../../../../../components/app/sidebars/topology/rack/EmptySlotComponent'
+
+const mapDispatchToProps = (dispatch, ownProps) => {
+ return {
+ onAdd: () => dispatch(addMachine(ownProps.position)),
+ }
+}
+
+const EmptySlotContainer = connect(undefined, mapDispatchToProps)(EmptySlotComponent)
+
+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
new file mode 100644
index 00000000..fe12827d
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineContainer.js
@@ -0,0 +1,19 @@
+import { connect } from 'react-redux'
+import { goFromRackToMachine } from '../../../../../actions/interaction-level'
+import MachineComponent from '../../../../../components/app/sidebars/topology/rack/MachineComponent'
+
+const mapStateToProps = (state, ownProps) => {
+ return {
+ machine: state.objects.machine[ownProps.machineId],
+ }
+}
+
+const mapDispatchToProps = (dispatch, ownProps) => {
+ return {
+ onClick: () => dispatch(goFromRackToMachine(ownProps.position)),
+ }
+}
+
+const MachineContainer = connect(mapStateToProps, mapDispatchToProps)(MachineComponent)
+
+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
new file mode 100644
index 00000000..bc5a285a
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/MachineListContainer.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import MachineListComponent from '../../../../../components/app/sidebars/topology/rack/MachineListComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ machineIds: state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rackId].machineIds,
+ }
+}
+
+const MachineListContainer = connect(mapStateToProps)(MachineListComponent)
+
+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
new file mode 100644
index 00000000..504dbc61
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackNameContainer.js
@@ -0,0 +1,19 @@
+import { connect } from 'react-redux'
+import { openEditRackNameModal } from '../../../../../actions/modals/topology'
+import RackNameComponent from '../../../../../components/app/sidebars/topology/rack/RackNameComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ rackName: state.objects.rack[state.objects.tile[state.interactionLevel.tileId].rackId].name,
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onEdit: () => dispatch(openEditRackNameModal()),
+ }
+}
+
+const RackNameContainer = connect(mapStateToProps, mapDispatchToProps)(RackNameComponent)
+
+export default RackNameContainer
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
new file mode 100644
index 00000000..453d7e41
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/rack/RackSidebarContainer.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import RackSidebarComponent from '../../../../../components/app/sidebars/topology/rack/RackSidebarComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ rackId: state.objects.tile[state.interactionLevel.tileId].rackId,
+ }
+}
+
+const RackSidebarContainer = connect(mapStateToProps)(RackSidebarComponent)
+
+export default RackSidebarContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/BackToBuildingContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/BackToBuildingContainer.js
new file mode 100644
index 00000000..4c1ab99d
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/BackToBuildingContainer.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import { goDownOneInteractionLevel } from '../../../../../actions/interaction-level'
+import BackToBuildingComponent from '../../../../../components/app/sidebars/topology/room/BackToBuildingComponent'
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onClick: () => dispatch(goDownOneInteractionLevel()),
+ }
+}
+
+const BackToBuildingContainer = connect(undefined, mapDispatchToProps)(BackToBuildingComponent)
+
+export default BackToBuildingContainer
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
new file mode 100644
index 00000000..636fa5c5
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/DeleteRoomContainer.js
@@ -0,0 +1,13 @@
+import { connect } from 'react-redux'
+import { openDeleteRoomModal } from '../../../../../actions/modals/topology'
+import DeleteRoomComponent from '../../../../../components/app/sidebars/topology/room/DeleteRoomComponent'
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onClick: () => dispatch(openDeleteRoomModal()),
+ }
+}
+
+const DeleteRoomContainer = connect(undefined, mapDispatchToProps)(DeleteRoomComponent)
+
+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
new file mode 100644
index 00000000..d17a45d1
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/EditRoomContainer.js
@@ -0,0 +1,21 @@
+import { connect } from 'react-redux'
+import { finishRoomEdit, startRoomEdit } from '../../../../../actions/topology/building'
+import EditRoomComponent from '../../../../../components/app/sidebars/topology/room/EditRoomComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ isEditing: state.construction.currentRoomInConstruction !== '-1',
+ isInRackConstructionMode: state.construction.inRackConstructionMode,
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onEdit: () => dispatch(startRoomEdit()),
+ onFinish: () => dispatch(finishRoomEdit()),
+ }
+}
+
+const EditRoomContainer = connect(mapStateToProps, mapDispatchToProps)(EditRoomComponent)
+
+export default EditRoomContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RackConstructionContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RackConstructionContainer.js
new file mode 100644
index 00000000..cd8319de
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RackConstructionContainer.js
@@ -0,0 +1,21 @@
+import { connect } from 'react-redux'
+import { startRackConstruction, stopRackConstruction } from '../../../../../actions/topology/room'
+import RackConstructionComponent from '../../../../../components/app/sidebars/topology/room/RackConstructionComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ inRackConstructionMode: state.construction.inRackConstructionMode,
+ isEditingRoom: state.construction.currentRoomInConstruction !== '-1',
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onStart: () => dispatch(startRackConstruction()),
+ onStop: () => dispatch(stopRackConstruction()),
+ }
+}
+
+const RackConstructionContainer = connect(mapStateToProps, mapDispatchToProps)(RackConstructionComponent)
+
+export default RackConstructionContainer
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
new file mode 100644
index 00000000..cab16016
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomNameContainer.js
@@ -0,0 +1,19 @@
+import { connect } from 'react-redux'
+import { openEditRoomNameModal } from '../../../../../actions/modals/topology'
+import RoomNameComponent from '../../../../../components/app/sidebars/topology/room/RoomNameComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ roomName: state.objects.room[state.interactionLevel.roomId].name,
+ }
+}
+
+const mapDispatchToProps = (dispatch) => {
+ return {
+ onEdit: () => dispatch(openEditRoomNameModal()),
+ }
+}
+
+const RoomNameContainer = connect(mapStateToProps, mapDispatchToProps)(RoomNameComponent)
+
+export default RoomNameContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomSidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomSidebarContainer.js
new file mode 100644
index 00000000..8c3ca8ab
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/topology/room/RoomSidebarContainer.js
@@ -0,0 +1,12 @@
+import { connect } from 'react-redux'
+import RoomSidebarComponent from '../../../../../components/app/sidebars/topology/room/RoomSidebarComponent'
+
+const mapStateToProps = (state) => {
+ return {
+ roomId: state.interactionLevel.roomId,
+ }
+}
+
+const RoomSidebarContainer = connect(mapStateToProps)(RoomSidebarComponent)
+
+export default RoomSidebarContainer