summaryrefslogtreecommitdiff
path: root/src/components/app/sidebars
diff options
context:
space:
mode:
authorGeorgios Andreadis <g.andreadis@student.tudelft.nl>2017-09-22 21:20:54 +0200
committerGeorgios Andreadis <g.andreadis@student.tudelft.nl>2017-09-23 10:06:18 +0200
commitbf7708f658cc6299a3b775afe24459b5a808c54d (patch)
tree227520267968759e2a2f1e29e6f3edfeb4e3cf8a /src/components/app/sidebars
parente722cf117d0e3ebac20237f96764fb08cab49a62 (diff)
Restructure component and container directories
Diffstat (limited to 'src/components/app/sidebars')
-rw-r--r--src/components/app/sidebars/Sidebar.js38
-rw-r--r--src/components/app/sidebars/Sidebar.sass50
-rw-r--r--src/components/app/sidebars/elements/LoadBarComponent.js22
-rw-r--r--src/components/app/sidebars/elements/LoadChartComponent.js40
-rw-r--r--src/components/app/sidebars/simulation/ExperimentMetadataComponent.js12
-rw-r--r--src/components/app/sidebars/simulation/LoadMetricComponent.js33
-rw-r--r--src/components/app/sidebars/simulation/SimulationSidebarComponent.js22
-rw-r--r--src/components/app/sidebars/simulation/SimulationSidebarComponent.sass8
-rw-r--r--src/components/app/sidebars/simulation/TaskComponent.js42
-rw-r--r--src/components/app/sidebars/simulation/TraceComponent.js20
-rw-r--r--src/components/app/sidebars/topology/NameComponent.js13
-rw-r--r--src/components/app/sidebars/topology/TopologySidebarComponent.js35
-rw-r--r--src/components/app/sidebars/topology/building/BuildingSidebarComponent.js19
-rw-r--r--src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js27
-rw-r--r--src/components/app/sidebars/topology/machine/BackToRackComponent.js10
-rw-r--r--src/components/app/sidebars/topology/machine/DeleteMachineComponent.js10
-rw-r--r--src/components/app/sidebars/topology/machine/MachineNameComponent.js7
-rw-r--r--src/components/app/sidebars/topology/machine/MachineSidebarComponent.js26
-rw-r--r--src/components/app/sidebars/topology/machine/UnitAddComponent.js38
-rw-r--r--src/components/app/sidebars/topology/machine/UnitComponent.js16
-rw-r--r--src/components/app/sidebars/topology/machine/UnitListComponent.js20
-rw-r--r--src/components/app/sidebars/topology/machine/UnitTabsComponent.js54
-rw-r--r--src/components/app/sidebars/topology/rack/BackToRoomComponent.js10
-rw-r--r--src/components/app/sidebars/topology/rack/DeleteRackComponent.js10
-rw-r--r--src/components/app/sidebars/topology/rack/EmptySlotComponent.js20
-rw-r--r--src/components/app/sidebars/topology/rack/MachineComponent.js65
-rw-r--r--src/components/app/sidebars/topology/rack/MachineListComponent.js20
-rw-r--r--src/components/app/sidebars/topology/rack/MachineListComponent.sass2
-rw-r--r--src/components/app/sidebars/topology/rack/RackNameComponent.js8
-rw-r--r--src/components/app/sidebars/topology/rack/RackSidebarComponent.js33
-rw-r--r--src/components/app/sidebars/topology/rack/RackSidebarComponent.sass11
-rw-r--r--src/components/app/sidebars/topology/room/BackToBuildingComponent.js10
-rw-r--r--src/components/app/sidebars/topology/room/DeleteRoomComponent.js10
-rw-r--r--src/components/app/sidebars/topology/room/RackConstructionComponent.js21
-rw-r--r--src/components/app/sidebars/topology/room/RoomNameComponent.js8
-rw-r--r--src/components/app/sidebars/topology/room/RoomSidebarComponent.js35
-rw-r--r--src/components/app/sidebars/topology/room/RoomTypeComponent.js10
37 files changed, 835 insertions, 0 deletions
diff --git a/src/components/app/sidebars/Sidebar.js b/src/components/app/sidebars/Sidebar.js
new file mode 100644
index 00000000..00e3607a
--- /dev/null
+++ b/src/components/app/sidebars/Sidebar.js
@@ -0,0 +1,38 @@
+import classNames from "classnames";
+import React from "react";
+import "./Sidebar.css";
+
+class Sidebar extends React.Component {
+ state = {
+ collapsed: false
+ };
+
+ render() {
+ const collapseButton = (
+ <div
+ className={classNames("sidebar-collapse-button", {"sidebar-collapse-button-right": this.props.isRight})}
+ onClick={() => this.setState({collapsed: !this.state.collapsed})}
+ >
+ {(this.state.collapsed && this.props.isRight) || (!this.state.collapsed && !this.props.isRight) ?
+ <span className="fa fa-angle-left" title={this.props.isRight ? "Expand" : "Collapse"}/> :
+ <span className="fa fa-angle-right" title={this.props.isRight ? "Collapse" : "Expand"}/>
+ }
+ </div>
+ );
+
+ if (this.state.collapsed) {
+ return collapseButton;
+ }
+ return (
+ <div
+ className={classNames("sidebar p-3 h-100", {"sidebar-right": this.props.isRight})}
+ onWheel={e => e.stopPropagation()}
+ >
+ {this.props.children}
+ {collapseButton}
+ </div>
+ );
+ }
+}
+
+export default Sidebar;
diff --git a/src/components/app/sidebars/Sidebar.sass b/src/components/app/sidebars/Sidebar.sass
new file mode 100644
index 00000000..4d0e5f1e
--- /dev/null
+++ b/src/components/app/sidebars/Sidebar.sass
@@ -0,0 +1,50 @@
+@import ../../../style-globals/_variables.sass
+@import ../../../style-globals/_mixins.sass
+
+.sidebar-collapse-button
+ position: absolute
+ left: 5px
+ top: 5px
+ padding: 5px 7px
+
+ background: white
+ border: solid 1px $gray-semi-light
+ z-index: 99
+
+ +clickable
+ +border-radius(5px)
+ +transition(background, 200ms)
+
+ &.sidebar-collapse-button-right
+ left: auto
+ right: 5px
+ top: 5px
+
+ &:hover
+ background: #eeeeee
+
+.sidebar
+ position: absolute
+ top: 0
+ left: 0
+ width: 350px
+
+ z-index: 100
+ background: white
+
+ border-right: $gray-semi-dark 1px solid
+
+ .sidebar-collapse-button
+ left: auto
+ right: -25px
+
+.sidebar-right
+ left: auto
+ right: 0
+
+ border-left: $gray-semi-dark 1px solid
+ border-right: none
+
+ .sidebar-collapse-button-right
+ left: -25px
+ right: auto
diff --git a/src/components/app/sidebars/elements/LoadBarComponent.js b/src/components/app/sidebars/elements/LoadBarComponent.js
new file mode 100644
index 00000000..65f94b3d
--- /dev/null
+++ b/src/components/app/sidebars/elements/LoadBarComponent.js
@@ -0,0 +1,22 @@
+import classNames from "classnames";
+import React from "react";
+
+const LoadBarComponent = ({percent, disabled}) => (
+ <div className="mt-1">
+ <strong>Current load</strong>
+ <div className={classNames("progress", {disabled})}>
+ <div
+ className="progress-bar"
+ role="progressbar"
+ aria-valuenow={percent}
+ aria-valuemin="0"
+ aria-valuemax="100"
+ style={{width: percent + "%"}}
+ >
+ {percent}%
+ </div>
+ </div>
+ </div>
+);
+
+export default LoadBarComponent;
diff --git a/src/components/app/sidebars/elements/LoadChartComponent.js b/src/components/app/sidebars/elements/LoadChartComponent.js
new file mode 100644
index 00000000..19d58f77
--- /dev/null
+++ b/src/components/app/sidebars/elements/LoadChartComponent.js
@@ -0,0 +1,40 @@
+import React from "react";
+import {VictoryAxis, VictoryChart, VictoryLine, VictoryScatter} from "victory";
+import {convertSecondsToFormattedTime} from "../../../../util/date-time";
+
+const LoadChartComponent = ({data, currentTick}) => (
+ <div className="mt-1">
+ <strong>Load over time</strong>
+ <VictoryChart
+ height={250}
+ padding={{top: 10, bottom: 50, left: 50, right: 50}}
+ >
+ <VictoryAxis
+ tickFormat={tick => convertSecondsToFormattedTime(tick)}
+ fixLabelOverlap={true}
+ label="Simulated Time"
+ />
+ <VictoryAxis
+ dependentAxis
+ label="Load"
+ />
+ <VictoryLine
+ data={data}
+ />
+ <VictoryScatter
+ data={data}
+ />
+ <VictoryLine
+ data={[
+ {x: currentTick + 1, y: 0},
+ {x: currentTick + 1, y: 1},
+ ]}
+ style={{
+ data: {stroke: "#00A6D6", strokeWidth: 3}
+ }}
+ />
+ </VictoryChart>
+ </div>
+);
+
+export default LoadChartComponent;
diff --git a/src/components/app/sidebars/simulation/ExperimentMetadataComponent.js b/src/components/app/sidebars/simulation/ExperimentMetadataComponent.js
new file mode 100644
index 00000000..3649045b
--- /dev/null
+++ b/src/components/app/sidebars/simulation/ExperimentMetadataComponent.js
@@ -0,0 +1,12 @@
+import React from "react";
+
+const ExperimentMetadataComponent = ({experimentName, pathName, traceName, schedulerName}) => (
+ <div>
+ <h2>{experimentName}</h2>
+ <p>Path: <strong>{pathName}</strong></p>
+ <p>Trace: <strong>{traceName}</strong></p>
+ <p>Scheduler: <strong>{schedulerName}</strong></p>
+ </div>
+);
+
+export default ExperimentMetadataComponent;
diff --git a/src/components/app/sidebars/simulation/LoadMetricComponent.js b/src/components/app/sidebars/simulation/LoadMetricComponent.js
new file mode 100644
index 00000000..e72e6b67
--- /dev/null
+++ b/src/components/app/sidebars/simulation/LoadMetricComponent.js
@@ -0,0 +1,33 @@
+import React from "react";
+import {SIM_HIGH_COLOR, SIM_LOW_COLOR, SIM_MID_HIGH_COLOR, SIM_MID_LOW_COLOR} from "../../../../util/colors";
+import {LOAD_NAME_MAP} from "../../../../util/simulation-load";
+
+const LoadMetricComponent = ({loadMetric}) => (
+ <div>
+ <div>Colors represent <strong>{LOAD_NAME_MAP[loadMetric]}</strong></div>
+ <div className="btn-group mb-2" style={{display: "flex"}}>
+ <span
+ className="btn btn-secondary"
+ style={{backgroundColor: SIM_LOW_COLOR, flex: 1}}
+ title="0-25%"
+ />
+ <span
+ className="btn btn-secondary"
+ style={{backgroundColor: SIM_MID_LOW_COLOR, flex: 1}}
+ title="25-50%"
+ />
+ <span
+ className="btn btn-secondary"
+ style={{backgroundColor: SIM_MID_HIGH_COLOR, flex: 1}}
+ title="50-75%"
+ />
+ <span
+ className="btn btn-secondary"
+ style={{backgroundColor: SIM_HIGH_COLOR, flex: 1}}
+ title="75-100%"
+ />
+ </div>
+ </div>
+);
+
+export default LoadMetricComponent;
diff --git a/src/components/app/sidebars/simulation/SimulationSidebarComponent.js b/src/components/app/sidebars/simulation/SimulationSidebarComponent.js
new file mode 100644
index 00000000..92651dfc
--- /dev/null
+++ b/src/components/app/sidebars/simulation/SimulationSidebarComponent.js
@@ -0,0 +1,22 @@
+import React from "react";
+import ExperimentMetadataContainer from "../../../../containers/app/sidebars/simulation/ExperimentMetadataContainer";
+import LoadMetricContainer from "../../../../containers/app/sidebars/simulation/LoadMetricContainer";
+import TraceContainer from "../../../../containers/app/sidebars/simulation/TraceContainer";
+import Sidebar from "../Sidebar";
+import "./SimulationSidebarComponent.css";
+
+const SimulationSidebarComponent = () => {
+ return (
+ <Sidebar isRight={false}>
+ <div className="simulation-sidebar-container flex-column">
+ <ExperimentMetadataContainer/>
+ <LoadMetricContainer/>
+ <div className="trace-container">
+ <TraceContainer/>
+ </div>
+ </div>
+ </Sidebar>
+ );
+};
+
+export default SimulationSidebarComponent;
diff --git a/src/components/app/sidebars/simulation/SimulationSidebarComponent.sass b/src/components/app/sidebars/simulation/SimulationSidebarComponent.sass
new file mode 100644
index 00000000..82af97fa
--- /dev/null
+++ b/src/components/app/sidebars/simulation/SimulationSidebarComponent.sass
@@ -0,0 +1,8 @@
+.simulation-sidebar-container
+ display: flex
+ height: 100%
+ max-height: 100%
+
+.trace-container
+ flex: 1
+ overflow-y: scroll
diff --git a/src/components/app/sidebars/simulation/TaskComponent.js b/src/components/app/sidebars/simulation/TaskComponent.js
new file mode 100644
index 00000000..f7f65817
--- /dev/null
+++ b/src/components/app/sidebars/simulation/TaskComponent.js
@@ -0,0 +1,42 @@
+import approx from "approximate-number";
+import React from "react";
+import {convertSecondsToFormattedTime} from "../../../../util/date-time";
+
+const TaskComponent = ({task, flopsLeft}) => {
+ let stateInfo;
+
+ if (flopsLeft === task.totalFlopCount) {
+ stateInfo = (
+ <div>
+ <span className="fa fa-hourglass-half mr-2"/>
+ Waiting
+ </div>
+ );
+ } else if (flopsLeft > 0) {
+ stateInfo = (
+ <div>
+ <span className="fa fa-refresh mr-2"/>
+ Running ({approx(task.totalFlopCount - flopsLeft)} / {approx(task.totalFlopCount)} FLOPS)
+ </div>
+ );
+ } else {
+ stateInfo = (
+ <div>
+ <span className="fa fa-check mr-2"/>
+ Completed
+ </div>
+ );
+ }
+
+ return (
+ <li className="list-group-item flex-column align-items-start">
+ <div className="d-flex w-100 justify-content-between">
+ <h5 className="mb-1">{approx(task.totalFlopCount)} FLOPS</h5>
+ <small>Starts at {convertSecondsToFormattedTime(task.startTick)}</small>
+ </div>
+ {stateInfo}
+ </li>
+ );
+};
+
+export default TaskComponent;
diff --git a/src/components/app/sidebars/simulation/TraceComponent.js b/src/components/app/sidebars/simulation/TraceComponent.js
new file mode 100644
index 00000000..b43a8cea
--- /dev/null
+++ b/src/components/app/sidebars/simulation/TraceComponent.js
@@ -0,0 +1,20 @@
+import React from "react";
+import TaskContainer from "../../../../containers/app/sidebars/simulation/TaskContainer";
+
+const TraceComponent = ({jobs}) => (
+ <div>
+ <h3>Trace</h3>
+ {jobs.map(job => (
+ <div key={job.id}>
+ <h4>Job: {job.name}</h4>
+ <ul className="list-group">
+ {job.taskIds.map(taskId => (
+ <TaskContainer taskId={taskId} key={taskId}/>
+ ))}
+ </ul>
+ </div>
+ ))}
+ </div>
+);
+
+export default TraceComponent;
diff --git a/src/components/app/sidebars/topology/NameComponent.js b/src/components/app/sidebars/topology/NameComponent.js
new file mode 100644
index 00000000..d663f4ae
--- /dev/null
+++ b/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/src/components/app/sidebars/topology/TopologySidebarComponent.js b/src/components/app/sidebars/topology/TopologySidebarComponent.js
new file mode 100644
index 00000000..ff4260a9
--- /dev/null
+++ b/src/components/app/sidebars/topology/TopologySidebarComponent.js
@@ -0,0 +1,35 @@
+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/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js b/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js
new file mode 100644
index 00000000..2bf81a48
--- /dev/null
+++ b/src/components/app/sidebars/topology/building/BuildingSidebarComponent.js
@@ -0,0 +1,19 @@
+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/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js b/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js
new file mode 100644
index 00000000..d89b0ac0
--- /dev/null
+++ b/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js
@@ -0,0 +1,27 @@
+import React from "react";
+
+const NewRoomConstructionComponent = ({onStart, onFinish, onCancel, currentRoomInConstruction}) => {
+ if (currentRoomInConstruction === -1) {
+ return (
+ <div className="btn btn-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/src/components/app/sidebars/topology/machine/BackToRackComponent.js b/src/components/app/sidebars/topology/machine/BackToRackComponent.js
new file mode 100644
index 00000000..19e33904
--- /dev/null
+++ b/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/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js b/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js
new file mode 100644
index 00000000..7ba08352
--- /dev/null
+++ b/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js
@@ -0,0 +1,10 @@
+import React from "react";
+
+const DeleteMachineComponent = ({onClick}) => (
+ <div className="btn btn-danger btn-block" onClick={onClick}>
+ <span className="fa fa-trash mr-2"/>
+ Delete this machine
+ </div>
+);
+
+export default DeleteMachineComponent;
diff --git a/src/components/app/sidebars/topology/machine/MachineNameComponent.js b/src/components/app/sidebars/topology/machine/MachineNameComponent.js
new file mode 100644
index 00000000..321e350d
--- /dev/null
+++ b/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/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js b/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js
new file mode 100644
index 00000000..cf4db80e
--- /dev/null
+++ b/src/components/app/sidebars/topology/machine/MachineSidebarComponent.js
@@ -0,0 +1,26 @@
+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/src/components/app/sidebars/topology/machine/UnitAddComponent.js b/src/components/app/sidebars/topology/machine/UnitAddComponent.js
new file mode 100644
index 00000000..f16700df
--- /dev/null
+++ b/src/components/app/sidebars/topology/machine/UnitAddComponent.js
@@ -0,0 +1,38 @@
+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-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/src/components/app/sidebars/topology/machine/UnitComponent.js b/src/components/app/sidebars/topology/machine/UnitComponent.js
new file mode 100644
index 00000000..c734f508
--- /dev/null
+++ b/src/components/app/sidebars/topology/machine/UnitComponent.js
@@ -0,0 +1,16 @@
+import React from "react";
+
+const UnitComponent = ({unit, onDelete, inSimulation}) => (
+ <li className="d-flex list-group-item justify-content-between align-items-center">
+ {unit.manufacturer + " " + unit.family + " " + unit.model + " " + unit.generation}
+ {inSimulation ?
+ undefined :
+ <span className="btn btn-outline-danger" onClick={onDelete}>
+ <span className="fa fa-trash mr-2"/>
+ Delete
+ </span>
+ }
+ </li>
+);
+
+export default UnitComponent;
diff --git a/src/components/app/sidebars/topology/machine/UnitListComponent.js b/src/components/app/sidebars/topology/machine/UnitListComponent.js
new file mode 100644
index 00000000..683f6023
--- /dev/null
+++ b/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, 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/src/components/app/sidebars/topology/machine/UnitTabsComponent.js b/src/components/app/sidebars/topology/machine/UnitTabsComponent.js
new file mode 100644
index 00000000..2113d6d8
--- /dev/null
+++ b/src/components/app/sidebars/topology/machine/UnitTabsComponent.js
@@ -0,0 +1,54 @@
+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/src/components/app/sidebars/topology/rack/BackToRoomComponent.js b/src/components/app/sidebars/topology/rack/BackToRoomComponent.js
new file mode 100644
index 00000000..267001c6
--- /dev/null
+++ b/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/src/components/app/sidebars/topology/rack/DeleteRackComponent.js b/src/components/app/sidebars/topology/rack/DeleteRackComponent.js
new file mode 100644
index 00000000..b268bd72
--- /dev/null
+++ b/src/components/app/sidebars/topology/rack/DeleteRackComponent.js
@@ -0,0 +1,10 @@
+import React from "react";
+
+const DeleteRackComponent = ({onClick}) => (
+ <div className="btn btn-danger btn-block" onClick={onClick}>
+ <span className="fa fa-trash mr-2"/>
+ Delete this rack
+ </div>
+);
+
+export default DeleteRackComponent;
diff --git a/src/components/app/sidebars/topology/rack/EmptySlotComponent.js b/src/components/app/sidebars/topology/rack/EmptySlotComponent.js
new file mode 100644
index 00000000..08665072
--- /dev/null
+++ b/src/components/app/sidebars/topology/rack/EmptySlotComponent.js
@@ -0,0 +1,20 @@
+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/src/components/app/sidebars/topology/rack/MachineComponent.js b/src/components/app/sidebars/topology/rack/MachineComponent.js
new file mode 100644
index 00000000..545bb916
--- /dev/null
+++ b/src/components/app/sidebars/topology/rack/MachineComponent.js
@@ -0,0 +1,65 @@
+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) {
+ 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/src/components/app/sidebars/topology/rack/MachineListComponent.js b/src/components/app/sidebars/topology/rack/MachineListComponent.js
new file mode 100644
index 00000000..fcb90d66
--- /dev/null
+++ b/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.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/src/components/app/sidebars/topology/rack/MachineListComponent.sass b/src/components/app/sidebars/topology/rack/MachineListComponent.sass
new file mode 100644
index 00000000..bbcfe696
--- /dev/null
+++ b/src/components/app/sidebars/topology/rack/MachineListComponent.sass
@@ -0,0 +1,2 @@
+.machine-list li
+ min-height: 64px
diff --git a/src/components/app/sidebars/topology/rack/RackNameComponent.js b/src/components/app/sidebars/topology/rack/RackNameComponent.js
new file mode 100644
index 00000000..ee8d194b
--- /dev/null
+++ b/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/src/components/app/sidebars/topology/rack/RackSidebarComponent.js b/src/components/app/sidebars/topology/rack/RackSidebarComponent.js
new file mode 100644
index 00000000..f563a52f
--- /dev/null
+++ b/src/components/app/sidebars/topology/rack/RackSidebarComponent.js
@@ -0,0 +1,33 @@
+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/src/components/app/sidebars/topology/rack/RackSidebarComponent.sass b/src/components/app/sidebars/topology/rack/RackSidebarComponent.sass
new file mode 100644
index 00000000..822804bc
--- /dev/null
+++ b/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/src/components/app/sidebars/topology/room/BackToBuildingComponent.js b/src/components/app/sidebars/topology/room/BackToBuildingComponent.js
new file mode 100644
index 00000000..81384ba5
--- /dev/null
+++ b/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/src/components/app/sidebars/topology/room/DeleteRoomComponent.js b/src/components/app/sidebars/topology/room/DeleteRoomComponent.js
new file mode 100644
index 00000000..3f41eac0
--- /dev/null
+++ b/src/components/app/sidebars/topology/room/DeleteRoomComponent.js
@@ -0,0 +1,10 @@
+import React from "react";
+
+const DeleteRoomComponent = ({onClick}) => (
+ <div className="btn btn-danger btn-block" onClick={onClick}>
+ <span className="fa fa-trash mr-2"/>
+ Delete this room
+ </div>
+);
+
+export default DeleteRoomComponent;
diff --git a/src/components/app/sidebars/topology/room/RackConstructionComponent.js b/src/components/app/sidebars/topology/room/RackConstructionComponent.js
new file mode 100644
index 00000000..9bfe28ce
--- /dev/null
+++ b/src/components/app/sidebars/topology/room/RackConstructionComponent.js
@@ -0,0 +1,21 @@
+import React from "react";
+
+const RackConstructionComponent = ({inRackConstructionMode, onStart, onStop}) => {
+ 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="btn btn-primary btn-block" onClick={onStart}>
+ <span className="fa fa-plus mr-2"/>
+ Start rack construction
+ </div>
+ );
+};
+
+export default RackConstructionComponent;
diff --git a/src/components/app/sidebars/topology/room/RoomNameComponent.js b/src/components/app/sidebars/topology/room/RoomNameComponent.js
new file mode 100644
index 00000000..4d3e41cc
--- /dev/null
+++ b/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/src/components/app/sidebars/topology/room/RoomSidebarComponent.js b/src/components/app/sidebars/topology/room/RoomSidebarComponent.js
new file mode 100644
index 00000000..53857408
--- /dev/null
+++ b/src/components/app/sidebars/topology/room/RoomSidebarComponent.js
@@ -0,0 +1,35 @@
+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 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}
+ <DeleteRoomContainer/>
+ </div>
+ }
+ </div>
+ );
+};
+
+export default RoomSidebarComponent;
diff --git a/src/components/app/sidebars/topology/room/RoomTypeComponent.js b/src/components/app/sidebars/topology/room/RoomTypeComponent.js
new file mode 100644
index 00000000..d42eefb6
--- /dev/null
+++ b/src/components/app/sidebars/topology/room/RoomTypeComponent.js
@@ -0,0 +1,10 @@
+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;