From 5e5ab63b280eb446db4090733cd3ad2e97d02018 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Mon, 19 Jul 2021 15:47:23 +0200 Subject: refactor(ui): Restructure components per page This change updates the source structure of the OpenDC frontend to follow the page structure. --- .../src/components/topologies/map/GrayContainer.js | 34 +++++++++ .../src/components/topologies/map/MapConstants.js | 25 +++++++ .../src/components/topologies/map/MapStage.js | 83 ++++++++++++++++++++++ .../components/topologies/map/MapStage.module.scss | 31 ++++++++ .../src/components/topologies/map/RackContainer.js | 37 ++++++++++ .../topologies/map/RackEnergyFillContainer.js | 34 +++++++++ .../topologies/map/RackSpaceFillContainer.js | 37 ++++++++++ .../src/components/topologies/map/RoomContainer.js | 45 ++++++++++++ .../src/components/topologies/map/TileContainer.js | 46 ++++++++++++ .../components/topologies/map/TopologyContainer.js | 35 +++++++++ .../src/components/topologies/map/WallContainer.js | 37 ++++++++++ .../components/topologies/map/controls/Collapse.js | 42 +++++++++++ .../topologies/map/controls/Collapse.module.scss | 55 ++++++++++++++ .../topologies/map/controls/ScaleIndicator.js | 18 +++++ .../map/controls/ScaleIndicator.module.scss | 10 +++ .../components/topologies/map/controls/Toolbar.js | 35 +++++++++ .../topologies/map/controls/Toolbar.module.scss | 29 ++++++++ .../components/topologies/map/elements/Backdrop.js | 10 +++ .../topologies/map/elements/GrayLayer.js | 24 +++++++ .../topologies/map/elements/HoverTile.js | 30 ++++++++ .../topologies/map/elements/ImageComponent.js | 36 ++++++++++ .../topologies/map/elements/RackFillBar.js | 68 ++++++++++++++++++ .../components/topologies/map/elements/RoomTile.js | 24 +++++++ .../topologies/map/elements/TileObject.js | 27 +++++++ .../topologies/map/elements/TilePlusIcon.js | 44 ++++++++++++ .../topologies/map/elements/WallSegment.js | 32 +++++++++ .../components/topologies/map/groups/GridGroup.js | 36 ++++++++++ .../components/topologies/map/groups/RackGroup.js | 25 +++++++ .../components/topologies/map/groups/RoomGroup.js | 52 ++++++++++++++ .../components/topologies/map/groups/TileGroup.js | 36 ++++++++++ .../topologies/map/groups/TopologyGroup.js | 44 ++++++++++++ .../components/topologies/map/groups/WallGroup.js | 22 ++++++ .../topologies/map/layers/HoverLayerComponent.js | 55 ++++++++++++++ .../components/topologies/map/layers/MapLayer.js | 41 +++++++++++ .../topologies/map/layers/ObjectHoverLayer.js | 53 ++++++++++++++ .../topologies/map/layers/RoomHoverLayer.js | 61 ++++++++++++++++ 36 files changed, 1353 insertions(+) create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/GrayContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/MapConstants.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.module.scss create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/RackContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/RackEnergyFillContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/RackSpaceFillContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/RoomContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/TileContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/TopologyContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/WallContainer.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.module.scss create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.module.scss create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.module.scss create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/elements/Backdrop.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/elements/GrayLayer.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/elements/HoverTile.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/elements/ImageComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/elements/RackFillBar.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/elements/RoomTile.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/elements/TileObject.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/elements/TilePlusIcon.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/elements/WallSegment.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/groups/GridGroup.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/groups/RackGroup.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/groups/RoomGroup.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/groups/TileGroup.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/groups/TopologyGroup.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/groups/WallGroup.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/layers/HoverLayerComponent.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/layers/MapLayer.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/layers/ObjectHoverLayer.js create mode 100644 opendc-web/opendc-web-ui/src/components/topologies/map/layers/RoomHoverLayer.js (limited to 'opendc-web/opendc-web-ui/src/components/topologies/map') diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/GrayContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/GrayContainer.js new file mode 100644 index 00000000..ccf637e5 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/GrayContainer.js @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useDispatch } from 'react-redux' +import { goDownOneInteractionLevel } from '../../../redux/actions/interaction-level' +import GrayLayer from './elements/GrayLayer' + +function GrayContainer() { + const dispatch = useDispatch() + const onClick = () => dispatch(goDownOneInteractionLevel()) + return +} + +export default GrayContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/MapConstants.js b/opendc-web/opendc-web-ui/src/components/topologies/map/MapConstants.js new file mode 100644 index 00000000..4c3b2757 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/MapConstants.js @@ -0,0 +1,25 @@ +export const MAP_SIZE = 50 +export const TILE_SIZE_IN_PIXELS = 100 +export const TILE_SIZE_IN_METERS = 0.5 +export const MAP_SIZE_IN_PIXELS = MAP_SIZE * TILE_SIZE_IN_PIXELS + +export const OBJECT_MARGIN_IN_PIXELS = TILE_SIZE_IN_PIXELS / 5 +export const TILE_PLUS_MARGIN_IN_PIXELS = TILE_SIZE_IN_PIXELS / 3 +export const OBJECT_SIZE_IN_PIXELS = TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS * 2 + +export const GRID_LINE_WIDTH_IN_PIXELS = 2 +export const WALL_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 16 +export const OBJECT_BORDER_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 16 +export const TILE_PLUS_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 10 + +export const RACK_FILL_ICON_WIDTH = OBJECT_SIZE_IN_PIXELS / 3 +export const RACK_FILL_ICON_OPACITY = 0.8 + +export const MAP_MOVE_PIXELS_PER_EVENT = 20 +export const MAP_SCALE_PER_EVENT = 1.1 +export const MAP_MIN_SCALE = 0.5 +export const MAP_MAX_SCALE = 1.5 + +export const MAX_NUM_UNITS_PER_MACHINE = 6 +export const DEFAULT_RACK_SLOT_CAPACITY = 42 +export const DEFAULT_RACK_POWER_CAPACITY = 10000 diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.js b/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.js new file mode 100644 index 00000000..5d19b3ad --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.js @@ -0,0 +1,83 @@ +import React, { useRef, useState } from 'react' +import { HotKeys } from 'react-hotkeys' +import { Stage } from 'react-konva' +import { MAP_MAX_SCALE, MAP_MIN_SCALE, MAP_MOVE_PIXELS_PER_EVENT, MAP_SCALE_PER_EVENT } from './MapConstants' +import { Provider, useStore } from 'react-redux' +import useResizeObserver from 'use-resize-observer' +import { mapContainer } from './MapStage.module.scss' +import MapLayer from './layers/MapLayer' +import RoomHoverLayer from './layers/RoomHoverLayer' +import ObjectHoverLayer from './layers/ObjectHoverLayer' +import ScaleIndicator from './controls/ScaleIndicator' +import Toolbar from './controls/Toolbar' + +function MapStage() { + const store = useStore() + const { ref, width = 100, height = 100 } = useResizeObserver() + const stageRef = useRef(null) + const [[x, y], setPos] = useState([0, 0]) + const [scale, setScale] = useState(1) + + const clampScale = (target) => Math.min(Math.max(target, MAP_MIN_SCALE), MAP_MAX_SCALE) + const moveWithDelta = (deltaX, deltaY) => setPos(([x, y]) => [x + deltaX, y + deltaY]) + + const onZoom = (e) => { + e.evt.preventDefault() + + const stage = stageRef.current.getStage() + const oldScale = scale + + const pointer = stage.getPointerPosition() + const mousePointTo = { + x: (pointer.x - x) / oldScale, + y: (pointer.y - y) / oldScale, + } + + const newScale = clampScale(e.evt.deltaY > 0 ? oldScale * MAP_SCALE_PER_EVENT : oldScale / MAP_SCALE_PER_EVENT) + + setScale(newScale) + setPos([pointer.x - mousePointTo.x * newScale, pointer.y - mousePointTo.y * newScale]) + } + const onZoomButton = (zoomIn) => + setScale((scale) => clampScale(zoomIn ? scale * MAP_SCALE_PER_EVENT : scale / MAP_SCALE_PER_EVENT)) + const onDragEnd = (e) => setPos([e.target.x(), e.target.y()]) + const onExport = () => { + const download = document.createElement('a') + download.href = stageRef.current.getStage().toDataURL() + download.download = 'opendc-canvas-export-' + Date.now() + '.png' + download.click() + } + + const handlers = { + MOVE_LEFT: () => moveWithDelta(MAP_MOVE_PIXELS_PER_EVENT, 0), + MOVE_RIGHT: () => moveWithDelta(-MAP_MOVE_PIXELS_PER_EVENT, 0), + MOVE_UP: () => moveWithDelta(0, MAP_MOVE_PIXELS_PER_EVENT), + MOVE_DOWN: () => moveWithDelta(0, -MAP_MOVE_PIXELS_PER_EVENT), + } + + return ( + + + + + + + + + + + + ) +} + +export default MapStage diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.module.scss b/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.module.scss new file mode 100644 index 00000000..d879b4c8 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.module.scss @@ -0,0 +1,31 @@ +/*! + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +.mapContainer { + background-color: var(--pf-global--Color--light-200); + position: relative; + display: flex; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; +} diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/RackContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/RackContainer.js new file mode 100644 index 00000000..14449a91 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/RackContainer.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useSelector } from 'react-redux' +import { Tile } from '../../../shapes' +import RackGroup from './groups/RackGroup' + +function RackContainer({ tile }) { + const interactionLevel = useSelector((state) => state.interactionLevel) + return +} + +RackContainer.propTypes = { + tile: Tile, +} + +export default RackContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/RackEnergyFillContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/RackEnergyFillContainer.js new file mode 100644 index 00000000..c35cbde7 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/RackEnergyFillContainer.js @@ -0,0 +1,34 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { useSelector } from 'react-redux' +import RackFillBar from './elements/RackFillBar' + +function RackSpaceFillContainer({ tileId, ...props }) { + const fillFraction = useSelector((state) => { + let energyConsumptionTotal = 0 + const rack = state.objects.rack[state.objects.tile[tileId].rack] + const machineIds = rack.machines + machineIds.forEach((machineId) => { + if (machineId !== null) { + const machine = state.objects.machine[machineId] + machine.cpus.forEach((id) => (energyConsumptionTotal += state.objects.cpu[id].energyConsumptionW)) + machine.gpus.forEach((id) => (energyConsumptionTotal += state.objects.gpu[id].energyConsumptionW)) + machine.memories.forEach( + (id) => (energyConsumptionTotal += state.objects.memory[id].energyConsumptionW) + ) + machine.storages.forEach( + (id) => (energyConsumptionTotal += state.objects.storage[id].energyConsumptionW) + ) + } + }) + + return Math.min(1, energyConsumptionTotal / rack.powerCapacityW) + }) + return +} + +RackSpaceFillContainer.propTypes = { + tileId: PropTypes.string.isRequired, +} + +export default RackSpaceFillContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/RackSpaceFillContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/RackSpaceFillContainer.js new file mode 100644 index 00000000..a6766f33 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/RackSpaceFillContainer.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import PropTypes from 'prop-types' +import { useSelector } from 'react-redux' +import RackFillBar from './elements/RackFillBar' + +function RackSpaceFillContainer({ tileId, ...props }) { + const rack = useSelector((state) => state.objects.rack[state.objects.tile[tileId].rack]) + return +} + +RackSpaceFillContainer.propTypes = { + tileId: PropTypes.string.isRequired, +} + +export default RackSpaceFillContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/RoomContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/RoomContainer.js new file mode 100644 index 00000000..93ba9c93 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/RoomContainer.js @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { goFromBuildingToRoom } from '../../../redux/actions/interaction-level' +import RoomGroup from './groups/RoomGroup' + +function RoomContainer({ roomId, ...props }) { + const state = useSelector((state) => { + return { + interactionLevel: state.interactionLevel, + currentRoomInConstruction: state.construction.currentRoomInConstruction, + room: state.objects.room[roomId], + } + }) + const dispatch = useDispatch() + return dispatch(goFromBuildingToRoom(roomId))} /> +} + +RoomContainer.propTypes = { + roomId: PropTypes.string, +} + +export default RoomContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/TileContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/TileContainer.js new file mode 100644 index 00000000..149e26a1 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/TileContainer.js @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import PropTypes from 'prop-types' +import { useDispatch, useSelector } from 'react-redux' +import { goFromRoomToRack } from '../../../redux/actions/interaction-level' +import TileGroup from './groups/TileGroup' + +function TileContainer({ tileId, ...props }) { + const interactionLevel = useSelector((state) => state.interactionLevel) + const tile = useSelector((state) => state.objects.tile[tileId]) + + const dispatch = useDispatch() + const onClick = (tile) => { + if (tile.rack) { + dispatch(goFromRoomToRack(tile._id)) + } + } + return +} + +TileContainer.propTypes = { + tileId: PropTypes.string.isRequired, +} + +export default TileContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/TopologyContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/TopologyContainer.js new file mode 100644 index 00000000..eaebabd5 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/TopologyContainer.js @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useSelector } from 'react-redux' +import { useActiveTopology } from '../../../data/topology' +import TopologyGroup from './groups/TopologyGroup' + +function TopologyContainer() { + const topology = useActiveTopology() + const interactionLevel = useSelector((state) => state.interactionLevel) + + return +} + +export default TopologyContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/WallContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/WallContainer.js new file mode 100644 index 00000000..77f553dd --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/WallContainer.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import PropTypes from 'prop-types' +import { useSelector } from 'react-redux' +import WallGroup from './groups/WallGroup' + +function WallContainer({ roomId, ...props }) { + const tiles = useSelector((state) => state.objects.room[roomId].tiles.map((tileId) => state.objects.tile[tileId])) + return +} + +WallContainer.propTypes = { + roomId: PropTypes.string.isRequired, +} + +export default WallContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.js b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.js new file mode 100644 index 00000000..f54b7c84 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.js @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import PropTypes from 'prop-types' +import { ChevronLeftIcon } from '@patternfly/react-icons' +import { collapseContainer } from './Collapse.module.scss' +import { Button } from '@patternfly/react-core' + +function Collapse({ onClick }) { + return ( +
+ +
+ ) +} + +Collapse.propTypes = { + onClick: PropTypes.func, +} + +export default Collapse diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.module.scss b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.module.scss new file mode 100644 index 00000000..0c1fac94 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.module.scss @@ -0,0 +1,55 @@ +/*! + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +.collapseContainer { + position: absolute; + right: var(--pf-global--spacer--xs); + top: 0; + bottom: 10%; + margin: auto 0; + height: 50px; + + button:global(.pf-m-tertiary) { + height: 100%; + padding: 2px; + + margin-right: var(--pf-global--spacer--xs); + margin-top: var(--pf-global--spacer--xs); + background-color: var(--pf-global--BackgroundColor--100); + border: none; + border-radius: var(--pf-global--BorderRadius--sm); + box-shadow: var(--pf-global--BoxShadow--sm); + + &:not(:global(.pf-m-disabled)) { + background-color: var(--pf-global--BackgroundColor--100); + } + + &:after { + display: none; + } + + &:hover { + border: none; + box-shadow: var(--pf-global--BoxShadow--md); + } + } +} diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.js b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.js new file mode 100644 index 00000000..58d2ccc9 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.js @@ -0,0 +1,18 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { TILE_SIZE_IN_METERS, TILE_SIZE_IN_PIXELS } from '../MapConstants' +import { scaleIndicator } from './ScaleIndicator.module.scss' + +function ScaleIndicator({ scale }) { + return ( +
+ {TILE_SIZE_IN_METERS}m +
+ ) +} + +ScaleIndicator.propTypes = { + scale: PropTypes.number.isRequired, +} + +export default ScaleIndicator diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.module.scss b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.module.scss new file mode 100644 index 00000000..f19e0ff2 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.module.scss @@ -0,0 +1,10 @@ +.scaleIndicator { + position: absolute; + right: 10px; + bottom: 10px; + z-index: 50; + + border: solid 2px #212529; + border-top: none; + border-left: none; +} diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.js b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.js new file mode 100644 index 00000000..469fd515 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.js @@ -0,0 +1,35 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { control, toolBar } from './Toolbar.module.scss' +import { Button } from '@patternfly/react-core' +import { SearchPlusIcon, SearchMinusIcon } from '@patternfly/react-icons' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCamera } from '@fortawesome/free-solid-svg-icons' + +function Toolbar({ onZoom, onExport }) { + return ( +
+ + + +
+ ) +} + +Toolbar.propTypes = { + onZoom: PropTypes.func, + onExport: PropTypes.func, +} + +export default Toolbar diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.module.scss b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.module.scss new file mode 100644 index 00000000..0d505acc --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.module.scss @@ -0,0 +1,29 @@ +.toolBar { + position: absolute; + bottom: var(--pf-global--spacer--md); + left: var(--pf-global--spacer--xl); +} + +.control { + &:global(.pf-m-tertiary) { + margin-right: var(--pf-global--spacer--xs); + margin-top: var(--pf-global--spacer--xs); + background-color: var(--pf-global--BackgroundColor--100); + border: none; + border-radius: var(--pf-global--BorderRadius--sm); + box-shadow: var(--pf-global--BoxShadow--sm); + + &:not(:global(.pf-m-disabled)) { + background-color: var(--pf-global--BackgroundColor--100); + } + + &:after { + display: none; + } + + &:hover { + border: none; + box-shadow: var(--pf-global--BoxShadow--md); + } + } +} diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/Backdrop.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/Backdrop.js new file mode 100644 index 00000000..93037b51 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/Backdrop.js @@ -0,0 +1,10 @@ +import React from 'react' +import { Rect } from 'react-konva' +import { BACKDROP_COLOR } from '../../../../util/colors' +import { MAP_SIZE_IN_PIXELS } from '../MapConstants' + +function Backdrop() { + return +} + +export default Backdrop diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/GrayLayer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/GrayLayer.js new file mode 100644 index 00000000..08c687f6 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/GrayLayer.js @@ -0,0 +1,24 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Rect } from 'react-konva' +import { GRAYED_OUT_AREA_COLOR } from '../../../../util/colors' +import { MAP_SIZE_IN_PIXELS } from '../MapConstants' + +function GrayLayer({ onClick }) { + return ( + + ) +} + +GrayLayer.propTypes = { + onClick: PropTypes.func, +} + +export default GrayLayer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/HoverTile.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/HoverTile.js new file mode 100644 index 00000000..20c2c6d1 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/HoverTile.js @@ -0,0 +1,30 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Rect } from 'react-konva' +import { ROOM_HOVER_INVALID_COLOR, ROOM_HOVER_VALID_COLOR } from '../../../../util/colors' +import { TILE_SIZE_IN_PIXELS } from '../MapConstants' + +function HoverTile({ x, y, isValid, scale = 1, onClick }) { + return ( + + ) +} + +HoverTile.propTypes = { + x: PropTypes.number.isRequired, + y: PropTypes.number.isRequired, + isValid: PropTypes.bool.isRequired, + scale: PropTypes.number, + onClick: PropTypes.func.isRequired, +} + +export default HoverTile diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/ImageComponent.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/ImageComponent.js new file mode 100644 index 00000000..7d304b6b --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/ImageComponent.js @@ -0,0 +1,36 @@ +import PropTypes from 'prop-types' +import React, { useEffect, useState } from 'react' +import { Image } from 'react-konva' + +const imageCaches = {} + +function ImageComponent({ src, x, y, width, height, opacity }) { + const [image, setImage] = useState(null) + + useEffect(() => { + if (imageCaches[src]) { + setImage(imageCaches[src]) + return + } + + const image = new window.Image() + image.src = src + image.onload = () => { + setImage(image) + imageCaches[src] = image + } + }, [src]) + + return +} + +ImageComponent.propTypes = { + src: PropTypes.string.isRequired, + x: PropTypes.number.isRequired, + y: PropTypes.number.isRequired, + width: PropTypes.number.isRequired, + height: PropTypes.number.isRequired, + opacity: PropTypes.number.isRequired, +} + +export default ImageComponent diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/RackFillBar.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/RackFillBar.js new file mode 100644 index 00000000..aa284944 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/RackFillBar.js @@ -0,0 +1,68 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Group, Rect } from 'react-konva' +import { + RACK_ENERGY_BAR_BACKGROUND_COLOR, + RACK_ENERGY_BAR_FILL_COLOR, + RACK_SPACE_BAR_BACKGROUND_COLOR, + RACK_SPACE_BAR_FILL_COLOR, +} from '../../../../util/colors' +import { + OBJECT_BORDER_WIDTH_IN_PIXELS, + OBJECT_MARGIN_IN_PIXELS, + RACK_FILL_ICON_OPACITY, + RACK_FILL_ICON_WIDTH, + TILE_SIZE_IN_PIXELS, +} from '../MapConstants' +import ImageComponent from './ImageComponent' + +function RackFillBar({ positionX, positionY, type, fillFraction }) { + const halfOfObjectBorderWidth = OBJECT_BORDER_WIDTH_IN_PIXELS / 2 + const x = + positionX * TILE_SIZE_IN_PIXELS + + OBJECT_MARGIN_IN_PIXELS + + (type === 'space' ? halfOfObjectBorderWidth : 0.5 * (TILE_SIZE_IN_PIXELS - 2 * OBJECT_MARGIN_IN_PIXELS)) + const startY = positionY * TILE_SIZE_IN_PIXELS + OBJECT_MARGIN_IN_PIXELS + halfOfObjectBorderWidth + const width = 0.5 * (TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS * 2) - halfOfObjectBorderWidth + const fullHeight = TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS * 2 - OBJECT_BORDER_WIDTH_IN_PIXELS + + const fractionHeight = fillFraction * fullHeight + const fractionY = + (positionY + 1) * TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS - halfOfObjectBorderWidth - fractionHeight + + return ( + + + + + + ) +} + +RackFillBar.propTypes = { + positionX: PropTypes.number.isRequired, + positionY: PropTypes.number.isRequired, + type: PropTypes.string.isRequired, + fillFraction: PropTypes.number.isRequired, +} + +export default RackFillBar diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/RoomTile.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/RoomTile.js new file mode 100644 index 00000000..e7329dc0 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/RoomTile.js @@ -0,0 +1,24 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Rect } from 'react-konva' +import { Tile } from '../../../../shapes' +import { TILE_SIZE_IN_PIXELS } from '../MapConstants' + +function RoomTile({ tile, color }) { + return ( + + ) +} + +RoomTile.propTypes = { + tile: Tile, + color: PropTypes.string, +} + +export default RoomTile diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/TileObject.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/TileObject.js new file mode 100644 index 00000000..3211f187 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/TileObject.js @@ -0,0 +1,27 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Rect } from 'react-konva' +import { OBJECT_BORDER_COLOR } from '../../../../util/colors' +import { OBJECT_BORDER_WIDTH_IN_PIXELS, OBJECT_MARGIN_IN_PIXELS, TILE_SIZE_IN_PIXELS } from '../MapConstants' + +function TileObject({ positionX, positionY, color }) { + return ( + + ) +} + +TileObject.propTypes = { + positionX: PropTypes.number.isRequired, + positionY: PropTypes.number.isRequired, + color: PropTypes.string.isRequired, +} + +export default TileObject diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/TilePlusIcon.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/TilePlusIcon.js new file mode 100644 index 00000000..186c2b3a --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/TilePlusIcon.js @@ -0,0 +1,44 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Group, Line } from 'react-konva' +import { TILE_PLUS_COLOR } from '../../../../util/colors' +import { TILE_PLUS_MARGIN_IN_PIXELS, TILE_PLUS_WIDTH_IN_PIXELS, TILE_SIZE_IN_PIXELS } from '../MapConstants' + +function TilePlusIcon({ x, y, scale = 1 }) { + const linePoints = [ + [ + x + 0.5 * TILE_SIZE_IN_PIXELS * scale, + y + TILE_PLUS_MARGIN_IN_PIXELS * scale, + x + 0.5 * TILE_SIZE_IN_PIXELS * scale, + y + TILE_SIZE_IN_PIXELS * scale - TILE_PLUS_MARGIN_IN_PIXELS * scale, + ], + [ + x + TILE_PLUS_MARGIN_IN_PIXELS * scale, + y + 0.5 * TILE_SIZE_IN_PIXELS * scale, + x + TILE_SIZE_IN_PIXELS * scale - TILE_PLUS_MARGIN_IN_PIXELS * scale, + y + 0.5 * TILE_SIZE_IN_PIXELS * scale, + ], + ] + return ( + + {linePoints.map((points, index) => ( + + ))} + + ) +} + +TilePlusIcon.propTypes = { + x: PropTypes.number, + y: PropTypes.number, + scale: PropTypes.number, +} + +export default TilePlusIcon diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/WallSegment.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/WallSegment.js new file mode 100644 index 00000000..4f18813e --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/WallSegment.js @@ -0,0 +1,32 @@ +import React from 'react' +import { Line } from 'react-konva' +import { WallSegment as WallSegmentShape } from '../../../../shapes' +import { WALL_COLOR } from '../../../../util/colors' +import { TILE_SIZE_IN_PIXELS, WALL_WIDTH_IN_PIXELS } from '../MapConstants' + +function WallSegment({ wallSegment }) { + let points + if (wallSegment.isHorizontal) { + points = [ + wallSegment.startPosX * TILE_SIZE_IN_PIXELS, + wallSegment.startPosY * TILE_SIZE_IN_PIXELS, + (wallSegment.startPosX + wallSegment.length) * TILE_SIZE_IN_PIXELS, + wallSegment.startPosY * TILE_SIZE_IN_PIXELS, + ] + } else { + points = [ + wallSegment.startPosX * TILE_SIZE_IN_PIXELS, + wallSegment.startPosY * TILE_SIZE_IN_PIXELS, + wallSegment.startPosX * TILE_SIZE_IN_PIXELS, + (wallSegment.startPosY + wallSegment.length) * TILE_SIZE_IN_PIXELS, + ] + } + + return +} + +WallSegment.propTypes = { + wallSegment: WallSegmentShape, +} + +export default WallSegment diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/GridGroup.js b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/GridGroup.js new file mode 100644 index 00000000..d66a18de --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/GridGroup.js @@ -0,0 +1,36 @@ +import React from 'react' +import { Group, Line } from 'react-konva' +import { GRID_COLOR } from '../../../../util/colors' +import { GRID_LINE_WIDTH_IN_PIXELS, MAP_SIZE, MAP_SIZE_IN_PIXELS, TILE_SIZE_IN_PIXELS } from '../MapConstants' + +const MAP_COORDINATE_ENTRIES = Array.from(new Array(MAP_SIZE), (x, i) => i) +const HORIZONTAL_POINT_PAIRS = MAP_COORDINATE_ENTRIES.map((index) => [ + 0, + index * TILE_SIZE_IN_PIXELS, + MAP_SIZE_IN_PIXELS, + index * TILE_SIZE_IN_PIXELS, +]) +const VERTICAL_POINT_PAIRS = MAP_COORDINATE_ENTRIES.map((index) => [ + index * TILE_SIZE_IN_PIXELS, + 0, + index * TILE_SIZE_IN_PIXELS, + MAP_SIZE_IN_PIXELS, +]) + +function GridGroup() { + return ( + + {HORIZONTAL_POINT_PAIRS.concat(VERTICAL_POINT_PAIRS).map((points, index) => ( + + ))} + + ) +} + +export default GridGroup diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/RackGroup.js b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/RackGroup.js new file mode 100644 index 00000000..46030135 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/RackGroup.js @@ -0,0 +1,25 @@ +import React from 'react' +import { Group } from 'react-konva' +import { Tile } from '../../../../shapes' +import { RACK_BACKGROUND_COLOR } from '../../../../util/colors' +import TileObject from '../elements/TileObject' +import RackSpaceFillContainer from '../RackSpaceFillContainer' +import RackEnergyFillContainer from '../RackEnergyFillContainer' + +function RackGroup({ tile }) { + return ( + + + + + + + + ) +} + +RackGroup.propTypes = { + tile: Tile, +} + +export default RackGroup diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/RoomGroup.js b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/RoomGroup.js new file mode 100644 index 00000000..a42e7bb7 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/RoomGroup.js @@ -0,0 +1,52 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Group } from 'react-konva' +import { InteractionLevel, Room } from '../../../../shapes' +import GrayContainer from '../GrayContainer' +import TileContainer from '../TileContainer' +import WallContainer from '../WallContainer' + +function RoomGroup({ room, interactionLevel, currentRoomInConstruction, onClick }) { + if (currentRoomInConstruction === room._id) { + return ( + + {room.tiles.map((tileId) => ( + + ))} + + ) + } + + return ( + + {(() => { + if ( + (interactionLevel.mode === 'RACK' || interactionLevel.mode === 'MACHINE') && + interactionLevel.roomId === room._id + ) { + return [ + room.tiles + .filter((tileId) => tileId !== interactionLevel.tileId) + .map((tileId) => ), + , + room.tiles + .filter((tileId) => tileId === interactionLevel.tileId) + .map((tileId) => ), + ] + } else { + return room.tiles.map((tileId) => ) + } + })()} + + + ) +} + +RoomGroup.propTypes = { + room: Room, + interactionLevel: InteractionLevel, + currentRoomInConstruction: PropTypes.string, + onClick: PropTypes.func, +} + +export default RoomGroup diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/TileGroup.js b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/TileGroup.js new file mode 100644 index 00000000..f2084017 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/TileGroup.js @@ -0,0 +1,36 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Group } from 'react-konva' +import { Tile } from '../../../../shapes' +import { ROOM_DEFAULT_COLOR, ROOM_IN_CONSTRUCTION_COLOR } from '../../../../util/colors' +import RoomTile from '../elements/RoomTile' +import RackContainer from '../RackContainer' + +function TileGroup({ tile, newTile, onClick }) { + let tileObject + if (tile.rack) { + tileObject = + } else { + tileObject = null + } + + let color = ROOM_DEFAULT_COLOR + if (newTile) { + color = ROOM_IN_CONSTRUCTION_COLOR + } + + return ( + onClick(tile)}> + + {tileObject} + + ) +} + +TileGroup.propTypes = { + tile: Tile, + newTile: PropTypes.bool, + onClick: PropTypes.func, +} + +export default TileGroup diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/TopologyGroup.js b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/TopologyGroup.js new file mode 100644 index 00000000..011dcf34 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/TopologyGroup.js @@ -0,0 +1,44 @@ +import React from 'react' +import { Group } from 'react-konva' +import { InteractionLevel, Topology } from '../../../../shapes' +import RoomContainer from '../RoomContainer' +import GrayContainer from '../GrayContainer' + +function TopologyGroup({ topology, interactionLevel }) { + if (!topology) { + return + } + + if (interactionLevel.mode === 'BUILDING') { + return ( + + {topology.rooms.map((roomId) => ( + + ))} + + ) + } + + return ( + + {topology.rooms + .filter((roomId) => roomId !== interactionLevel.roomId) + .map((roomId) => ( + + ))} + {interactionLevel.mode === 'ROOM' ? : null} + {topology.rooms + .filter((roomId) => roomId === interactionLevel.roomId) + .map((roomId) => ( + + ))} + + ) +} + +TopologyGroup.propTypes = { + topology: Topology, + interactionLevel: InteractionLevel, +} + +export default TopologyGroup diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/WallGroup.js b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/WallGroup.js new file mode 100644 index 00000000..6cbd1cd0 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/WallGroup.js @@ -0,0 +1,22 @@ +import PropTypes from 'prop-types' +import React from 'react' +import { Group } from 'react-konva' +import { Tile } from '../../../../shapes' +import { deriveWallLocations } from '../../../../util/tile-calculations' +import WallSegment from '../elements/WallSegment' + +function WallGroup({ tiles }) { + return ( + + {deriveWallLocations(tiles).map((wallSegment, index) => ( + + ))} + + ) +} + +WallGroup.propTypes = { + tiles: PropTypes.arrayOf(Tile).isRequired, +} + +export default WallGroup diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/HoverLayerComponent.js b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/HoverLayerComponent.js new file mode 100644 index 00000000..2b1060c0 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/HoverLayerComponent.js @@ -0,0 +1,55 @@ +import PropTypes from 'prop-types' +import React, { useMemo, useState } from 'react' +import { Layer } from 'react-konva/lib/ReactKonva' +import HoverTile from '../elements/HoverTile' +import { TILE_SIZE_IN_PIXELS } from '../MapConstants' +import { useEffectRef } from '../../../../util/effect-ref' + +function HoverLayerComponent({ isEnabled, isValid, onClick, children }) { + const [[mouseWorldX, mouseWorldY], setPos] = useState([0, 0]) + + const layerRef = useEffectRef((layer) => { + if (!layer) { + return + } + + const stage = layer.getStage() + + // Transform used to convert mouse coordinates to world coordinates + const transform = stage.getAbsoluteTransform().copy() + transform.invert() + + stage.on('mousemove.hover', () => { + const { x, y } = transform.point(stage.getPointerPosition()) + setPos([x, y]) + }) + return () => stage.off('mousemove.hover') + }) + + const gridX = Math.floor(mouseWorldX / TILE_SIZE_IN_PIXELS) + const gridY = Math.floor(mouseWorldY / TILE_SIZE_IN_PIXELS) + const valid = useMemo(() => isEnabled && isValid(gridX, gridY), [isEnabled, isValid, gridX, gridY]) + + if (!isEnabled) { + return + } + + const x = gridX * TILE_SIZE_IN_PIXELS + const y = gridY * TILE_SIZE_IN_PIXELS + + return ( + + (valid ? onClick(gridX, gridY) : undefined)} /> + {children ? React.cloneElement(children, { x, y, scale: 1 }) : undefined} + + ) +} + +HoverLayerComponent.propTypes = { + isEnabled: PropTypes.bool.isRequired, + isValid: PropTypes.func.isRequired, + onClick: PropTypes.func.isRequired, + children: PropTypes.node, +} + +export default HoverLayerComponent diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/MapLayer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/MapLayer.js new file mode 100644 index 00000000..c902532b --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/MapLayer.js @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { Group, Layer } from 'react-konva' +import Backdrop from '../elements/Backdrop' +import TopologyContainer from '../TopologyContainer' +import GridGroup from '../groups/GridGroup' + +function MapLayer() { + return ( + + + + + + + + ) +} + +export default MapLayer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/ObjectHoverLayer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/ObjectHoverLayer.js new file mode 100644 index 00000000..47d9c992 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/ObjectHoverLayer.js @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { addRackToTile } from '../../../../redux/actions/topology/room' +import { findTileWithPosition } from '../../../../util/tile-calculations' +import HoverLayerComponent from './HoverLayerComponent' +import TilePlusIcon from '../elements/TilePlusIcon' + +function ObjectHoverLayer() { + const isEnabled = useSelector((state) => state.construction.inRackConstructionMode) + const isValid = useSelector((state) => (x, y) => { + if (state.interactionLevel.mode !== 'ROOM') { + return false + } + + const currentRoom = state.objects.room[state.interactionLevel.roomId] + const tiles = currentRoom.tiles.map((tileId) => state.objects.tile[tileId]) + const tile = findTileWithPosition(tiles, x, y) + + return !(tile === null || tile.rack) + }) + + const dispatch = useDispatch() + const onClick = (x, y) => dispatch(addRackToTile(x, y)) + return ( + + + + ) +} + +export default ObjectHoverLayer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/RoomHoverLayer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/RoomHoverLayer.js new file mode 100644 index 00000000..59f83b2b --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/RoomHoverLayer.js @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import React from 'react' +import { useDispatch, useSelector } from 'react-redux' +import { toggleTileAtLocation } from '../../../../redux/actions/topology/building' +import { + deriveValidNextTilePositions, + findPositionInPositions, + findPositionInRooms, +} from '../../../../util/tile-calculations' +import HoverLayerComponent from './HoverLayerComponent' + +function RoomHoverLayer() { + const dispatch = useDispatch() + const onClick = (x, y) => dispatch(toggleTileAtLocation(x, y)) + const isEnabled = useSelector((state) => state.construction.currentRoomInConstruction !== '-1') + const isValid = useSelector((state) => (x, y) => { + const newRoom = { ...state.objects.room[state.construction.currentRoomInConstruction] } + const oldRooms = Object.keys(state.objects.room) + .map((id) => ({ ...state.objects.room[id] })) + .filter( + (room) => + state.objects.topology[state.currentTopologyId].rooms.indexOf(room._id) !== -1 && + room._id !== state.construction.currentRoomInConstruction + ) + + ;[...oldRooms, newRoom].forEach((room) => { + room.tiles = room.tiles.map((tileId) => state.objects.tile[tileId]) + }) + if (newRoom.tiles.length === 0) { + return findPositionInRooms(oldRooms, x, y) === -1 + } + + const validNextPositions = deriveValidNextTilePositions(oldRooms, newRoom.tiles) + return findPositionInPositions(validNextPositions, x, y) !== -1 + }) + + return +} + +export default RoomHoverLayer -- cgit v1.2.3 From 54f424a18cc21a52ea518d40893218a07ab55989 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 21 Jul 2021 15:04:22 +0200 Subject: feat(ui): Extract topology construction out of Sagas This change updates the OpenDC frontend to perform the construction of the topology directly in the reducers instead of performing the mutations in Redux Sagas as side effects. This allows us to nicely map actions to mutations in the reducers. --- .../opendc-web-ui/src/components/topologies/map/MapStage.js | 10 +++++----- .../src/components/topologies/map/RackEnergyFillContainer.js | 12 ++++++------ .../src/components/topologies/map/RackSpaceFillContainer.js | 2 +- .../src/components/topologies/map/RoomContainer.js | 2 +- .../src/components/topologies/map/TileContainer.js | 2 +- .../src/components/topologies/map/TopologyContainer.js | 3 +-- .../src/components/topologies/map/WallContainer.js | 4 +++- .../src/components/topologies/map/layers/ObjectHoverLayer.js | 4 ++-- .../src/components/topologies/map/layers/RoomHoverLayer.js | 10 +++++----- 9 files changed, 25 insertions(+), 24 deletions(-) (limited to 'opendc-web/opendc-web-ui/src/components/topologies/map') diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.js b/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.js index 5d19b3ad..d8735cf1 100644 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.js +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.js @@ -1,8 +1,8 @@ -import React, { useRef, useState } from 'react' +import React, { useRef, useState, useContext } from 'react' import { HotKeys } from 'react-hotkeys' import { Stage } from 'react-konva' import { MAP_MAX_SCALE, MAP_MIN_SCALE, MAP_MOVE_PIXELS_PER_EVENT, MAP_SCALE_PER_EVENT } from './MapConstants' -import { Provider, useStore } from 'react-redux' +import { ReactReduxContext } from 'react-redux' import useResizeObserver from 'use-resize-observer' import { mapContainer } from './MapStage.module.scss' import MapLayer from './layers/MapLayer' @@ -12,7 +12,7 @@ import ScaleIndicator from './controls/ScaleIndicator' import Toolbar from './controls/Toolbar' function MapStage() { - const store = useStore() + const reduxContext = useContext(ReactReduxContext) const { ref, width = 100, height = 100 } = useResizeObserver() const stageRef = useRef(null) const [[x, y], setPos] = useState([0, 0]) @@ -68,11 +68,11 @@ function MapStage() { x={x} y={y} > - + - + diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/RackEnergyFillContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/RackEnergyFillContainer.js index c35cbde7..be1f3e45 100644 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/RackEnergyFillContainer.js +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/RackEnergyFillContainer.js @@ -6,18 +6,18 @@ import RackFillBar from './elements/RackFillBar' function RackSpaceFillContainer({ tileId, ...props }) { const fillFraction = useSelector((state) => { let energyConsumptionTotal = 0 - const rack = state.objects.rack[state.objects.tile[tileId].rack] + const rack = state.topology.racks[state.topology.tiles[tileId].rack] const machineIds = rack.machines machineIds.forEach((machineId) => { if (machineId !== null) { - const machine = state.objects.machine[machineId] - machine.cpus.forEach((id) => (energyConsumptionTotal += state.objects.cpu[id].energyConsumptionW)) - machine.gpus.forEach((id) => (energyConsumptionTotal += state.objects.gpu[id].energyConsumptionW)) + const machine = state.topology.machines[machineId] + machine.cpus.forEach((id) => (energyConsumptionTotal += state.topology.cpus[id].energyConsumptionW)) + machine.gpus.forEach((id) => (energyConsumptionTotal += state.topology.gpus[id].energyConsumptionW)) machine.memories.forEach( - (id) => (energyConsumptionTotal += state.objects.memory[id].energyConsumptionW) + (id) => (energyConsumptionTotal += state.topology.memories[id].energyConsumptionW) ) machine.storages.forEach( - (id) => (energyConsumptionTotal += state.objects.storage[id].energyConsumptionW) + (id) => (energyConsumptionTotal += state.topology.storages[id].energyConsumptionW) ) } }) diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/RackSpaceFillContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/RackSpaceFillContainer.js index a6766f33..0c15d54b 100644 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/RackSpaceFillContainer.js +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/RackSpaceFillContainer.js @@ -26,7 +26,7 @@ import { useSelector } from 'react-redux' import RackFillBar from './elements/RackFillBar' function RackSpaceFillContainer({ tileId, ...props }) { - const rack = useSelector((state) => state.objects.rack[state.objects.tile[tileId].rack]) + const rack = useSelector((state) => state.topology.racks[state.topology.tiles[tileId].rack]) return } diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/RoomContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/RoomContainer.js index 93ba9c93..65189891 100644 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/RoomContainer.js +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/RoomContainer.js @@ -31,7 +31,7 @@ function RoomContainer({ roomId, ...props }) { return { interactionLevel: state.interactionLevel, currentRoomInConstruction: state.construction.currentRoomInConstruction, - room: state.objects.room[roomId], + room: state.topology.rooms[roomId], } }) const dispatch = useDispatch() diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/TileContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/TileContainer.js index 149e26a1..411a5ca7 100644 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/TileContainer.js +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/TileContainer.js @@ -28,7 +28,7 @@ import TileGroup from './groups/TileGroup' function TileContainer({ tileId, ...props }) { const interactionLevel = useSelector((state) => state.interactionLevel) - const tile = useSelector((state) => state.objects.tile[tileId]) + const tile = useSelector((state) => state.topology.tiles[tileId]) const dispatch = useDispatch() const onClick = (tile) => { diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/TopologyContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/TopologyContainer.js index eaebabd5..cc0d46b3 100644 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/TopologyContainer.js +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/TopologyContainer.js @@ -22,11 +22,10 @@ import React from 'react' import { useSelector } from 'react-redux' -import { useActiveTopology } from '../../../data/topology' import TopologyGroup from './groups/TopologyGroup' function TopologyContainer() { - const topology = useActiveTopology() + const topology = useSelector((state) => state.topology.root) const interactionLevel = useSelector((state) => state.interactionLevel) return diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/WallContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/WallContainer.js index 77f553dd..143f70c2 100644 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/WallContainer.js +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/WallContainer.js @@ -26,7 +26,9 @@ import { useSelector } from 'react-redux' import WallGroup from './groups/WallGroup' function WallContainer({ roomId, ...props }) { - const tiles = useSelector((state) => state.objects.room[roomId].tiles.map((tileId) => state.objects.tile[tileId])) + const tiles = useSelector((state) => { + return state.topology.rooms[roomId].tiles.map((tileId) => state.topology.tiles[tileId]) + }) return } diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/ObjectHoverLayer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/ObjectHoverLayer.js index 47d9c992..1f00de36 100644 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/ObjectHoverLayer.js +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/ObjectHoverLayer.js @@ -34,8 +34,8 @@ function ObjectHoverLayer() { return false } - const currentRoom = state.objects.room[state.interactionLevel.roomId] - const tiles = currentRoom.tiles.map((tileId) => state.objects.tile[tileId]) + const currentRoom = state.topology.rooms[state.interactionLevel.roomId] + const tiles = currentRoom.tiles.map((tileId) => state.topology.tiles[tileId]) const tile = findTileWithPosition(tiles, x, y) return !(tile === null || tile.rack) diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/RoomHoverLayer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/RoomHoverLayer.js index 59f83b2b..5e351691 100644 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/RoomHoverLayer.js +++ b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/RoomHoverLayer.js @@ -35,17 +35,17 @@ function RoomHoverLayer() { const onClick = (x, y) => dispatch(toggleTileAtLocation(x, y)) const isEnabled = useSelector((state) => state.construction.currentRoomInConstruction !== '-1') const isValid = useSelector((state) => (x, y) => { - const newRoom = { ...state.objects.room[state.construction.currentRoomInConstruction] } - const oldRooms = Object.keys(state.objects.room) - .map((id) => ({ ...state.objects.room[id] })) + const newRoom = { ...state.topology.rooms[state.construction.currentRoomInConstruction] } + const oldRooms = Object.keys(state.topology.rooms) + .map((id) => ({ ...state.topology.rooms[id] })) .filter( (room) => - state.objects.topology[state.currentTopologyId].rooms.indexOf(room._id) !== -1 && + state.topology.root.rooms.indexOf(room._id) !== -1 && room._id !== state.construction.currentRoomInConstruction ) ;[...oldRooms, newRoom].forEach((room) => { - room.tiles = room.tiles.map((tileId) => state.objects.tile[tileId]) + room.tiles = room.tiles.map((tileId) => state.topology.tiles[tileId]) }) if (newRoom.tiles.length === 0) { return findPositionInRooms(oldRooms, x, y) === -1 -- cgit v1.2.3