summaryrefslogtreecommitdiff
path: root/frontend/src/components/app/sidebars/topology
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/components/app/sidebars/topology')
-rw-r--r--frontend/src/components/app/sidebars/topology/NameComponent.js13
-rw-r--r--frontend/src/components/app/sidebars/topology/TopologySidebarComponent.js31
-rw-r--r--frontend/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js20
-rw-r--r--frontend/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js31
-rw-r--r--frontend/src/components/app/sidebars/topology/machine/BackToRackComponent.js10
-rw-r--r--frontend/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js10
-rw-r--r--frontend/src/components/app/sidebars/topology/machine/MachineNameComponent.js7
-rw-r--r--frontend/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js27
-rw-r--r--frontend/src/components/app/sidebars/topology/machine/UnitAddComponent.js46
-rw-r--r--frontend/src/components/app/sidebars/topology/machine/UnitComponent.js78
-rw-r--r--frontend/src/components/app/sidebars/topology/machine/UnitListComponent.js29
-rw-r--r--frontend/src/components/app/sidebars/topology/machine/UnitTabsComponent.js65
-rw-r--r--frontend/src/components/app/sidebars/topology/rack/BackToRoomComponent.js10
-rw-r--r--frontend/src/components/app/sidebars/topology/rack/DeleteRackComponent.js10
-rw-r--r--frontend/src/components/app/sidebars/topology/rack/EmptySlotComponent.js19
-rw-r--r--frontend/src/components/app/sidebars/topology/rack/MachineComponent.js78
-rw-r--r--frontend/src/components/app/sidebars/topology/rack/MachineListComponent.js26
-rw-r--r--frontend/src/components/app/sidebars/topology/rack/MachineListComponent.sass2
-rw-r--r--frontend/src/components/app/sidebars/topology/rack/RackNameComponent.js8
-rw-r--r--frontend/src/components/app/sidebars/topology/rack/RackSidebarComponent.js34
-rw-r--r--frontend/src/components/app/sidebars/topology/rack/RackSidebarComponent.sass11
-rw-r--r--frontend/src/components/app/sidebars/topology/room/BackToBuildingComponent.js10
-rw-r--r--frontend/src/components/app/sidebars/topology/room/DeleteRoomComponent.js10
-rw-r--r--frontend/src/components/app/sidebars/topology/room/EditRoomComponent.js27
-rw-r--r--frontend/src/components/app/sidebars/topology/room/RackConstructionComponent.js32
-rw-r--r--frontend/src/components/app/sidebars/topology/room/RoomNameComponent.js8
-rw-r--r--frontend/src/components/app/sidebars/topology/room/RoomSidebarComponent.js38
-rw-r--r--frontend/src/components/app/sidebars/topology/room/RoomTypeComponent.js8
28 files changed, 698 insertions, 0 deletions
diff --git a/frontend/src/components/app/sidebars/topology/NameComponent.js b/frontend/src/components/app/sidebars/topology/NameComponent.js
new file mode 100644
index 00000000..805538b3
--- /dev/null
+++ b/frontend/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/frontend/src/components/app/sidebars/topology/TopologySidebarComponent.js b/frontend/src/components/app/sidebars/topology/TopologySidebarComponent.js
new file mode 100644
index 00000000..81e510a1
--- /dev/null
+++ b/frontend/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/frontend/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js b/frontend/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js
new file mode 100644
index 00000000..f16c19f0
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js
@@ -0,0 +1,20 @@
+import React from "react";
+import NewRoomConstructionContainer from "../../../../../containers/app/sidebars/topology/building/NewRoomConstructionContainer";
+
+const BuildingSidebarComponent = ({ inSimulation }) => {
+ return (
+ <div>
+ <h2>Building</h2>
+ {inSimulation ? (
+ <div className="alert alert-info">
+ <span className="fa fa-info-circle mr-2" />
+ <strong>Click on individual rooms</strong> to see their stats!
+ </div>
+ ) : (
+ <NewRoomConstructionContainer />
+ )}
+ </div>
+ );
+};
+
+export default BuildingSidebarComponent;
diff --git a/frontend/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js b/frontend/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js
new file mode 100644
index 00000000..7b049642
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js
@@ -0,0 +1,31 @@
+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/frontend/src/components/app/sidebars/topology/machine/BackToRackComponent.js b/frontend/src/components/app/sidebars/topology/machine/BackToRackComponent.js
new file mode 100644
index 00000000..7f56aca0
--- /dev/null
+++ b/frontend/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/frontend/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js b/frontend/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js
new file mode 100644
index 00000000..d8774bf9
--- /dev/null
+++ b/frontend/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/frontend/src/components/app/sidebars/topology/machine/MachineNameComponent.js b/frontend/src/components/app/sidebars/topology/machine/MachineNameComponent.js
new file mode 100644
index 00000000..0ad8b79c
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/machine/MachineNameComponent.js
@@ -0,0 +1,7 @@
+import React from "react";
+
+const MachineNameComponent = ({ position }) => (
+ <h2>Machine at slot {position}</h2>
+);
+
+export default MachineNameComponent;
diff --git a/frontend/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js b/frontend/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js
new file mode 100644
index 00000000..5ccaf25c
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js
@@ -0,0 +1,27 @@
+import React from "react";
+import LoadBarContainer from "../../../../../containers/app/sidebars/elements/LoadBarContainer";
+import LoadChartContainer from "../../../../../containers/app/sidebars/elements/LoadChartContainer";
+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 = ({ inSimulation, machineId }) => {
+ return (
+ <div>
+ <MachineNameContainer />
+ <BackToRackContainer />
+ {inSimulation ? (
+ <div>
+ <LoadBarContainer objectType="machine" objectId={machineId} />
+ <LoadChartContainer objectType="machine" objectId={machineId} />
+ </div>
+ ) : (
+ <DeleteMachineContainer />
+ )}
+ <UnitTabsContainer />
+ </div>
+ );
+};
+
+export default MachineSidebarComponent;
diff --git a/frontend/src/components/app/sidebars/topology/machine/UnitAddComponent.js b/frontend/src/components/app/sidebars/topology/machine/UnitAddComponent.js
new file mode 100644
index 00000000..0c903228
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/machine/UnitAddComponent.js
@@ -0,0 +1,46 @@
+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-75 mr-1"
+ ref={unitSelect => (this.unitSelect = unitSelect)}
+ >
+ {this.props.units.map(unit => (
+ <option value={unit.id} key={unit.id}>
+ {unit.manufacturer +
+ " " +
+ unit.family +
+ " " +
+ unit.model +
+ " " +
+ unit.generation}
+ </option>
+ ))}
+ </select>
+ <button
+ type="submit"
+ className="btn btn-outline-primary"
+ onClick={() =>
+ this.props.onAdd(parseInt(this.unitSelect.value, 10))
+ }
+ >
+ <span className="fa fa-plus mr-2" />
+ Add
+ </button>
+ </div>
+ </div>
+ );
+ }
+}
+
+export default UnitAddComponent;
diff --git a/frontend/src/components/app/sidebars/topology/machine/UnitComponent.js b/frontend/src/components/app/sidebars/topology/machine/UnitComponent.js
new file mode 100644
index 00000000..7c27043d
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/machine/UnitComponent.js
@@ -0,0 +1,78 @@
+import React from "react";
+import jQuery from "../../../../../util/jquery";
+
+class UnitComponent extends React.Component {
+ componentDidMount() {
+ jQuery(".unit-info-popover").popover({
+ trigger: "focus"
+ });
+ }
+
+ render() {
+ let unitInfo;
+ if (this.props.unitType === "cpu" || this.props.unitType === "gpu") {
+ unitInfo =
+ "<strong>Clockrate:</strong> <code>" +
+ this.props.unit.clockRateMhz +
+ " MHz</code><br/>" +
+ "<strong>Num. Cores:</strong> <code>" +
+ this.props.unit.numberOfCores +
+ "</code><br/>" +
+ "<strong>Energy Cons.:</strong> <code>" +
+ this.props.unit.energyConsumptionW +
+ " W</code>";
+ } else if (
+ this.props.unitType === "memory" ||
+ this.props.unitType === "storage"
+ ) {
+ unitInfo =
+ "<strong>Speed:</strong> <code>" +
+ this.props.unit.speedMbPerS +
+ " Mb/s</code><br/>" +
+ "<strong>Size:</strong> <code>" +
+ this.props.unit.sizeMb +
+ " MB</code><br/>" +
+ "<strong>Energy Cons.:</strong> <code> " +
+ this.props.unit.energyConsumptionW +
+ " W</code>";
+ }
+
+ return (
+ <li className="d-flex list-group-item justify-content-between align-items-center">
+ <span style={{ maxWidth: "60%" }}>
+ {this.props.unit.manufacturer +
+ " " +
+ this.props.unit.family +
+ " " +
+ this.props.unit.model +
+ " " +
+ this.props.unit.generation}
+ </span>
+ <span>
+ <span
+ tabIndex="0"
+ className="unit-info-popover btn btn-outline-info mr-1 fa fa-info-circle"
+ role="button"
+ data-toggle="popover"
+ data-trigger="focus"
+ title="Unit information"
+ data-content={unitInfo}
+ data-html="true"
+ />
+ {this.props.inSimulation ? (
+ undefined
+ ) : (
+ <span
+ className="btn btn-outline-danger"
+ onClick={this.props.onDelete}
+ >
+ <span className="fa fa-trash" />
+ </span>
+ )}
+ </span>
+ </li>
+ );
+ }
+}
+
+export default UnitComponent;
diff --git a/frontend/src/components/app/sidebars/topology/machine/UnitListComponent.js b/frontend/src/components/app/sidebars/topology/machine/UnitListComponent.js
new file mode 100644
index 00000000..38df806b
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/machine/UnitListComponent.js
@@ -0,0 +1,29 @@
+import React from "react";
+import UnitContainer from "../../../../../containers/app/sidebars/topology/machine/UnitContainer";
+
+const UnitListComponent = ({ unitType, unitIds, inSimulation }) => (
+ <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">
+ {inSimulation ? (
+ <strong>No units of this type in this machine</strong>
+ ) : (
+ <span>
+ <strong>No units...</strong> Add some with the menu above!
+ </span>
+ )}
+ </div>
+ )}
+ </ul>
+);
+
+export default UnitListComponent;
diff --git a/frontend/src/components/app/sidebars/topology/machine/UnitTabsComponent.js b/frontend/src/components/app/sidebars/topology/machine/UnitTabsComponent.js
new file mode 100644
index 00000000..0683c796
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/machine/UnitTabsComponent.js
@@ -0,0 +1,65 @@
+import React from "react";
+import UnitAddContainer from "../../../../../containers/app/sidebars/topology/machine/UnitAddContainer";
+import UnitListContainer from "../../../../../containers/app/sidebars/topology/machine/UnitListContainer";
+
+const UnitTabsComponent = ({ inSimulation }) => (
+ <div>
+ <ul className="nav nav-tabs mt-2 mb-1" role="tablist">
+ <li className="nav-item">
+ <a
+ className="nav-link active"
+ data-toggle="tab"
+ href="#cpu-units"
+ role="tab"
+ >
+ CPU
+ </a>
+ </li>
+ <li className="nav-item">
+ <a className="nav-link" data-toggle="tab" href="#gpu-units" role="tab">
+ GPU
+ </a>
+ </li>
+ <li className="nav-item">
+ <a
+ className="nav-link"
+ data-toggle="tab"
+ href="#memory-units"
+ role="tab"
+ >
+ Memory
+ </a>
+ </li>
+ <li className="nav-item">
+ <a
+ className="nav-link"
+ data-toggle="tab"
+ href="#storage-units"
+ role="tab"
+ >
+ Storage
+ </a>
+ </li>
+ </ul>
+ <div className="tab-content">
+ <div className="tab-pane active" id="cpu-units" role="tabpanel">
+ {inSimulation ? undefined : <UnitAddContainer unitType="cpu" />}
+ <UnitListContainer unitType="cpu" />
+ </div>
+ <div className="tab-pane" id="gpu-units" role="tabpanel">
+ {inSimulation ? undefined : <UnitAddContainer unitType="gpu" />}
+ <UnitListContainer unitType="gpu" />
+ </div>
+ <div className="tab-pane" id="memory-units" role="tabpanel">
+ {inSimulation ? undefined : <UnitAddContainer unitType="memory" />}
+ <UnitListContainer unitType="memory" />
+ </div>
+ <div className="tab-pane" id="storage-units" role="tabpanel">
+ {inSimulation ? undefined : <UnitAddContainer unitType="storage" />}
+ <UnitListContainer unitType="storage" />
+ </div>
+ </div>
+ </div>
+);
+
+export default UnitTabsComponent;
diff --git a/frontend/src/components/app/sidebars/topology/rack/BackToRoomComponent.js b/frontend/src/components/app/sidebars/topology/rack/BackToRoomComponent.js
new file mode 100644
index 00000000..6bcf4088
--- /dev/null
+++ b/frontend/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/frontend/src/components/app/sidebars/topology/rack/DeleteRackComponent.js b/frontend/src/components/app/sidebars/topology/rack/DeleteRackComponent.js
new file mode 100644
index 00000000..d8aa7634
--- /dev/null
+++ b/frontend/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/frontend/src/components/app/sidebars/topology/rack/EmptySlotComponent.js b/frontend/src/components/app/sidebars/topology/rack/EmptySlotComponent.js
new file mode 100644
index 00000000..d86f9fee
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/rack/EmptySlotComponent.js
@@ -0,0 +1,19 @@
+import React from "react";
+
+const EmptySlotComponent = ({ position, onAdd, inSimulation }) => (
+ <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>
+ {inSimulation ? (
+ <span className="badge badge-default badge-success">Empty Slot</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/frontend/src/components/app/sidebars/topology/rack/MachineComponent.js b/frontend/src/components/app/sidebars/topology/rack/MachineComponent.js
new file mode 100644
index 00000000..2521f4a2
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/rack/MachineComponent.js
@@ -0,0 +1,78 @@
+import React from "react";
+import Shapes from "../../../../../shapes";
+import { convertLoadToSimulationColor } from "../../../../../util/simulation-load";
+
+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,
+ inSimulation,
+ machineLoad,
+ onClick
+}) => {
+ let color = "white";
+ if (inSimulation && machineLoad >= 0) {
+ color = convertLoadToSimulationColor(machineLoad);
+ }
+ 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: color }}
+ >
+ <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/frontend/src/components/app/sidebars/topology/rack/MachineListComponent.js b/frontend/src/components/app/sidebars/topology/rack/MachineListComponent.js
new file mode 100644
index 00000000..d5521557
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/rack/MachineListComponent.js
@@ -0,0 +1,26 @@
+import React from "react";
+import EmptySlotContainer from "../../../../../containers/app/sidebars/topology/rack/EmptySlotContainer";
+import MachineContainer from "../../../../../containers/app/sidebars/topology/rack/MachineContainer";
+import "./MachineListComponent.css";
+
+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/frontend/src/components/app/sidebars/topology/rack/MachineListComponent.sass b/frontend/src/components/app/sidebars/topology/rack/MachineListComponent.sass
new file mode 100644
index 00000000..bbcfe696
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/rack/MachineListComponent.sass
@@ -0,0 +1,2 @@
+.machine-list li
+ min-height: 64px
diff --git a/frontend/src/components/app/sidebars/topology/rack/RackNameComponent.js b/frontend/src/components/app/sidebars/topology/rack/RackNameComponent.js
new file mode 100644
index 00000000..5e095823
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/rack/RackNameComponent.js
@@ -0,0 +1,8 @@
+import React from "react";
+import NameComponent from "../NameComponent";
+
+const RackNameComponent = ({ rackName, onEdit }) => (
+ <NameComponent name={rackName} onEdit={onEdit} />
+);
+
+export default RackNameComponent;
diff --git a/frontend/src/components/app/sidebars/topology/rack/RackSidebarComponent.js b/frontend/src/components/app/sidebars/topology/rack/RackSidebarComponent.js
new file mode 100644
index 00000000..f832b9b9
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/rack/RackSidebarComponent.js
@@ -0,0 +1,34 @@
+import React from "react";
+import LoadBarContainer from "../../../../../containers/app/sidebars/elements/LoadBarContainer";
+import LoadChartContainer from "../../../../../containers/app/sidebars/elements/LoadChartContainer";
+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.css";
+
+const RackSidebarComponent = ({ inSimulation, rackId }) => {
+ return (
+ <div className="rack-sidebar-container flex-column">
+ <div className="rack-sidebar-header-container">
+ <RackNameContainer />
+ <BackToRoomContainer />
+ {inSimulation ? (
+ <div>
+ <LoadBarContainer objectType="rack" objectId={rackId} />
+ <LoadChartContainer objectType="rack" objectId={rackId} />
+ </div>
+ ) : (
+ <div>
+ <DeleteRackContainer />
+ </div>
+ )}
+ </div>
+ <div className="machine-list-container mt-2">
+ <MachineListContainer />
+ </div>
+ </div>
+ );
+};
+
+export default RackSidebarComponent;
diff --git a/frontend/src/components/app/sidebars/topology/rack/RackSidebarComponent.sass b/frontend/src/components/app/sidebars/topology/rack/RackSidebarComponent.sass
new file mode 100644
index 00000000..822804bc
--- /dev/null
+++ b/frontend/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/frontend/src/components/app/sidebars/topology/room/BackToBuildingComponent.js b/frontend/src/components/app/sidebars/topology/room/BackToBuildingComponent.js
new file mode 100644
index 00000000..0409dbdd
--- /dev/null
+++ b/frontend/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/frontend/src/components/app/sidebars/topology/room/DeleteRoomComponent.js b/frontend/src/components/app/sidebars/topology/room/DeleteRoomComponent.js
new file mode 100644
index 00000000..3e3b3b36
--- /dev/null
+++ b/frontend/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/frontend/src/components/app/sidebars/topology/room/EditRoomComponent.js b/frontend/src/components/app/sidebars/topology/room/EditRoomComponent.js
new file mode 100644
index 00000000..c3b9f0ad
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/room/EditRoomComponent.js
@@ -0,0 +1,27 @@
+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/frontend/src/components/app/sidebars/topology/room/RackConstructionComponent.js b/frontend/src/components/app/sidebars/topology/room/RackConstructionComponent.js
new file mode 100644
index 00000000..06b8a2aa
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/room/RackConstructionComponent.js
@@ -0,0 +1,32 @@
+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/frontend/src/components/app/sidebars/topology/room/RoomNameComponent.js b/frontend/src/components/app/sidebars/topology/room/RoomNameComponent.js
new file mode 100644
index 00000000..11b88edd
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/room/RoomNameComponent.js
@@ -0,0 +1,8 @@
+import React from "react";
+import NameComponent from "../NameComponent";
+
+const RoomNameComponent = ({ roomName, onEdit }) => (
+ <NameComponent name={roomName} onEdit={onEdit} />
+);
+
+export default RoomNameComponent;
diff --git a/frontend/src/components/app/sidebars/topology/room/RoomSidebarComponent.js b/frontend/src/components/app/sidebars/topology/room/RoomSidebarComponent.js
new file mode 100644
index 00000000..275f9624
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/room/RoomSidebarComponent.js
@@ -0,0 +1,38 @@
+import React from "react";
+import LoadBarContainer from "../../../../../containers/app/sidebars/elements/LoadBarContainer";
+import LoadChartContainer from "../../../../../containers/app/sidebars/elements/LoadChartContainer";
+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";
+import RoomTypeContainer from "../../../../../containers/app/sidebars/topology/room/RoomTypeContainer";
+
+const RoomSidebarComponent = ({ roomId, roomType, inSimulation }) => {
+ let allowedObjects;
+ if (!inSimulation && roomType === "SERVER") {
+ allowedObjects = <RackConstructionContainer />;
+ }
+
+ return (
+ <div>
+ <RoomNameContainer />
+ <RoomTypeContainer />
+ <BackToBuildingContainer />
+ {inSimulation ? (
+ <div>
+ <LoadBarContainer objectType="room" objectId={roomId} />
+ <LoadChartContainer objectType="room" objectId={roomId} />
+ </div>
+ ) : (
+ <div>
+ {allowedObjects}
+ <EditRoomContainer />
+ <DeleteRoomContainer />
+ </div>
+ )}
+ </div>
+ );
+};
+
+export default RoomSidebarComponent;
diff --git a/frontend/src/components/app/sidebars/topology/room/RoomTypeComponent.js b/frontend/src/components/app/sidebars/topology/room/RoomTypeComponent.js
new file mode 100644
index 00000000..46d91c2c
--- /dev/null
+++ b/frontend/src/components/app/sidebars/topology/room/RoomTypeComponent.js
@@ -0,0 +1,8 @@
+import React from "react";
+import { ROOM_TYPE_TO_NAME_MAP } from "../../../../../util/room-types";
+
+const RoomTypeComponent = ({ roomType }) => (
+ <p className="lead">{ROOM_TYPE_TO_NAME_MAP[roomType]}</p>
+);
+
+export default RoomTypeComponent;