diff options
31 files changed, 480 insertions, 37 deletions
diff --git a/src/actions/interaction-level.js b/src/actions/interaction-level.js index 884f1988..5ceff500 100644 --- a/src/actions/interaction-level.js +++ b/src/actions/interaction-level.js @@ -1,5 +1,5 @@ export const GO_FROM_BUILDING_TO_ROOM = "GO_FROM_BUILDING_TO_ROOM"; -export const GO_FROM_ROOM_TO_OBJECT = "GO_FROM_ROOM_TO_OBJECT"; +export const GO_FROM_ROOM_TO_RACK = "GO_FROM_ROOM_TO_RACK"; export const GO_DOWN_ONE_INTERACTION_LEVEL = "GO_DOWN_ONE_INTERACTION_LEVEL"; export function goFromBuildingToRoom(roomId) { @@ -16,14 +16,14 @@ export function goFromBuildingToRoom(roomId) { }; } -export function goFromRoomToObject(tileId) { +export function goFromRoomToRack(tileId) { return (dispatch, getState) => { const {interactionLevel} = getState(); if (interactionLevel.mode !== "ROOM") { return; } dispatch({ - type: GO_FROM_ROOM_TO_OBJECT, + type: GO_FROM_ROOM_TO_RACK, tileId }); }; diff --git a/src/actions/modals/topology.js b/src/actions/modals/topology.js index ded8771a..507c1de6 100644 --- a/src/actions/modals/topology.js +++ b/src/actions/modals/topology.js @@ -2,6 +2,10 @@ export const OPEN_EDIT_ROOM_NAME_MODAL = "OPEN_EDIT_ROOM_NAME_MODAL"; export const CLOSE_EDIT_ROOM_NAME_MODAL = "CLOSE_EDIT_ROOM_NAME_MODAL"; export const OPEN_DELETE_ROOM_MODAL = "OPEN_DELETE_ROOM_MODAL"; export const CLOSE_DELETE_ROOM_MODAL = "CLOSE_DELETE_ROOM_MODAL"; +export const OPEN_EDIT_RACK_NAME_MODAL = "OPEN_EDIT_RACK_NAME_MODAL"; +export const CLOSE_EDIT_RACK_NAME_MODAL = "CLOSE_EDIT_RACK_NAME_MODAL"; +export const OPEN_DELETE_RACK_MODAL = "OPEN_DELETE_RACK_MODAL"; +export const CLOSE_DELETE_RACK_MODAL = "CLOSE_DELETE_RACK_MODAL"; export function openEditRoomNameModal() { return { @@ -26,3 +30,27 @@ export function closeDeleteRoomModal() { type: CLOSE_DELETE_ROOM_MODAL }; } + +export function openEditRackNameModal() { + return { + type: OPEN_EDIT_RACK_NAME_MODAL + }; +} + +export function closeEditRackNameModal() { + return { + type: CLOSE_EDIT_RACK_NAME_MODAL + }; +} + +export function openDeleteRackModal() { + return { + type: OPEN_DELETE_RACK_MODAL + }; +} + +export function closeDeleteRackModal() { + return { + type: CLOSE_DELETE_RACK_MODAL + }; +} diff --git a/src/actions/topology.js b/src/actions/topology.js index ec614c1c..4d9f1e52 100644 --- a/src/actions/topology.js +++ b/src/actions/topology.js @@ -16,10 +16,12 @@ export const ADD_TILE = "ADD_TILE"; export const DELETE_TILE = "DELETE_TILE"; export const EDIT_ROOM_NAME = "EDIT_ROOM_NAME"; export const DELETE_ROOM = "DELETE_ROOM"; -export const START_OBJECT_CONSTRUCTION = "START_OBJECT_CONSTRUCTION"; -export const STOP_OBJECT_CONSTRUCTION = "STOP_OBJECT_CONSTRUCTION"; +export const EDIT_RACK_NAME = "EDIT_RACK_NAME"; +export const DELETE_RACK = "DELETE_RACK"; +export const START_RACK_CONSTRUCTION = "START_RACK_CONSTRUCTION"; +export const STOP_RACK_CONSTRUCTION = "STOP_RACK_CONSTRUCTION"; export const ADD_RACK_TO_TILE = "ADD_RACK_TO_TILE"; -export const ADD_RACK_TO_TILE_SUCCEEDED = "ADD_RACK_TO_TILE_SUCCEEDED"; +export const ADD_MACHINE = "ADD_MACHINE"; export function fetchLatestDatacenter() { return (dispatch, getState) => { @@ -151,15 +153,15 @@ export function editRoomNameSucceeded(name) { }; } -export function startObjectConstruction() { +export function startRackConstruction() { return { - type: START_OBJECT_CONSTRUCTION + type: START_RACK_CONSTRUCTION }; } -export function stopObjectConstruction() { +export function stopRackConstruction() { return { - type: STOP_OBJECT_CONSTRUCTION + type: STOP_RACK_CONSTRUCTION }; } @@ -200,3 +202,50 @@ export function deleteRoomSucceeded() { dispatch(removeIdFromStoreObjectListProp("datacenter", currentDatacenterId, "roomIds", currentRoomId)); }; } + +export function editRackName(name) { + return { + type: EDIT_RACK_NAME, + name + }; +} + +export function editRackNameSucceeded(name) { + return (dispatch, getState) => { + const {objects, interactionLevel} = getState(); + dispatch(addPropToStoreObject("rack", objects.tile[interactionLevel.tileId].objectId, {name})); + }; +} + +export function deleteRack() { + return { + type: DELETE_RACK + }; +} + +export function deleteRackSucceeded() { + return (dispatch, getState) => { + const {interactionLevel} = getState(); + const currentTileId = interactionLevel.tileId; + dispatch(goDownOneInteractionLevel()); + dispatch(addPropToStoreObject("tile", currentTileId, {objectType: undefined})); + dispatch(addPropToStoreObject("tile", currentTileId, {objectId: undefined})); + }; +} + +export function addMachine(position) { + return { + type: ADD_MACHINE, + position + }; +} + +export function addMachineSucceeded(machine) { + return (dispatch, getState) => { + const {objects, interactionLevel} = getState(); + const rack = objects.rack[objects.tile[interactionLevel.tileId].objectId]; + const machineIds = [...rack.machineIds]; + machineIds[machine.position] = machine.id; + dispatch(addPropToStoreObject("rack", rack.id, {machineIds})); + }; +} diff --git a/src/components/map/groups/RoomGroup.js b/src/components/map/groups/RoomGroup.js index 85d3c7c9..1c42106a 100644 --- a/src/components/map/groups/RoomGroup.js +++ b/src/components/map/groups/RoomGroup.js @@ -19,7 +19,7 @@ const RoomGroup = ({room, interactionLevel, currentRoomInConstruction, onClick}) return ( <Group onClick={onClick}> {(() => { - if (interactionLevel.mode === "OBJECT" && interactionLevel.roomId === room.id) { + if (interactionLevel.mode === "RACK" && interactionLevel.roomId === room.id) { return [ room.tileIds .filter(tileId => tileId !== interactionLevel.tileId) diff --git a/src/components/sidebars/Sidebar.js b/src/components/sidebars/Sidebar.js index be957956..2f4d77a7 100644 --- a/src/components/sidebars/Sidebar.js +++ b/src/components/sidebars/Sidebar.js @@ -3,7 +3,7 @@ import React from "react"; import "./Sidebar.css"; const Sidebar = ({isRight, children}) => ( - <div className={classNames("sidebar p-3", {"sidebar-right": isRight})}> + <div className={classNames("sidebar p-3 h-100", {"sidebar-right": isRight})}> {children} </div> ); diff --git a/src/components/sidebars/Sidebar.sass b/src/components/sidebars/Sidebar.sass index 79ea3c27..16a6d48f 100644 --- a/src/components/sidebars/Sidebar.sass +++ b/src/components/sidebars/Sidebar.sass @@ -5,7 +5,6 @@ top: 0 left: 0 width: 300px - height: 100% z-index: 100 background: white diff --git a/src/components/sidebars/topology/TopologySidebarComponent.js b/src/components/sidebars/topology/TopologySidebarComponent.js index dc4dc231..bc23d320 100644 --- a/src/components/sidebars/topology/TopologySidebarComponent.js +++ b/src/components/sidebars/topology/TopologySidebarComponent.js @@ -2,6 +2,7 @@ import React from "react"; import BuildingSidebarContainer from "../../../containers/sidebars/topology/building/BuildingSidebarContainer"; import RoomSidebarContainer from "../../../containers/sidebars/topology/room/RoomSidebarContainer"; import Sidebar from "../Sidebar"; +import RackSidebarComponent from "./rack/RackSidebarComponent"; const TopologySidebarComponent = ({interactionLevel}) => { let sidebarContent; @@ -13,6 +14,9 @@ const TopologySidebarComponent = ({interactionLevel}) => { case "ROOM": sidebarContent = <RoomSidebarContainer/>; break; + case "RACK": + sidebarContent = <RackSidebarComponent/>; + break; default: sidebarContent = "Missing Content"; } diff --git a/src/components/sidebars/topology/rack/DeleteRackComponent.js b/src/components/sidebars/topology/rack/DeleteRackComponent.js new file mode 100644 index 00000000..555f6d02 --- /dev/null +++ b/src/components/sidebars/topology/rack/DeleteRackComponent.js @@ -0,0 +1,11 @@ +import React from "react"; + +const DeleteRackComponent = ({onClick}) => { + return ( + <div className="btn btn-danger btn-block" onClick={onClick}> + Delete this rack + </div> + ); +}; + +export default DeleteRackComponent; diff --git a/src/components/sidebars/topology/rack/EmptySlotComponent.js b/src/components/sidebars/topology/rack/EmptySlotComponent.js new file mode 100644 index 00000000..5234ee63 --- /dev/null +++ b/src/components/sidebars/topology/rack/EmptySlotComponent.js @@ -0,0 +1,16 @@ +import React from "react"; +import FontAwesome from "react-fontawesome"; + +const EmptySlotComponent = ({position, onAdd}) => ( + <li className="list-group-item justify-content-between"> + <span className="badge badge-default badge-info"> + {position} + </span> + Add machine + <button className="btn btn-secondary" onClick={onAdd}> + <FontAwesome name="plus"/> + </button> + </li> +); + +export default EmptySlotComponent; diff --git a/src/components/sidebars/topology/rack/MachineComponent.js b/src/components/sidebars/topology/rack/MachineComponent.js new file mode 100644 index 00000000..e328951e --- /dev/null +++ b/src/components/sidebars/topology/rack/MachineComponent.js @@ -0,0 +1,28 @@ +import React from "react"; +import Shapes from "../../../../shapes"; + +const MachineComponent = ({position, machine, onClick}) => ( + <li className="list-group-item list-group-item-action justify-content-between" onClick={onClick}> + <span className="badge badge-default badge-info"> + {position} + </span> + <span className="badge badge-primary badge-pill"> + {machine.cpuIds.length} CPUs + </span> + <span className="badge badge-warning badge-pill"> + {machine.gpuIds.length} GPUs + </span> + <span className="badge badge-success badge-pill"> + {machine.memoryIds.length} Memories + </span> + <span className="badge badge-info badge-pill"> + {machine.storageIds.length} Storages + </span> + </li> +); + +MachineComponent.propTypes = { + machine: Shapes.Machine +}; + +export default MachineComponent; diff --git a/src/components/sidebars/topology/rack/MachineListComponent.js b/src/components/sidebars/topology/rack/MachineListComponent.js new file mode 100644 index 00000000..d8a31ddc --- /dev/null +++ b/src/components/sidebars/topology/rack/MachineListComponent.js @@ -0,0 +1,19 @@ +import React from "react"; +import EmptySlotContainer from "../../../../containers/sidebars/topology/rack/EmptySlotContainer"; +import MachineContainer from "../../../../containers/sidebars/topology/rack/MachineContainer"; + +const MachineListComponent = ({machineIds}) => { + return ( + <ul className="list-group"> + {machineIds.map((machineId, index) => { + if (machineId === null) { + return <EmptySlotContainer key={index} position={index}/>; + } else { + return <MachineContainer key={index} position={index} machineId={machineId}/>; + } + })} + </ul> + ); +}; + +export default MachineListComponent; diff --git a/src/components/sidebars/topology/rack/RackNameComponent.js b/src/components/sidebars/topology/rack/RackNameComponent.js new file mode 100644 index 00000000..ee8d194b --- /dev/null +++ b/src/components/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/sidebars/topology/rack/RackSidebarComponent.js b/src/components/sidebars/topology/rack/RackSidebarComponent.js new file mode 100644 index 00000000..ddb10387 --- /dev/null +++ b/src/components/sidebars/topology/rack/RackSidebarComponent.js @@ -0,0 +1,16 @@ +import React from "react"; +import DeleteRackContainer from "../../../../containers/sidebars/topology/rack/DeleteRackContainer"; +import MachineListContainer from "../../../../containers/sidebars/topology/rack/MachineListContainer"; +import RackNameContainer from "../../../../containers/sidebars/topology/rack/RackNameContainer"; + +const RackSidebarComponent = () => { + return ( + <div> + <RackNameContainer/> + <DeleteRackContainer/> + <MachineListContainer/> + </div> + ); +}; + +export default RackSidebarComponent; diff --git a/src/components/sidebars/topology/room/RackConstructionComponent.js b/src/components/sidebars/topology/room/RackConstructionComponent.js index 8298eade..894ffdf7 100644 --- a/src/components/sidebars/topology/room/RackConstructionComponent.js +++ b/src/components/sidebars/topology/room/RackConstructionComponent.js @@ -1,7 +1,7 @@ import React from "react"; -const RackConstructionComponent = ({inObjectConstructionMode, onStart, onStop}) => { - if (inObjectConstructionMode) { +const RackConstructionComponent = ({inRackConstructionMode, onStart, onStop}) => { + if (inRackConstructionMode) { return ( <div className="btn btn-primary btn-block" onClick={onStop}> Stop rack construction diff --git a/src/containers/map/TileContainer.js b/src/containers/map/TileContainer.js index d00c3611..9e98636a 100644 --- a/src/containers/map/TileContainer.js +++ b/src/containers/map/TileContainer.js @@ -1,5 +1,5 @@ import {connect} from "react-redux"; -import {goFromRoomToObject} from "../../actions/interaction-level"; +import {goFromRoomToRack} from "../../actions/interaction-level"; import TileGroup from "../../components/map/groups/TileGroup"; const mapStateToProps = (state, ownProps) => { @@ -13,7 +13,7 @@ const mapDispatchToProps = dispatch => { return { onClick: tile => { if (tile.objectType) { - dispatch(goFromRoomToObject(tile.id)) + dispatch(goFromRoomToRack(tile.id)) } } }; diff --git a/src/containers/map/layers/ObjectHoverLayer.js b/src/containers/map/layers/ObjectHoverLayer.js index e9df0384..138daa2c 100644 --- a/src/containers/map/layers/ObjectHoverLayer.js +++ b/src/containers/map/layers/ObjectHoverLayer.js @@ -5,7 +5,7 @@ import {findTileWithPosition} from "../../../util/tile-calculations"; const mapStateToProps = state => { return { - isEnabled: () => state.construction.inObjectConstructionMode, + isEnabled: () => state.construction.inRackConstructionMode, isValid: (x, y) => { if (state.interactionLevel.mode !== "ROOM") { return false; diff --git a/src/containers/modals/DeleteRackModal.js b/src/containers/modals/DeleteRackModal.js new file mode 100644 index 00000000..00dd036c --- /dev/null +++ b/src/containers/modals/DeleteRackModal.js @@ -0,0 +1,36 @@ +import React from "react"; +import {connect} from "react-redux"; +import {closeDeleteRackModal} from "../../actions/modals/topology"; +import {deleteRack} from "../../actions/topology"; +import ConfirmationModal from "../../components/modals/ConfirmationModal"; + +const DeleteRackModalComponent = ({visible, callback}) => ( + <ConfirmationModal title="Delete this rack" + message="Are you sure you want to delete this rack?" + show={visible} + callback={callback}/> +); + +const mapStateToProps = state => { + return { + visible: state.modals.deleteRackModalVisible + }; +}; + +const mapDispatchToProps = dispatch => { + return { + callback: (isConfirmed) => { + if (isConfirmed) { + dispatch(deleteRack()); + } + dispatch(closeDeleteRackModal()); + } + }; +}; + +const DeleteRackModal = connect( + mapStateToProps, + mapDispatchToProps +)(DeleteRackModalComponent); + +export default DeleteRackModal; diff --git a/src/containers/modals/EditRackNameModal.js b/src/containers/modals/EditRackNameModal.js new file mode 100644 index 00000000..e793f146 --- /dev/null +++ b/src/containers/modals/EditRackNameModal.js @@ -0,0 +1,39 @@ +import React from "react"; +import {connect} from "react-redux"; +import {closeEditRackNameModal} from "../../actions/modals/topology"; +import {editRackName} from "../../actions/topology"; +import TextInputModal from "../../components/modals/TextInputModal"; + +const EditRackNameModalComponent = ({visible, previousName, callback}) => ( + <TextInputModal title="Edit rack name" + label="Rack name" + show={visible} + initialValue={previousName} + callback={callback}/> +); + +const mapStateToProps = state => { + return { + visible: state.modals.editRackNameModalVisible, + previousName: state.interactionLevel.mode === "RACK" ? + state.objects.rack[state.objects.tile[state.interactionLevel.tileId].objectId].name : "", + }; +}; + +const mapDispatchToProps = dispatch => { + return { + callback: (name) => { + if (name) { + dispatch(editRackName(name)); + } + dispatch(closeEditRackNameModal()); + } + }; +}; + +const EditRackNameModal = connect( + mapStateToProps, + mapDispatchToProps +)(EditRackNameModalComponent); + +export default EditRackNameModal; diff --git a/src/containers/sidebars/topology/rack/DeleteRackContainer.js b/src/containers/sidebars/topology/rack/DeleteRackContainer.js new file mode 100644 index 00000000..f95c48b8 --- /dev/null +++ b/src/containers/sidebars/topology/rack/DeleteRackContainer.js @@ -0,0 +1,16 @@ +import {connect} from "react-redux"; +import {openDeleteRackModal} from "../../../../actions/modals/topology"; +import DeleteRackComponent from "../../../../components/sidebars/topology/rack/DeleteRackComponent"; + +const mapDispatchToProps = dispatch => { + return { + onClick: () => dispatch(openDeleteRackModal()), + }; +}; + +const DeleteRackContainer = connect( + undefined, + mapDispatchToProps +)(DeleteRackComponent); + +export default DeleteRackContainer; diff --git a/src/containers/sidebars/topology/rack/EmptySlotContainer.js b/src/containers/sidebars/topology/rack/EmptySlotContainer.js new file mode 100644 index 00000000..01ec6529 --- /dev/null +++ b/src/containers/sidebars/topology/rack/EmptySlotContainer.js @@ -0,0 +1,16 @@ +import {connect} from "react-redux"; +import {addMachine} from "../../../../actions/topology"; +import EmptySlotComponent from "../../../../components/sidebars/topology/rack/EmptySlotComponent"; + +const mapDispatchToProps = (dispatch, ownProps) => { + return { + onAdd: () => dispatch(addMachine(ownProps.position)), + }; +}; + +const EmptySlotContainer = connect( + undefined, + mapDispatchToProps +)(EmptySlotComponent); + +export default EmptySlotContainer; diff --git a/src/containers/sidebars/topology/rack/MachineContainer.js b/src/containers/sidebars/topology/rack/MachineContainer.js new file mode 100644 index 00000000..74a0bfbc --- /dev/null +++ b/src/containers/sidebars/topology/rack/MachineContainer.js @@ -0,0 +1,21 @@ +import {connect} from "react-redux"; +import MachineComponent from "../../../../components/sidebars/topology/rack/MachineComponent"; + +const mapStateToProps = (state, ownProps) => { + return { + machine: state.objects.machine[ownProps.machineId], + }; +}; + +const mapDispatchToProps = dispatch => { + return { + onClick: () => undefined, // TODO implement transition to MACHINE mode + }; +}; + +const MachineContainer = connect( + mapStateToProps, + mapDispatchToProps +)(MachineComponent); + +export default MachineContainer; diff --git a/src/containers/sidebars/topology/rack/MachineListContainer.js b/src/containers/sidebars/topology/rack/MachineListContainer.js new file mode 100644 index 00000000..eef2a4e1 --- /dev/null +++ b/src/containers/sidebars/topology/rack/MachineListContainer.js @@ -0,0 +1,14 @@ +import {connect} from "react-redux"; +import MachineListComponent from "../../../../components/sidebars/topology/rack/MachineListComponent"; + +const mapStateToProps = state => { + return { + machineIds: state.objects.rack[state.objects.tile[state.interactionLevel.tileId].objectId].machineIds, + }; +}; + +const MachineListContainer = connect( + mapStateToProps +)(MachineListComponent); + +export default MachineListContainer; diff --git a/src/containers/sidebars/topology/rack/RackNameContainer.js b/src/containers/sidebars/topology/rack/RackNameContainer.js new file mode 100644 index 00000000..34416938 --- /dev/null +++ b/src/containers/sidebars/topology/rack/RackNameContainer.js @@ -0,0 +1,22 @@ +import {connect} from "react-redux"; +import {openEditRackNameModal} from "../../../../actions/modals/topology"; +import RackNameComponent from "../../../../components/sidebars/topology/rack/RackNameComponent"; + +const mapStateToProps = state => { + return { + rackName: state.objects.rack[state.objects.tile[state.interactionLevel.tileId].objectId].name, + }; +}; + +const mapDispatchToProps = dispatch => { + return { + onEdit: () => dispatch(openEditRackNameModal()), + }; +}; + +const RackNameContainer = connect( + mapStateToProps, + mapDispatchToProps +)(RackNameComponent); + +export default RackNameContainer; diff --git a/src/containers/sidebars/topology/room/RackConstructionContainer.js b/src/containers/sidebars/topology/room/RackConstructionContainer.js index e1a481e1..47ca43fc 100644 --- a/src/containers/sidebars/topology/room/RackConstructionContainer.js +++ b/src/containers/sidebars/topology/room/RackConstructionContainer.js @@ -1,17 +1,17 @@ import {connect} from "react-redux"; -import {startObjectConstruction, stopObjectConstruction} from "../../../../actions/topology"; +import {startRackConstruction, stopRackConstruction} from "../../../../actions/topology"; import RackConstructionComponent from "../../../../components/sidebars/topology/room/RackConstructionComponent"; const mapStateToProps = state => { return { - inObjectConstructionMode: state.construction.inObjectConstructionMode, + inRackConstructionMode: state.construction.inRackConstructionMode, }; }; const mapDispatchToProps = dispatch => { return { - onStart: () => dispatch(startObjectConstruction()), - onStop: () => dispatch(stopObjectConstruction()), + onStart: () => dispatch(startRackConstruction()), + onStop: () => dispatch(stopRackConstruction()), }; }; diff --git a/src/pages/App.js b/src/pages/App.js index 001644a2..3aaab454 100644 --- a/src/pages/App.js +++ b/src/pages/App.js @@ -6,7 +6,9 @@ import {openSimulationSucceeded} from "../actions/simulations"; import {fetchLatestDatacenter, resetCurrentDatacenter} from "../actions/topology"; import MapStage from "../components/map/MapStage"; import AppNavbar from "../components/navigation/AppNavbar"; +import DeleteRackModal from "../containers/modals/DeleteRackModal"; import DeleteRoomModal from "../containers/modals/DeleteRoomModal"; +import EditRackNameModal from "../containers/modals/EditRackNameModal"; import EditRoomNameModal from "../containers/modals/EditRoomNameModal"; import TopologySidebar from "../containers/sidebars/topology/TopologySidebar"; import KeymapConfiguration from "../shortcuts/keymap"; @@ -43,6 +45,8 @@ class AppContainer extends React.Component { </div> <EditRoomNameModal/> <DeleteRoomModal/> + <EditRackNameModal/> + <DeleteRackModal/> </div> ); } diff --git a/src/reducers/construction.js b/src/reducers/construction.js index 33485842..772135ff 100644 --- a/src/reducers/construction.js +++ b/src/reducers/construction.js @@ -3,8 +3,8 @@ import { CANCEL_NEW_ROOM_CONSTRUCTION_SUCCEEDED, FINISH_NEW_ROOM_CONSTRUCTION, START_NEW_ROOM_CONSTRUCTION_SUCCEEDED, - START_OBJECT_CONSTRUCTION, - STOP_OBJECT_CONSTRUCTION + START_RACK_CONSTRUCTION, + STOP_RACK_CONSTRUCTION } from "../actions/topology"; export function currentRoomInConstruction(state = -1, action) { @@ -19,11 +19,11 @@ export function currentRoomInConstruction(state = -1, action) { } } -export function inObjectConstructionMode(state = false, action) { +export function inRackConstructionMode(state = false, action) { switch (action.type) { - case START_OBJECT_CONSTRUCTION: + case START_RACK_CONSTRUCTION: return true; - case STOP_OBJECT_CONSTRUCTION: + case STOP_RACK_CONSTRUCTION: return false; default: return state; @@ -32,5 +32,5 @@ export function inObjectConstructionMode(state = false, action) { export const construction = combineReducers({ currentRoomInConstruction, - inObjectConstructionMode, + inRackConstructionMode, }); diff --git a/src/reducers/interaction-level.js b/src/reducers/interaction-level.js index 5ca917c7..b6287aac 100644 --- a/src/reducers/interaction-level.js +++ b/src/reducers/interaction-level.js @@ -1,7 +1,7 @@ import { GO_DOWN_ONE_INTERACTION_LEVEL, GO_FROM_BUILDING_TO_ROOM, - GO_FROM_ROOM_TO_OBJECT + GO_FROM_ROOM_TO_RACK } from "../actions/interaction-level"; export function interactionLevel(state = {mode: "BUILDING"}, action) { @@ -11,9 +11,9 @@ export function interactionLevel(state = {mode: "BUILDING"}, action) { mode: "ROOM", roomId: action.roomId }; - case GO_FROM_ROOM_TO_OBJECT: + case GO_FROM_ROOM_TO_RACK: return { - mode: "OBJECT", + mode: "RACK", roomId: state.roomId, tileId: action.tileId }; @@ -22,7 +22,7 @@ export function interactionLevel(state = {mode: "BUILDING"}, action) { return { mode: "BUILDING" }; - } else if (state.mode === "OBJECT") { + } else if (state.mode === "RACK") { return { mode: "ROOM", roomId: state.roomId diff --git a/src/reducers/modals.js b/src/reducers/modals.js index f6d1efea..7a89dae5 100644 --- a/src/reducers/modals.js +++ b/src/reducers/modals.js @@ -2,9 +2,13 @@ import {combineReducers} from "redux"; import {CLOSE_DELETE_PROFILE_MODAL, OPEN_DELETE_PROFILE_MODAL} from "../actions/modals/profile"; import {CLOSE_NEW_SIMULATION_MODAL, OPEN_NEW_SIMULATION_MODAL} from "../actions/modals/simulations"; import { + CLOSE_DELETE_RACK_MODAL, CLOSE_DELETE_ROOM_MODAL, + CLOSE_EDIT_RACK_NAME_MODAL, CLOSE_EDIT_ROOM_NAME_MODAL, + OPEN_DELETE_RACK_MODAL, OPEN_DELETE_ROOM_MODAL, + OPEN_EDIT_RACK_NAME_MODAL, OPEN_EDIT_ROOM_NAME_MODAL } from "../actions/modals/topology"; @@ -26,4 +30,6 @@ export const modals = combineReducers({ deleteProfileModalVisible: modal(OPEN_DELETE_PROFILE_MODAL, CLOSE_DELETE_PROFILE_MODAL), editRoomNameModalVisible: modal(OPEN_EDIT_ROOM_NAME_MODAL, CLOSE_EDIT_ROOM_NAME_MODAL), deleteRoomModalVisible: modal(OPEN_DELETE_ROOM_MODAL, CLOSE_DELETE_ROOM_MODAL), + editRackNameModalVisible: modal(OPEN_EDIT_RACK_NAME_MODAL, CLOSE_EDIT_RACK_NAME_MODAL), + deleteRackModalVisible: modal(OPEN_DELETE_RACK_MODAL, CLOSE_DELETE_RACK_MODAL), }); diff --git a/src/sagas/index.js b/src/sagas/index.js index 024e40aa..e6c48da4 100644 --- a/src/sagas/index.js +++ b/src/sagas/index.js @@ -2,11 +2,14 @@ import {takeEvery} from "redux-saga/effects"; import {LOG_IN} from "../actions/auth"; import {ADD_SIMULATION, DELETE_SIMULATION} from "../actions/simulations"; import { + ADD_MACHINE, ADD_RACK_TO_TILE, ADD_TILE, CANCEL_NEW_ROOM_CONSTRUCTION, + DELETE_RACK, DELETE_ROOM, DELETE_TILE, + EDIT_RACK_NAME, EDIT_ROOM_NAME, FETCH_LATEST_DATACENTER, START_NEW_ROOM_CONSTRUCTION @@ -15,11 +18,14 @@ import {DELETE_CURRENT_USER, FETCH_AUTHORIZATIONS_OF_CURRENT_USER} from "../acti import {onDeleteCurrentUser} from "./profile"; import {onSimulationAdd, onSimulationDelete} from "./simulations"; import { + onAddMachine, onAddRackToTile, onAddTile, onCancelNewRoomConstruction, + onDeleteRack, onDeleteRoom, onDeleteTile, + onEditRackName, onEditRoomName, onFetchLatestDatacenter, onStartNewRoomConstruction @@ -39,5 +45,8 @@ export default function* rootSaga() { yield takeEvery(DELETE_TILE, onDeleteTile); yield takeEvery(EDIT_ROOM_NAME, onEditRoomName); yield takeEvery(DELETE_ROOM, onDeleteRoom); + yield takeEvery(EDIT_RACK_NAME, onEditRackName); + yield takeEvery(DELETE_RACK, onDeleteRack); yield takeEvery(ADD_RACK_TO_TILE, onAddRackToTile); + yield takeEvery(ADD_MACHINE, onAddMachine); } diff --git a/src/sagas/topology.js b/src/sagas/topology.js index ab544bbe..2ae31074 100644 --- a/src/sagas/topology.js +++ b/src/sagas/topology.js @@ -1,26 +1,40 @@ import {call, put, select} from "redux-saga/effects"; import {addPropToStoreObject, addToStore} from "../actions/objects"; import { + addMachineSucceeded, addRackToTileSucceeded, addTileSucceeded, cancelNewRoomConstructionSucceeded, + deleteRackSucceeded, deleteRoomSucceeded, deleteTileSucceeded, + editRackNameSucceeded, editRoomNameSucceeded, fetchLatestDatacenterSucceeded, startNewRoomConstructionSucceeded } from "../actions/topology"; import {addRoomToDatacenter} from "../api/routes/datacenters"; import {addTileToRoom, deleteRoom, updateRoom} from "../api/routes/rooms"; -import {addRackToTile, deleteTile} from "../api/routes/tiles"; +import { + addMachineToRackOnTile, + addRackToTile, + deleteRackFromTile, + deleteTile, + updateRackOnTile +} from "../api/routes/tiles"; import { fetchAndStoreCoolingItem, + fetchAndStoreCPU, fetchAndStoreDatacenter, + fetchAndStoreGPU, + fetchAndStoreMachinesOfTile, + fetchAndStoreMemory, fetchAndStorePathsOfSimulation, fetchAndStorePSU, fetchAndStoreRackOnTile, fetchAndStoreRoomsOfDatacenter, fetchAndStoreSectionsOfPath, + fetchAndStoreStorage, fetchAndStoreTilesOfRoom } from "./objects"; @@ -69,6 +83,7 @@ function* fetchTile(tile) { case "RACK": const rack = yield fetchAndStoreRackOnTile(tile.objectId, tile.id); yield put(addPropToStoreObject("tile", tile.id, {rackId: rack.id})); + yield fetchMachinesOfRack(tile.id, rack); break; case "COOLING_ITEM": const coolingItem = yield fetchAndStoreCoolingItem(tile.objectId); @@ -79,7 +94,30 @@ function* fetchTile(tile) { yield put(addPropToStoreObject("tile", tile.id, {psuId: psu.id})); break; default: - console.warn("Unknown object type encountered while fetching tile objects"); + console.warn("Unknown rack type encountered while fetching tile objects"); + } +} + +function* fetchMachinesOfRack(tileId, rack) { + const machines = yield fetchAndStoreMachinesOfTile(tileId); + const machineIds = new Array(rack.capacity).fill(null); + machines.forEach(machine => machineIds[machine.position] = machine.id); + + yield put(addPropToStoreObject("rack", rack.id, {machineIds})); + + for (let index in machines) { + for (let i in machines[index].cpuIds) { + yield fetchAndStoreCPU(machines[index].cpuIds[i]); + } + for (let i in machines[index].gpuIds) { + yield fetchAndStoreGPU(machines[index].gpuIds[i]); + } + for (let i in machines[index].memoryIds) { + yield fetchAndStoreMemory(machines[index].memoryIds[i]); + } + for (let i in machines[index].storageIds) { + yield fetchAndStoreStorage(machines[index].storageIds[i]); + } } } @@ -155,6 +193,29 @@ export function* onDeleteRoom() { } } +export function* onEditRackName(action) { + try { + const tileId = yield select(state => state.interactionLevel.tileId); + const rackId = yield select(state => state.objects.tile[state.interactionLevel.tileId].objectId); + const rack = Object.assign({}, yield select(state => state.objects.rack[rackId])); + rack.name = action.name; + yield call(updateRackOnTile, tileId, rack); + yield put(editRackNameSucceeded(action.name)); + } catch (error) { + console.log(error); + } +} + +export function* onDeleteRack() { + try { + const tileId = yield select(state => state.interactionLevel.tileId); + yield call(deleteRackFromTile, tileId); + yield put(deleteRackSucceeded()); + } catch (error) { + console.log(error); + } +} + export function* onAddRackToTile(action) { try { const rack = yield call(addRackToTile, action.tileId, { @@ -170,3 +231,24 @@ export function* onAddRackToTile(action) { console.log(error); } } + +export function* onAddMachine(action) { + try { + const tileId = yield select(state => state.interactionLevel.tileId); + const rackId = yield select(state => state.objects.tile[state.interactionLevel.tileId].objectId); + const machine = yield call(addMachineToRackOnTile, tileId, { + id: -1, + rackId, + position: action.position, + tags: [], + cpuIds: [], + gpuIds: [], + memoryIds: [], + storageIds: [], + }); + yield put(addToStore("machine", machine)); + yield put(addMachineSucceeded(machine)); + } catch (error) { + console.log(error); + } +} diff --git a/src/util/date-time.js b/src/util/date-time.js index 0093e846..ef3524db 100644 --- a/src/util/date-time.js +++ b/src/util/date-time.js @@ -11,12 +11,12 @@ export function parseAndFormatDateTime(dateTimeString) { } /** - * Parses date-time string representations and returns a parsed object. + * Parses date-time string representations and returns a parsed rack. * * The format assumed is "YYYY-MM-DDTHH:MM:SS". * * @param dateTimeString A string expressing a date and a time, in the above mentioned format. - * @returns {object} An object with the parsed date and time information as content. + * @returns {object} An rack with the parsed date and time information as content. */ export function parseDateTime(dateTimeString) { const output = { @@ -45,7 +45,7 @@ export function parseDateTime(dateTimeString) { /** * Serializes the given date and time value to a human-friendly string. * - * @param dateTime An object representation of a date and time. + * @param dateTime An rack representation of a date and time. * @returns {string} A human-friendly string version of that date and time. */ export function formatDateTime(dateTime) { |
