summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-ui/src/components/app/sidebars/topology
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-web/opendc-web-ui/src/components/app/sidebars/topology')
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/NameComponent.js13
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebarComponent.js31
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js13
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js26
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/BackToRackComponent.js10
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js10
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineNameComponent.js5
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js18
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddComponent.js35
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitComponent.js52
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitListComponent.js20
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitTabsComponent.js78
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabComponent.js10
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/BackToRoomComponent.js10
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/DeleteRackComponent.js10
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/EmptySlotComponent.js13
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineComponent.js43
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.js20
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.sass2
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackNameComponent.js6
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.js25
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.sass11
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/BackToBuildingComponent.js10
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomComponent.js10
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/EditRoomComponent.js22
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionComponent.js27
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomNameComponent.js6
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomSidebarComponent.js20
28 files changed, 556 insertions, 0 deletions
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/NameComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/NameComponent.js
new file mode 100644
index 00000000..5fb0dc55
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/NameComponent.js
@@ -0,0 +1,13 @@
+import React from 'react'
+import FontAwesome from 'react-fontawesome'
+
+const NameComponent = ({ name, onEdit }) => (
+ <h2>
+ {name}
+ <button className="btn btn-outline-secondary float-right" onClick={onEdit}>
+ <FontAwesome name="pencil" />
+ </button>
+ </h2>
+)
+
+export default NameComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebarComponent.js
new file mode 100644
index 00000000..f5eee36b
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/TopologySidebarComponent.js
@@ -0,0 +1,31 @@
+import React from 'react'
+import BuildingSidebarContainer from '../../../../containers/app/sidebars/topology/building/BuildingSidebarContainer'
+import MachineSidebarContainer from '../../../../containers/app/sidebars/topology/machine/MachineSidebarContainer'
+import RackSidebarContainer from '../../../../containers/app/sidebars/topology/rack/RackSidebarContainer'
+import RoomSidebarContainer from '../../../../containers/app/sidebars/topology/room/RoomSidebarContainer'
+import Sidebar from '../Sidebar'
+
+const TopologySidebarComponent = ({ interactionLevel }) => {
+ let sidebarContent
+
+ switch (interactionLevel.mode) {
+ case 'BUILDING':
+ sidebarContent = <BuildingSidebarContainer />
+ break
+ case 'ROOM':
+ sidebarContent = <RoomSidebarContainer />
+ break
+ case 'RACK':
+ sidebarContent = <RackSidebarContainer />
+ break
+ case 'MACHINE':
+ sidebarContent = <MachineSidebarContainer />
+ break
+ default:
+ sidebarContent = 'Missing Content'
+ }
+
+ return <Sidebar isRight={true}>{sidebarContent}</Sidebar>
+}
+
+export default TopologySidebarComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js
new file mode 100644
index 00000000..eea62f84
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js
@@ -0,0 +1,13 @@
+import React from 'react'
+import NewRoomConstructionContainer from '../../../../../containers/app/sidebars/topology/building/NewRoomConstructionContainer'
+
+const BuildingSidebarComponent = () => {
+ return (
+ <div>
+ <h2>Building</h2>
+ <NewRoomConstructionContainer />
+ </div>
+ )
+}
+
+export default BuildingSidebarComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js
new file mode 100644
index 00000000..fd552c1e
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js
@@ -0,0 +1,26 @@
+import React from 'react'
+
+const NewRoomConstructionComponent = ({ onStart, onFinish, onCancel, currentRoomInConstruction }) => {
+ if (currentRoomInConstruction === '-1') {
+ return (
+ <div className="btn btn-outline-primary btn-block" onClick={onStart}>
+ <span className="fa fa-plus mr-2" />
+ Construct a new room
+ </div>
+ )
+ }
+ return (
+ <div>
+ <div className="btn btn-primary btn-block" onClick={onFinish}>
+ <span className="fa fa-check mr-2" />
+ Finalize new room
+ </div>
+ <div className="btn btn-default btn-block" onClick={onCancel}>
+ <span className="fa fa-times mr-2" />
+ Cancel construction
+ </div>
+ </div>
+ )
+}
+
+export default NewRoomConstructionComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/BackToRackComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/BackToRackComponent.js
new file mode 100644
index 00000000..70d522b2
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/BackToRackComponent.js
@@ -0,0 +1,10 @@
+import React from 'react'
+
+const BackToRackComponent = ({ onClick }) => (
+ <div className="btn btn-secondary btn-block" onClick={onClick}>
+ <span className="fa fa-angle-left mr-2" />
+ Back to rack
+ </div>
+)
+
+export default BackToRackComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js
new file mode 100644
index 00000000..37820316
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js
@@ -0,0 +1,10 @@
+import React from 'react'
+
+const DeleteMachineComponent = ({ onClick }) => (
+ <div className="btn btn-outline-danger btn-block" onClick={onClick}>
+ <span className="fa fa-trash mr-2" />
+ Delete this machine
+ </div>
+)
+
+export default DeleteMachineComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineNameComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineNameComponent.js
new file mode 100644
index 00000000..992383c4
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineNameComponent.js
@@ -0,0 +1,5 @@
+import React from 'react'
+
+const MachineNameComponent = ({ position }) => <h2>Machine at slot {position}</h2>
+
+export default MachineNameComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js
new file mode 100644
index 00000000..7c78cf9e
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js
@@ -0,0 +1,18 @@
+import React from 'react'
+import BackToRackContainer from '../../../../../containers/app/sidebars/topology/machine/BackToRackContainer'
+import DeleteMachineContainer from '../../../../../containers/app/sidebars/topology/machine/DeleteMachineContainer'
+import MachineNameContainer from '../../../../../containers/app/sidebars/topology/machine/MachineNameContainer'
+import UnitTabsContainer from '../../../../../containers/app/sidebars/topology/machine/UnitTabsContainer'
+
+const MachineSidebarComponent = ({ machineId }) => {
+ return (
+ <div className="h-100 overflow-auto">
+ <MachineNameContainer />
+ <BackToRackContainer />
+ <DeleteMachineContainer />
+ <UnitTabsContainer />
+ </div>
+ )
+}
+
+export default MachineSidebarComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddComponent.js
new file mode 100644
index 00000000..4e9dbc7e
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddComponent.js
@@ -0,0 +1,35 @@
+import PropTypes from 'prop-types'
+import React from 'react'
+
+class UnitAddComponent extends React.Component {
+ static propTypes = {
+ units: PropTypes.array.isRequired,
+ onAdd: PropTypes.func.isRequired,
+ }
+
+ render() {
+ return (
+ <div className="form-inline">
+ <div className="form-group w-100">
+ <select className="form-control w-70 mr-1" ref={(unitSelect) => (this.unitSelect = unitSelect)}>
+ {this.props.units.map((unit) => (
+ <option value={unit._id} key={unit._id}>
+ {unit.name}
+ </option>
+ ))}
+ </select>
+ <button
+ type="submit"
+ className="btn btn-outline-primary"
+ onClick={() => this.props.onAdd(this.unitSelect.value)}
+ >
+ <span className="fa fa-plus mr-2" />
+ Add
+ </button>
+ </div>
+ </div>
+ )
+ }
+}
+
+export default UnitAddComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitComponent.js
new file mode 100644
index 00000000..de55e506
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitComponent.js
@@ -0,0 +1,52 @@
+import React from 'react'
+import { UncontrolledPopover, PopoverHeader, PopoverBody, Button } from 'reactstrap'
+
+function UnitComponent({ index, unitType, unit, onDelete }) {
+ let unitInfo
+ if (unitType === 'cpu' || unitType === 'gpu') {
+ unitInfo = (
+ <>
+ <strong>Clockrate: </strong>
+ <code>{unit.clockRateMhz}</code>
+ <br />
+ <strong>Num. Cores: </strong>
+ <code>{unit.numberOfCores}</code>
+ <br />
+ <strong>Energy Cons.: </strong>
+ <code>{unit.energyConsumptionW} W</code>
+ <br />
+ </>
+ )
+ } else if (unitType === 'memory' || unitType === 'storage') {
+ unitInfo = (
+ <>
+ <strong>Speed:</strong>
+ <code>{unit.speedMbPerS} Mb/s</code>
+ <br />
+ <strong>Size:</strong>
+ <code>{unit.sizeMb} MB</code>
+ <br />
+ <strong>Energy Cons.:</strong>
+ <code>{unit.energyConsumptionW} W</code>
+ <br />
+ </>
+ )
+ }
+
+ return (
+ <li className="d-flex list-group-item justify-content-between align-items-center">
+ <span style={{ maxWidth: '60%' }}>{unit.name}</span>
+ <span>
+ <Button outline={true} color="info" className="mr-1 fa fa-info-circle" id={`unit-${index}`} />
+ <UncontrolledPopover trigger="focus" placement="left" target={`unit-${index}`}>
+ <PopoverHeader>Unit Information</PopoverHeader>
+ <PopoverBody>{unitInfo}</PopoverBody>
+ </UncontrolledPopover>
+
+ <span className="btn btn-outline-danger fa fa-trash" onClick={onDelete} />
+ </span>
+ </li>
+ )
+}
+
+export default UnitComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitListComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitListComponent.js
new file mode 100644
index 00000000..2ade0f6a
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitListComponent.js
@@ -0,0 +1,20 @@
+import React from 'react'
+import UnitContainer from '../../../../../containers/app/sidebars/topology/machine/UnitContainer'
+
+const UnitListComponent = ({ unitType, unitIds }) => (
+ <ul className="list-group mt-1">
+ {unitIds.length !== 0 ? (
+ unitIds.map((unitId, index) => (
+ <UnitContainer unitType={unitType} unitId={unitId} index={index} key={index} />
+ ))
+ ) : (
+ <div className="alert alert-info">
+ <span>
+ <strong>No units...</strong> Add some with the menu above!
+ </span>
+ </div>
+ )}
+ </ul>
+)
+
+export default UnitListComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitTabsComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitTabsComponent.js
new file mode 100644
index 00000000..6599fefd
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitTabsComponent.js
@@ -0,0 +1,78 @@
+import React, { useState } from 'react'
+import { Nav, NavItem, NavLink, TabContent, TabPane } from 'reactstrap'
+import UnitAddContainer from '../../../../../containers/app/sidebars/topology/machine/UnitAddContainer'
+import UnitListContainer from '../../../../../containers/app/sidebars/topology/machine/UnitListContainer'
+
+const UnitTabsComponent = () => {
+ const [activeTab, setActiveTab] = useState('cpu-units')
+ const toggle = (tab) => {
+ if (activeTab !== tab) setActiveTab(tab)
+ }
+
+ return (
+ <div>
+ <Nav tabs>
+ <NavItem>
+ <NavLink
+ className={activeTab === 'cpu-units' ? 'active' : ''}
+ onClick={() => {
+ toggle('cpu-units')
+ }}
+ >
+ CPU
+ </NavLink>
+ </NavItem>
+ <NavItem>
+ <NavLink
+ className={activeTab === 'gpu-units' ? 'active' : ''}
+ onClick={() => {
+ toggle('gpu-units')
+ }}
+ >
+ GPU
+ </NavLink>
+ </NavItem>
+ <NavItem>
+ <NavLink
+ className={activeTab === 'memory-units' ? 'active' : ''}
+ onClick={() => {
+ toggle('memory-units')
+ }}
+ >
+ Memory
+ </NavLink>
+ </NavItem>
+ <NavItem>
+ <NavLink
+ className={activeTab === 'storage-units' ? 'active' : ''}
+ onClick={() => {
+ toggle('storage-units')
+ }}
+ >
+ Stor.
+ </NavLink>
+ </NavItem>
+ </Nav>
+ <TabContent activeTab={activeTab}>
+ <TabPane tabId="cpu-units">
+ <UnitAddContainer unitType="cpu" />
+ <UnitListContainer unitType="cpu" />
+ </TabPane>
+ <TabPane tabId="gpu-units">
+ <UnitAddContainer unitType="gpu" />
+ <UnitListContainer unitType="gpu" />
+ </TabPane>
+ <TabPane tabId="memory-units">
+ <UnitAddContainer unitType="memory" />
+ <UnitListContainer unitType="memory" />
+ </TabPane>
+ <TabPane tabId="storage-units">
+ <UnitAddContainer unitType="storage" />
+ <UnitListContainer unitType="storage" />
+ </TabPane>
+ </TabContent>
+ </div>
+ )
+}
+
+export default UnitTabsComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabComponent.js
new file mode 100644
index 00000000..75418f9d
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabComponent.js
@@ -0,0 +1,10 @@
+import React from 'react'
+
+const AddPrefabComponent = ({ onClick }) => (
+ <div className="btn btn-primary btn-block" onClick={onClick}>
+ <span className="fa fa-floppy-o mr-2" />
+ Save this rack to a prefab
+ </div>
+)
+
+export default AddPrefabComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/BackToRoomComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/BackToRoomComponent.js
new file mode 100644
index 00000000..c14775bf
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/BackToRoomComponent.js
@@ -0,0 +1,10 @@
+import React from 'react'
+
+const BackToRoomComponent = ({ onClick }) => (
+ <div className="btn btn-secondary btn-block mb-2" onClick={onClick}>
+ <span className="fa fa-angle-left mr-2" />
+ Back to room
+ </div>
+)
+
+export default BackToRoomComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/DeleteRackComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/DeleteRackComponent.js
new file mode 100644
index 00000000..23b0daac
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/DeleteRackComponent.js
@@ -0,0 +1,10 @@
+import React from 'react'
+
+const DeleteRackComponent = ({ onClick }) => (
+ <div className="btn btn-outline-danger btn-block" onClick={onClick}>
+ <span className="fa fa-trash mr-2" />
+ Delete this rack
+ </div>
+)
+
+export default DeleteRackComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/EmptySlotComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/EmptySlotComponent.js
new file mode 100644
index 00000000..d7e30f1d
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/EmptySlotComponent.js
@@ -0,0 +1,13 @@
+import React from 'react'
+
+const EmptySlotComponent = ({ position, onAdd }) => (
+ <li className="list-group-item d-flex justify-content-between align-items-center">
+ <span className="badge badge-default badge-info mr-1 disabled">{position}</span>
+ <button className="btn btn-outline-primary" onClick={onAdd}>
+ <span className="fa fa-plus mr-2" />
+ Add machine
+ </button>
+ </li>
+)
+
+export default EmptySlotComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineComponent.js
new file mode 100644
index 00000000..caa3dc04
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineComponent.js
@@ -0,0 +1,43 @@
+import React from 'react'
+import Shapes from '../../../../../shapes'
+
+const UnitIcon = ({ id, type }) => (
+ <div>
+ <img
+ src={'/img/topology/' + id + '-icon.png'}
+ alt={'Machine contains ' + type + ' units'}
+ className="img-fluid ml-1"
+ style={{ maxHeight: '35px' }}
+ />
+ </div>
+)
+
+const MachineComponent = ({ position, machine, onClick }) => {
+ const hasNoUnits =
+ machine.cpuIds.length + machine.gpuIds.length + machine.memoryIds.length + machine.storageIds.length === 0
+
+ return (
+ <li
+ className="d-flex list-group-item list-group-item-action justify-content-between align-items-center"
+ onClick={onClick}
+ style={{ backgroundColor: 'white' }}
+ >
+ <span className="badge badge-default badge-info mr-1">{position}</span>
+ <div className="d-inline-flex">
+ {machine.cpuIds.length > 0 ? <UnitIcon id="cpu" type="CPU" /> : undefined}
+ {machine.gpuIds.length > 0 ? <UnitIcon id="gpu" type="GPU" /> : undefined}
+ {machine.memoryIds.length > 0 ? <UnitIcon id="memory" type="memory" /> : undefined}
+ {machine.storageIds.length > 0 ? <UnitIcon id="storage" type="storage" /> : undefined}
+ {hasNoUnits ? (
+ <span className="badge badge-default badge-warning">Machine with no units</span>
+ ) : undefined}
+ </div>
+ </li>
+ )
+}
+
+MachineComponent.propTypes = {
+ machine: Shapes.Machine,
+}
+
+export default MachineComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.js
new file mode 100644
index 00000000..12be26bd
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.js
@@ -0,0 +1,20 @@
+import React from 'react'
+import EmptySlotContainer from '../../../../../containers/app/sidebars/topology/rack/EmptySlotContainer'
+import MachineContainer from '../../../../../containers/app/sidebars/topology/rack/MachineContainer'
+import './MachineListComponent.sass'
+
+const MachineListComponent = ({ machineIds }) => {
+ return (
+ <ul className="list-group machine-list">
+ {machineIds.map((machineId, index) => {
+ if (machineId === null) {
+ return <EmptySlotContainer key={index} position={index + 1} />
+ } else {
+ return <MachineContainer key={index} position={index + 1} machineId={machineId} />
+ }
+ })}
+ </ul>
+ )
+}
+
+export default MachineListComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.sass b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.sass
new file mode 100644
index 00000000..11b82c93
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.sass
@@ -0,0 +1,2 @@
+.machine-list li
+ min-height: 64px
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackNameComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackNameComponent.js
new file mode 100644
index 00000000..b701909a
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackNameComponent.js
@@ -0,0 +1,6 @@
+import React from 'react'
+import NameComponent from '../NameComponent'
+
+const RackNameComponent = ({ rackName, onEdit }) => <NameComponent name={rackName} onEdit={onEdit} />
+
+export default RackNameComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.js
new file mode 100644
index 00000000..ca41bf57
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.js
@@ -0,0 +1,25 @@
+import React from 'react'
+import BackToRoomContainer from '../../../../../containers/app/sidebars/topology/rack/BackToRoomContainer'
+import DeleteRackContainer from '../../../../../containers/app/sidebars/topology/rack/DeleteRackContainer'
+import MachineListContainer from '../../../../../containers/app/sidebars/topology/rack/MachineListContainer'
+import RackNameContainer from '../../../../../containers/app/sidebars/topology/rack/RackNameContainer'
+import './RackSidebarComponent.sass'
+import AddPrefabContainer from '../../../../../containers/app/sidebars/topology/rack/AddPrefabContainer'
+
+const RackSidebarComponent = () => {
+ return (
+ <div className="rack-sidebar-container flex-column">
+ <div className="rack-sidebar-header-container">
+ <RackNameContainer />
+ <BackToRoomContainer />
+ <AddPrefabContainer />
+ <DeleteRackContainer />
+ </div>
+ <div className="machine-list-container mt-2">
+ <MachineListContainer />
+ </div>
+ </div>
+ )
+}
+
+export default RackSidebarComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.sass b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.sass
new file mode 100644
index 00000000..29fec02a
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.sass
@@ -0,0 +1,11 @@
+.rack-sidebar-container
+ display: flex
+ height: 100%
+ max-height: 100%
+
+.rack-sidebar-header-container
+ flex: 0
+
+.machine-list-container
+ flex: 1
+ overflow-y: scroll
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/BackToBuildingComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/BackToBuildingComponent.js
new file mode 100644
index 00000000..64c0a1f6
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/BackToBuildingComponent.js
@@ -0,0 +1,10 @@
+import React from 'react'
+
+const BackToBuildingComponent = ({ onClick }) => (
+ <div className="btn btn-secondary btn-block mb-2" onClick={onClick}>
+ <span className="fa fa-angle-left mr-2" />
+ Back to building
+ </div>
+)
+
+export default BackToBuildingComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomComponent.js
new file mode 100644
index 00000000..78417359
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomComponent.js
@@ -0,0 +1,10 @@
+import React from 'react'
+
+const DeleteRoomComponent = ({ onClick }) => (
+ <div className="btn btn-outline-danger btn-block" onClick={onClick}>
+ <span className="fa fa-trash mr-2" />
+ Delete this room
+ </div>
+)
+
+export default DeleteRoomComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/EditRoomComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/EditRoomComponent.js
new file mode 100644
index 00000000..857a646f
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/EditRoomComponent.js
@@ -0,0 +1,22 @@
+import classNames from 'classnames'
+import React from 'react'
+
+const EditRoomComponent = ({ onEdit, onFinish, isEditing, isInRackConstructionMode }) =>
+ isEditing ? (
+ <div className="btn btn-info btn-block" onClick={onFinish}>
+ <span className="fa fa-check mr-2" />
+ Finish editing room
+ </div>
+ ) : (
+ <div
+ className={classNames('btn btn-outline-info btn-block', {
+ disabled: isInRackConstructionMode,
+ })}
+ onClick={() => (isInRackConstructionMode ? undefined : onEdit())}
+ >
+ <span className="fa fa-pencil mr-2" />
+ Edit the tiles of this room
+ </div>
+ )
+
+export default EditRoomComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionComponent.js
new file mode 100644
index 00000000..44566f61
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionComponent.js
@@ -0,0 +1,27 @@
+import classNames from 'classnames'
+import React from 'react'
+
+const RackConstructionComponent = ({ onStart, onStop, inRackConstructionMode, isEditingRoom }) => {
+ if (inRackConstructionMode) {
+ return (
+ <div className="btn btn-primary btn-block" onClick={onStop}>
+ <span className="fa fa-times mr-2" />
+ Stop rack construction
+ </div>
+ )
+ }
+
+ return (
+ <div
+ className={classNames('btn btn-outline-primary btn-block', {
+ disabled: isEditingRoom,
+ })}
+ onClick={() => (isEditingRoom ? undefined : onStart())}
+ >
+ <span className="fa fa-plus mr-2" />
+ Start rack construction
+ </div>
+ )
+}
+
+export default RackConstructionComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomNameComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomNameComponent.js
new file mode 100644
index 00000000..d637828e
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomNameComponent.js
@@ -0,0 +1,6 @@
+import React from 'react'
+import NameComponent from '../NameComponent'
+
+const RoomNameComponent = ({ roomName, onEdit }) => <NameComponent name={roomName} onEdit={onEdit} />
+
+export default RoomNameComponent
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomSidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomSidebarComponent.js
new file mode 100644
index 00000000..1bc6533e
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomSidebarComponent.js
@@ -0,0 +1,20 @@
+import React from 'react'
+import BackToBuildingContainer from '../../../../../containers/app/sidebars/topology/room/BackToBuildingContainer'
+import DeleteRoomContainer from '../../../../../containers/app/sidebars/topology/room/DeleteRoomContainer'
+import EditRoomContainer from '../../../../../containers/app/sidebars/topology/room/EditRoomContainer'
+import RackConstructionContainer from '../../../../../containers/app/sidebars/topology/room/RackConstructionContainer'
+import RoomNameContainer from '../../../../../containers/app/sidebars/topology/room/RoomNameContainer'
+
+const RoomSidebarComponent = () => {
+ return (
+ <div>
+ <RoomNameContainer />
+ <BackToBuildingContainer />
+ <RackConstructionContainer />
+ <EditRoomContainer />
+ <DeleteRoomContainer />
+ </div>
+ )
+}
+
+export default RoomSidebarComponent