diff options
Diffstat (limited to 'opendc-web/opendc-web-ui/src/components/app')
52 files changed, 600 insertions, 623 deletions
diff --git a/opendc-web/opendc-web-ui/src/components/app/map/LoadingScreen.js b/opendc-web/opendc-web-ui/src/components/app/map/LoadingScreen.js index 7efea9b0..ddb94990 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/LoadingScreen.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/LoadingScreen.js @@ -1,9 +1,10 @@ import React from 'react' -import FontAwesome from 'react-fontawesome' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faSpinner } from '@fortawesome/free-solid-svg-icons' const LoadingScreen = () => ( <div className="display-4"> - <FontAwesome name="refresh" className="mr-4" spin /> + <FontAwesomeIcon icon={faSpinner} spin className="mr-4" /> Loading your project... </div> ) diff --git a/opendc-web/opendc-web-ui/src/components/app/map/MapStageComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/MapStageComponent.js index 7ca10792..7c97f3e4 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/MapStageComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/MapStageComponent.js @@ -1,4 +1,4 @@ -import React from 'react' +import React, { useEffect, useRef, useState } from 'react' import { HotKeys } from 'react-hotkeys' import { Stage } from 'react-konva' import MapLayer from '../../../containers/app/map/layers/MapLayer' @@ -6,85 +6,75 @@ import ObjectHoverLayer from '../../../containers/app/map/layers/ObjectHoverLaye import RoomHoverLayer from '../../../containers/app/map/layers/RoomHoverLayer' import { NAVBAR_HEIGHT } from '../../navigation/Navbar' import { MAP_MOVE_PIXELS_PER_EVENT } from './MapConstants' -import { Provider } from 'react-redux' -import { store } from '../../../store/configure-store' +import { Provider, useStore } from 'react-redux' -class MapStageComponent extends React.Component { - state = { - mouseX: 0, - mouseY: 0, +function MapStageComponent({ + mapDimensions, + mapPosition, + setMapDimensions, + setMapPositionWithBoundsCheck, + zoomInOnPosition, +}) { + const [pos, setPos] = useState([0, 0]) + const stage = useRef(null) + const [x, y] = pos + 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), } - constructor(props) { - super(props) + const moveWithDelta = (deltaX, deltaY) => + setMapPositionWithBoundsCheck(mapPosition.x + deltaX, mapPosition.y + deltaY) + const updateMousePosition = () => { + if (!stage.current) { + return + } - this.updateDimensions = this.updateDimensions.bind(this) - this.updateScale = this.updateScale.bind(this) + const mousePos = stage.current.getStage().getPointerPosition() + setPos([mousePos.x, mousePos.y]) } + const updateDimensions = () => setMapDimensions(window.innerWidth, window.innerHeight - NAVBAR_HEIGHT) + const updateScale = (e) => zoomInOnPosition(e.deltaY < 0, x, y) - componentDidMount() { - this.updateDimensions() + useEffect(() => { + updateDimensions() - window.addEventListener('resize', this.updateDimensions) - window.addEventListener('wheel', this.updateScale) + window.addEventListener('resize', updateDimensions) + window.addEventListener('wheel', updateScale) window['exportCanvasToImage'] = () => { const download = document.createElement('a') - download.href = this.stage.getStage().toDataURL() + download.href = stage.current.getStage().toDataURL() download.download = 'opendc-canvas-export-' + Date.now() + '.png' download.click() } - } - - componentWillUnmount() { - window.removeEventListener('resize', this.updateDimensions) - window.removeEventListener('wheel', this.updateScale) - } - - updateDimensions() { - this.props.setMapDimensions(window.innerWidth, window.innerHeight - NAVBAR_HEIGHT) - } - - updateScale(e) { - this.props.zoomInOnPosition(e.deltaY < 0, this.state.mouseX, this.state.mouseY) - } - - updateMousePosition() { - const mousePos = this.stage.getStage().getPointerPosition() - this.setState({ mouseX: mousePos.x, mouseY: mousePos.y }) - } - handlers = { - MOVE_LEFT: () => this.moveWithDelta(MAP_MOVE_PIXELS_PER_EVENT, 0), - MOVE_RIGHT: () => this.moveWithDelta(-MAP_MOVE_PIXELS_PER_EVENT, 0), - MOVE_UP: () => this.moveWithDelta(0, MAP_MOVE_PIXELS_PER_EVENT), - MOVE_DOWN: () => this.moveWithDelta(0, -MAP_MOVE_PIXELS_PER_EVENT), - } + return () => { + window.removeEventListener('resize', updateDimensions) + window.removeEventListener('wheel', updateScale) + } + }, []) - moveWithDelta(deltaX, deltaY) { - this.props.setMapPositionWithBoundsCheck(this.props.mapPosition.x + deltaX, this.props.mapPosition.y + deltaY) - } + const store = useStore() - render() { - return ( - <HotKeys handlers={this.handlers}> - <Stage - ref={(stage) => { - this.stage = stage - }} - width={this.props.mapDimensions.width} - height={this.props.mapDimensions.height} - onMouseMove={this.updateMousePosition.bind(this)} - > - <Provider store={store}> - <MapLayer /> - <RoomHoverLayer mouseX={this.state.mouseX} mouseY={this.state.mouseY} /> - <ObjectHoverLayer mouseX={this.state.mouseX} mouseY={this.state.mouseY} /> - </Provider> - </Stage> - </HotKeys> - ) - } + return ( + <HotKeys handlers={handlers} allowChanges={true}> + <Stage + ref={stage} + width={mapDimensions.width} + height={mapDimensions.height} + onMouseMove={updateMousePosition} + > + <Provider store={store}> + <MapLayer /> + <RoomHoverLayer mouseX={x} mouseY={y} /> + <ObjectHoverLayer mouseX={x} mouseY={y} /> + </Provider> + </Stage> + </HotKeys> + ) } export default MapStageComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ExportCanvasComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/controls/ExportCanvasComponent.js index 8487f47b..9e8cb36a 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/controls/ExportCanvasComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/controls/ExportCanvasComponent.js @@ -1,4 +1,6 @@ import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faCamera } from '@fortawesome/free-solid-svg-icons' const ExportCanvasComponent = () => ( <button @@ -6,7 +8,7 @@ const ExportCanvasComponent = () => ( title="Export Canvas to PNG Image" onClick={() => window['exportCanvasToImage']()} > - <span className="fa fa-camera" /> + <FontAwesomeIcon icon={faCamera} /> </button> ) diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.js index 7cbb45c0..13226602 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.js @@ -1,9 +1,9 @@ import React from 'react' import { TILE_SIZE_IN_METERS, TILE_SIZE_IN_PIXELS } from '../MapConstants' -import './ScaleIndicatorComponent.sass' +import { scaleIndicator } from './ScaleIndicatorComponent.module.scss' const ScaleIndicatorComponent = ({ scale }) => ( - <div className="scale-indicator" style={{ width: TILE_SIZE_IN_PIXELS * scale }}> + <div className={scaleIndicator} style={{ width: TILE_SIZE_IN_PIXELS * scale }}> {TILE_SIZE_IN_METERS}m </div> ) diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.module.scss b/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.module.scss new file mode 100644 index 00000000..f19e0ff2 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.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/app/map/controls/ScaleIndicatorComponent.sass b/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.sass deleted file mode 100644 index 03a72c99..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/map/controls/ScaleIndicatorComponent.sass +++ /dev/null @@ -1,9 +0,0 @@ -.scale-indicator - 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/app/map/controls/ToolPanelComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.js index f372734d..d2f70953 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.js @@ -1,10 +1,10 @@ import React from 'react' import ZoomControlContainer from '../../../../containers/app/map/controls/ZoomControlContainer' import ExportCanvasComponent from './ExportCanvasComponent' -import './ToolPanelComponent.sass' +import { toolPanel } from './ToolPanelComponent.module.scss' const ToolPanelComponent = () => ( - <div className="tool-panel"> + <div className={toolPanel}> <ZoomControlContainer /> <ExportCanvasComponent /> </div> diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.module.scss b/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.module.scss new file mode 100644 index 00000000..970b1ce2 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.module.scss @@ -0,0 +1,6 @@ +.toolPanel { + position: absolute; + left: 10px; + bottom: 10px; + z-index: 50; +} diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.sass b/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.sass deleted file mode 100644 index 8b27d24a..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/map/controls/ToolPanelComponent.sass +++ /dev/null @@ -1,5 +0,0 @@ -.tool-panel - position: absolute - left: 10px - bottom: 10px - z-index: 50 diff --git a/opendc-web/opendc-web-ui/src/components/app/map/controls/ZoomControlComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/controls/ZoomControlComponent.js index 65944bea..6bae792c 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/controls/ZoomControlComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/controls/ZoomControlComponent.js @@ -1,4 +1,6 @@ import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faPlus, faMinus } from '@fortawesome/free-solid-svg-icons' const ZoomControlComponent = ({ zoomInOnCenter }) => { return ( @@ -8,14 +10,14 @@ const ZoomControlComponent = ({ zoomInOnCenter }) => { title="Zoom in" onClick={() => zoomInOnCenter(true)} > - <span className="fa fa-plus" /> + <FontAwesomeIcon icon={faPlus} /> </button> <button className="btn btn-default btn-circle btn-sm mr-1" title="Zoom out" onClick={() => zoomInOnCenter(false)} > - <span className="fa fa-minus" /> + <FontAwesomeIcon icon={faMinus} /> </button> </span> ) diff --git a/opendc-web/opendc-web-ui/src/components/app/map/elements/ImageComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/elements/ImageComponent.js index 2b5c569f..7d304b6b 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/elements/ImageComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/elements/ImageComponent.js @@ -1,48 +1,36 @@ import PropTypes from 'prop-types' -import React from 'react' +import React, { useEffect, useState } from 'react' import { Image } from 'react-konva' -class ImageComponent extends React.Component { - static imageCaches = {} - static 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, - } +const imageCaches = {} - state = { - image: null, - } +function ImageComponent({ src, x, y, width, height, opacity }) { + const [image, setImage] = useState(null) - componentDidMount() { - if (ImageComponent.imageCaches[this.props.src]) { - this.setState({ image: ImageComponent.imageCaches[this.props.src] }) + useEffect(() => { + if (imageCaches[src]) { + setImage(imageCaches[src]) return } const image = new window.Image() - image.src = this.props.src + image.src = src image.onload = () => { - this.setState({ image }) - ImageComponent.imageCaches[this.props.src] = image + setImage(image) + imageCaches[src] = image } - } + }, [src]) - render() { - return ( - <Image - image={this.state.image} - x={this.props.x} - y={this.props.y} - width={this.props.width} - height={this.props.height} - opacity={this.props.opacity} - /> - ) - } + return <Image image={image} x={x} y={y} width={width} height={height} opacity={opacity} /> +} + +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/app/map/elements/RoomTile.js b/opendc-web/opendc-web-ui/src/components/app/map/elements/RoomTile.js index 43bf918e..b2cc1273 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/elements/RoomTile.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/elements/RoomTile.js @@ -1,6 +1,6 @@ import React from 'react' import { Rect } from 'react-konva' -import Shapes from '../../../../shapes/index' +import { Tile } from '../../../../shapes' import { TILE_SIZE_IN_PIXELS } from '../MapConstants' const RoomTile = ({ tile, color }) => ( @@ -14,7 +14,7 @@ const RoomTile = ({ tile, color }) => ( ) RoomTile.propTypes = { - tile: Shapes.Tile, + tile: Tile, } export default RoomTile diff --git a/opendc-web/opendc-web-ui/src/components/app/map/elements/WallSegment.js b/opendc-web/opendc-web-ui/src/components/app/map/elements/WallSegment.js index 8aa2aebf..ad6412c3 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/elements/WallSegment.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/elements/WallSegment.js @@ -1,6 +1,6 @@ import React from 'react' import { Line } from 'react-konva' -import Shapes from '../../../../shapes/index' +import { WallSegment as WallSegmentShape } from '../../../../shapes' import { WALL_COLOR } from '../../../../util/colors' import { TILE_SIZE_IN_PIXELS, WALL_WIDTH_IN_PIXELS } from '../MapConstants' @@ -26,7 +26,7 @@ const WallSegment = ({ wallSegment }) => { } WallSegment.propTypes = { - wallSegment: Shapes.WallSegment, + wallSegment: WallSegmentShape, } export default WallSegment diff --git a/opendc-web/opendc-web-ui/src/components/app/map/groups/RackGroup.js b/opendc-web/opendc-web-ui/src/components/app/map/groups/RackGroup.js index eb6dc24a..40e28f01 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/groups/RackGroup.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/groups/RackGroup.js @@ -2,7 +2,7 @@ import React from 'react' import { Group } from 'react-konva' import RackEnergyFillContainer from '../../../../containers/app/map/RackEnergyFillContainer' import RackSpaceFillContainer from '../../../../containers/app/map/RackSpaceFillContainer' -import Shapes from '../../../../shapes/index' +import { Tile } from '../../../../shapes' import { RACK_BACKGROUND_COLOR } from '../../../../util/colors' import TileObject from '../elements/TileObject' @@ -19,7 +19,7 @@ const RackGroup = ({ tile }) => { } RackGroup.propTypes = { - tile: Shapes.Tile, + tile: Tile, } export default RackGroup diff --git a/opendc-web/opendc-web-ui/src/components/app/map/groups/RoomGroup.js b/opendc-web/opendc-web-ui/src/components/app/map/groups/RoomGroup.js index 1fd54687..d7c207ca 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/groups/RoomGroup.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/groups/RoomGroup.js @@ -3,7 +3,7 @@ import { Group } from 'react-konva' import GrayContainer from '../../../../containers/app/map/GrayContainer' import TileContainer from '../../../../containers/app/map/TileContainer' import WallContainer from '../../../../containers/app/map/WallContainer' -import Shapes from '../../../../shapes/index' +import { Room } from '../../../../shapes' const RoomGroup = ({ room, interactionLevel, currentRoomInConstruction, onClick }) => { if (currentRoomInConstruction === room._id) { @@ -42,7 +42,7 @@ const RoomGroup = ({ room, interactionLevel, currentRoomInConstruction, onClick } RoomGroup.propTypes = { - room: Shapes.Room, + room: Room, } export default RoomGroup diff --git a/opendc-web/opendc-web-ui/src/components/app/map/groups/TileGroup.js b/opendc-web/opendc-web-ui/src/components/app/map/groups/TileGroup.js index 1e106823..ff6ec7ec 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/groups/TileGroup.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/groups/TileGroup.js @@ -2,7 +2,7 @@ import PropTypes from 'prop-types' import React from 'react' import { Group } from 'react-konva' import RackContainer from '../../../../containers/app/map/RackContainer' -import Shapes from '../../../../shapes/index' +import { Tile } from '../../../../shapes' import { ROOM_DEFAULT_COLOR, ROOM_IN_CONSTRUCTION_COLOR } from '../../../../util/colors' import RoomTile from '../elements/RoomTile' @@ -28,7 +28,7 @@ const TileGroup = ({ tile, newTile, roomLoad, onClick }) => { } TileGroup.propTypes = { - tile: Shapes.Tile, + tile: Tile, newTile: PropTypes.bool, } diff --git a/opendc-web/opendc-web-ui/src/components/app/map/groups/TopologyGroup.js b/opendc-web/opendc-web-ui/src/components/app/map/groups/TopologyGroup.js index 6096fc8b..57107768 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/groups/TopologyGroup.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/groups/TopologyGroup.js @@ -2,7 +2,7 @@ import React from 'react' import { Group } from 'react-konva' import GrayContainer from '../../../../containers/app/map/GrayContainer' import RoomContainer from '../../../../containers/app/map/RoomContainer' -import Shapes from '../../../../shapes/index' +import { InteractionLevel, Topology } from '../../../../shapes' const TopologyGroup = ({ topology, interactionLevel }) => { if (!topology) { @@ -37,8 +37,8 @@ const TopologyGroup = ({ topology, interactionLevel }) => { } TopologyGroup.propTypes = { - topology: Shapes.Topology, - interactionLevel: Shapes.InteractionLevel, + topology: Topology, + interactionLevel: InteractionLevel, } export default TopologyGroup diff --git a/opendc-web/opendc-web-ui/src/components/app/map/groups/WallGroup.js b/opendc-web/opendc-web-ui/src/components/app/map/groups/WallGroup.js index 7b0f5ca0..c73a95a7 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/groups/WallGroup.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/groups/WallGroup.js @@ -1,7 +1,7 @@ import PropTypes from 'prop-types' import React from 'react' import { Group } from 'react-konva' -import Shapes from '../../../../shapes/index' +import { Tile } from '../../../../shapes' import { deriveWallLocations } from '../../../../util/tile-calculations' import WallSegment from '../elements/WallSegment' @@ -16,7 +16,7 @@ const WallGroup = ({ tiles }) => { } WallGroup.propTypes = { - tiles: PropTypes.arrayOf(Shapes.Tile).isRequired, + tiles: PropTypes.arrayOf(Tile).isRequired, } export default WallGroup diff --git a/opendc-web/opendc-web-ui/src/components/app/map/layers/HoverLayerComponent.js b/opendc-web/opendc-web-ui/src/components/app/map/layers/HoverLayerComponent.js index bead87de..08d31dac 100644 --- a/opendc-web/opendc-web-ui/src/components/app/map/layers/HoverLayerComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/map/layers/HoverLayerComponent.js @@ -1,75 +1,63 @@ import PropTypes from 'prop-types' -import React from 'react' +import React, { useEffect, useState } from 'react' import { Layer } from 'react-konva' import HoverTile from '../elements/HoverTile' import { TILE_SIZE_IN_PIXELS } from '../MapConstants' -class HoverLayerComponent extends React.Component { - static propTypes = { - mouseX: PropTypes.number.isRequired, - mouseY: PropTypes.number.isRequired, - mapPosition: PropTypes.object.isRequired, - mapScale: PropTypes.number.isRequired, - isEnabled: PropTypes.func.isRequired, - onClick: PropTypes.func.isRequired, - } - - state = { - positionX: -1, - positionY: -1, - validity: false, - } +function HoverLayerComponent({ mouseX, mouseY, mapPosition, mapScale, isEnabled, isValid, onClick, children }) { + const [pos, setPos] = useState([-1, -1]) + const [x, y] = pos + const [valid, setValid] = useState(false) - componentDidUpdate() { - if (!this.props.isEnabled()) { + useEffect(() => { + if (!isEnabled()) { return } - const positionX = Math.floor( - (this.props.mouseX - this.props.mapPosition.x) / (this.props.mapScale * TILE_SIZE_IN_PIXELS) - ) - const positionY = Math.floor( - (this.props.mouseY - this.props.mapPosition.y) / (this.props.mapScale * TILE_SIZE_IN_PIXELS) - ) + const positionX = Math.floor((mouseX - mapPosition.x) / (mapScale * TILE_SIZE_IN_PIXELS)) + const positionY = Math.floor((mouseY - mapPosition.y) / (mapScale * TILE_SIZE_IN_PIXELS)) - if (positionX !== this.state.positionX || positionY !== this.state.positionY) { - this.setState({ - positionX, - positionY, - validity: this.props.isValid(positionX, positionY), - }) + if (positionX !== x || positionY !== y) { + setPos([positionX, positionY]) + setValid(isValid(positionX, positionY)) } - } + }, [mouseX, mouseY, mapPosition, mapScale]) - render() { - if (!this.props.isEnabled()) { - return <Layer /> - } + if (!isEnabled()) { + return <Layer /> + } - const pixelX = this.props.mapScale * this.state.positionX * TILE_SIZE_IN_PIXELS + this.props.mapPosition.x - const pixelY = this.props.mapScale * this.state.positionY * TILE_SIZE_IN_PIXELS + this.props.mapPosition.y + const pixelX = mapScale * x * TILE_SIZE_IN_PIXELS + mapPosition.x + const pixelY = mapScale * y * TILE_SIZE_IN_PIXELS + mapPosition.y + + return ( + <Layer opacity={0.6}> + <HoverTile + pixelX={pixelX} + pixelY={pixelY} + scale={mapScale} + isValid={valid} + onClick={() => (valid ? onClick(x, y) : undefined)} + /> + {children + ? React.cloneElement(children, { + pixelX, + pixelY, + scale: mapScale, + }) + : undefined} + </Layer> + ) +} - return ( - <Layer opacity={0.6}> - <HoverTile - pixelX={pixelX} - pixelY={pixelY} - scale={this.props.mapScale} - isValid={this.state.validity} - onClick={() => - this.state.validity ? this.props.onClick(this.state.positionX, this.state.positionY) : undefined - } - /> - {this.props.children - ? React.cloneElement(this.props.children, { - pixelX, - pixelY, - scale: this.props.mapScale, - }) - : undefined} - </Layer> - ) - } +HoverLayerComponent.propTypes = { + mouseX: PropTypes.number.isRequired, + mouseY: PropTypes.number.isRequired, + mapPosition: PropTypes.object.isRequired, + mapScale: PropTypes.number.isRequired, + isEnabled: PropTypes.func.isRequired, + isValid: PropTypes.func.isRequired, + onClick: PropTypes.func.isRequired, } export default HoverLayerComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultsComponent.js b/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultsComponent.js index 759acd57..983a5c1d 100644 --- a/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultsComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/results/PortfolioResultsComponent.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types' import { Bar, CartesianGrid, ComposedChart, ErrorBar, ResponsiveContainer, Scatter, XAxis, YAxis } from 'recharts' import { AVAILABLE_METRICS, METRIC_NAMES_SHORT, METRIC_UNITS } from '../../../util/available-metrics' import { mean, std } from 'mathjs' -import Shapes from '../../../shapes/index' +import { Portfolio, Scenario } from '../../../shapes' import approx from 'approximate-number' const PortfolioResultsComponent = ({ portfolio, scenarios }) => { @@ -86,8 +86,8 @@ const PortfolioResultsComponent = ({ portfolio, scenarios }) => { } PortfolioResultsComponent.propTypes = { - portfolio: Shapes.Portfolio, - scenarios: PropTypes.arrayOf(Shapes.Scenario), + portfolio: Portfolio, + scenarios: PropTypes.arrayOf(Scenario), } export default PortfolioResultsComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.js index f7368f54..ccaa4144 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.js @@ -1,53 +1,47 @@ import PropTypes from 'prop-types' import classNames from 'classnames' -import React from 'react' -import './Sidebar.sass' +import React, { useState } from 'react' +import { collapseButton, collapseButtonRight, sidebar, sidebarRight } from './Sidebar.module.scss' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faAngleLeft, faAngleRight } from '@fortawesome/free-solid-svg-icons' -class Sidebar extends React.Component { - static propTypes = { - isRight: PropTypes.bool.isRequired, - collapsible: PropTypes.bool, - } +function Sidebar({ isRight, collapsible = true, children }) { + const [isCollapsed, setCollapsed] = useState(false) - static defaultProps = { - collapsible: true, - } + const button = ( + <div + className={classNames(collapseButton, { + [collapseButtonRight]: isRight, + })} + onClick={() => setCollapsed(!isCollapsed)} + > + {(isCollapsed && isRight) || (!isCollapsed && !isRight) ? ( + <FontAwesomeIcon icon={faAngleLeft} title={isRight ? 'Expand' : 'Collapse'} /> + ) : ( + <FontAwesomeIcon icon={faAngleRight} title={isRight ? 'Collapse' : 'Expand'} /> + )} + </div> + ) - state = { - collapsed: false, + if (isCollapsed) { + return button } + return ( + <div + className={classNames(`${sidebar} p-3 h-100`, { + [sidebarRight]: isRight, + })} + onWheel={(e) => e.stopPropagation()} + > + {children} + {collapsible && button} + </div> + ) +} - render() { - const collapseButton = ( - <div - className={classNames('sidebar-collapse-button', { - 'sidebar-collapse-button-right': this.props.isRight, - })} - onClick={() => this.setState({ collapsed: !this.state.collapsed })} - > - {(this.state.collapsed && this.props.isRight) || (!this.state.collapsed && !this.props.isRight) ? ( - <span className="fa fa-angle-left" title={this.props.isRight ? 'Expand' : 'Collapse'} /> - ) : ( - <span className="fa fa-angle-right" title={this.props.isRight ? 'Collapse' : 'Expand'} /> - )} - </div> - ) - - if (this.state.collapsed) { - return collapseButton - } - return ( - <div - className={classNames('sidebar p-3 h-100', { - 'sidebar-right': this.props.isRight, - })} - onWheel={(e) => e.stopPropagation()} - > - {this.props.children} - {this.props.collapsible && collapseButton} - </div> - ) - } +Sidebar.propTypes = { + isRight: PropTypes.bool.isRequired, + collapsible: PropTypes.bool, } export default Sidebar diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.module.scss b/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.module.scss new file mode 100644 index 00000000..19c6a97f --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.module.scss @@ -0,0 +1,57 @@ +@import 'src/style/_variables.scss'; +@import 'src/style/_mixins.scss'; + +.collapseButton { + position: absolute; + left: 5px; + top: 5px; + padding: 5px 7px; + + background: white; + border: solid 1px $gray-semi-light; + z-index: 99; + + @include clickable; + border-radius: 5px; + transition: background 200ms; + + &.collapseButtonRight { + left: auto; + right: 5px; + top: 5px; + } + + &:hover { + background: #eeeeee; + } +} + +.sidebar { + position: absolute; + top: 0; + left: 0; + width: $side-bar-width; + + z-index: 100; + background: white; + + border-right: $gray-semi-dark 1px solid; + + .collapseButton { + left: auto; + right: -25px; + } +} + +.sidebarRight { + left: auto; + right: 0; + + border-left: $gray-semi-dark 1px solid; + border-right: none; + + .collapseButtonRight { + left: -25px; + right: auto; + } +} diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.sass b/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.sass deleted file mode 100644 index b8e15716..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/Sidebar.sass +++ /dev/null @@ -1,50 +0,0 @@ -@import ../../../style-globals/_variables.sass -@import ../../../style-globals/_mixins.sass - -.sidebar-collapse-button - position: absolute - left: 5px - top: 5px - padding: 5px 7px - - background: white - border: solid 1px $gray-semi-light - z-index: 99 - - +clickable - +border-radius(5px) - +transition(background, 200ms) - - &.sidebar-collapse-button-right - left: auto - right: 5px - top: 5px - - &:hover - background: #eeeeee - -.sidebar - position: absolute - top: 0 - left: 0 - width: $side-bar-width - - z-index: 100 - background: white - - border-right: $gray-semi-dark 1px solid - - .sidebar-collapse-button - left: auto - right: -25px - -.sidebar-right - left: auto - right: 0 - - border-left: $gray-semi-dark 1px solid - border-right: none - - .sidebar-collapse-button-right - left: -25px - right: auto diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js index b000b9e2..9dd36d5e 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js @@ -1,66 +1,71 @@ import PropTypes from 'prop-types' import React from 'react' -import Shapes from '../../../../shapes' -import { Link } from 'react-router-dom' -import FontAwesome from 'react-fontawesome' +import { Portfolio } from '../../../../shapes' +import Link from 'next/link' import ScenarioListContainer from '../../../../containers/app/sidebars/project/ScenarioListContainer' +import { Button, Col, Row } from 'reactstrap' +import classNames from 'classnames' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faPlus, faPlay, faTrash } from '@fortawesome/free-solid-svg-icons' -class PortfolioListComponent extends React.Component { - static propTypes = { - portfolios: PropTypes.arrayOf(Shapes.Portfolio), - currentProjectId: PropTypes.string.isRequired, - currentPortfolioId: PropTypes.string, - onNewPortfolio: PropTypes.func.isRequired, - onChoosePortfolio: PropTypes.func.isRequired, - onDeletePortfolio: PropTypes.func.isRequired, - } +function PortfolioListComponent({ + portfolios, + currentProjectId, + currentPortfolioId, + onNewPortfolio, + onChoosePortfolio, + onDeletePortfolio, +}) { + return ( + <div className="pb-3"> + <h2> + Portfolios + <Button color="primary" outline className="float-right" onClick={(e) => onNewPortfolio(e)}> + <FontAwesomeIcon icon={faPlus} /> + </Button> + </h2> - onDelete(id) { - this.props.onDeletePortfolio(id) - } - - render() { - return ( - <div className="pb-3"> - <h2> - Portfolios - <button - className="btn btn-outline-primary float-right" - onClick={this.props.onNewPortfolio.bind(this)} - > - <FontAwesome name="plus" /> - </button> - </h2> + {portfolios.map((portfolio, idx) => ( + <div key={portfolio._id}> + <Row className="row mb-1"> + <Col + xs="7" + className={classNames('align-self-center', { + 'font-weight-bold': portfolio._id === currentPortfolioId, + })} + > + {portfolio.name} + </Col> + <Col xs="5" className="text-right"> + <Link href={`/projects/${currentProjectId}/portfolios/${portfolio._id}`}> + <Button + color="primary" + outline + className="mr-1" + onClick={() => onChoosePortfolio(portfolio._id)} + > + <FontAwesomeIcon icon={faPlay} /> + </Button> + </Link> + <Button color="danger" outline onClick={() => onDeletePortfolio(portfolio._id)}> + <FontAwesomeIcon icon={faTrash} /> + </Button> + </Col> + </Row> + <ScenarioListContainer portfolioId={portfolio._id} /> + </div> + ))} + </div> + ) +} - {this.props.portfolios.map((portfolio, idx) => ( - <div key={portfolio._id}> - <div className="row mb-1"> - <div - className={ - 'col-7 align-self-center ' + - (portfolio._id === this.props.currentPortfolioId ? 'font-weight-bold' : '') - } - > - {portfolio.name} - </div> - <div className="col-5 text-right"> - <Link - className="btn btn-outline-primary mr-1 fa fa-play" - to={`/projects/${this.props.currentProjectId}/portfolios/${portfolio._id}`} - onClick={() => this.props.onChoosePortfolio(portfolio._id)} - /> - <span - className="btn btn-outline-danger fa fa-trash" - onClick={() => this.onDelete(portfolio._id)} - /> - </div> - </div> - <ScenarioListContainer portfolioId={portfolio._id} /> - </div> - ))} - </div> - ) - } +PortfolioListComponent.propTypes = { + portfolios: PropTypes.arrayOf(Portfolio), + currentProjectId: PropTypes.string.isRequired, + currentPortfolioId: PropTypes.string, + onNewPortfolio: PropTypes.func.isRequired, + onChoosePortfolio: PropTypes.func.isRequired, + onDeletePortfolio: PropTypes.func.isRequired, } export default PortfolioListComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ProjectSidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ProjectSidebarComponent.js index 4789315e..7dd13663 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ProjectSidebarComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ProjectSidebarComponent.js @@ -2,13 +2,14 @@ import React from 'react' import Sidebar from '../Sidebar' import TopologyListContainer from '../../../../containers/app/sidebars/project/TopologyListContainer' import PortfolioListContainer from '../../../../containers/app/sidebars/project/PortfolioListContainer' +import { Container } from 'reactstrap' const ProjectSidebarComponent = ({ collapsible }) => ( <Sidebar isRight={false} collapsible={collapsible}> - <div className="h-100 overflow-auto container-fluid"> + <Container fluid className="h-100 overflow-auto"> <TopologyListContainer /> <PortfolioListContainer /> - </div> + </Container> </Sidebar> ) diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ScenarioListComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ScenarioListComponent.js index e775a663..131a00b5 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ScenarioListComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/ScenarioListComponent.js @@ -1,62 +1,76 @@ import PropTypes from 'prop-types' import React from 'react' -import Shapes from '../../../../shapes' -import { Link } from 'react-router-dom' -import FontAwesome from 'react-fontawesome' +import { Scenario } from '../../../../shapes' +import Link from 'next/link' +import { Button, Col, Row } from 'reactstrap' +import classNames from 'classnames' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faPlus, faPlay, faTrash } from '@fortawesome/free-solid-svg-icons' -class ScenarioListComponent extends React.Component { - static propTypes = { - scenarios: PropTypes.arrayOf(Shapes.Scenario), - portfolioId: PropTypes.string, - currentProjectId: PropTypes.string.isRequired, - currentScenarioId: PropTypes.string, - onNewScenario: PropTypes.func.isRequired, - onChooseScenario: PropTypes.func.isRequired, - onDeleteScenario: PropTypes.func.isRequired, - } - - onDelete(id) { - this.props.onDeleteScenario(id) - } - - render() { - return ( - <> - {this.props.scenarios.map((scenario, idx) => ( - <div key={scenario._id} className="row mb-1"> - <div - className={ - 'col-7 pl-5 align-self-center ' + - (scenario._id === this.props.currentScenarioId ? 'font-weight-bold' : '') - } - > - {scenario.name} - </div> - <div className="col-5 text-right"> - <Link - className="btn btn-outline-primary mr-1 fa fa-play disabled" - to={`/projects/${this.props.currentProjectId}/portfolios/${scenario.portfolioId}/scenarios/${scenario._id}`} - onClick={() => this.props.onChooseScenario(scenario.portfolioId, scenario._id)} - /> - <span - className={'btn btn-outline-danger fa fa-trash ' + (idx === 0 ? 'disabled' : '')} - onClick={() => (idx !== 0 ? this.onDelete(scenario._id) : undefined)} - /> - </div> - </div> - ))} - <div className="pl-4 mb-2"> - <div - className="btn btn-outline-primary" - onClick={() => this.props.onNewScenario(this.props.portfolioId)} +function ScenarioListComponent({ + scenarios, + portfolioId, + currentProjectId, + currentScenarioId, + onNewScenario, + onChooseScenario, + onDeleteScenario, +}) { + return ( + <> + {scenarios.map((scenario, idx) => ( + <Row key={scenario._id} className="mb-1"> + <Col + xs="7" + className={classNames('pl-5 align-self-center', { + 'font-weight-bold': scenario._id === currentScenarioId, + })} > - <FontAwesome name="plus" className="mr-1" /> - New scenario - </div> - </div> - </> - ) - } + {scenario.name} + </Col> + <Col xs="5" className="text-right"> + <Link + href={`/projects/${currentProjectId}/portfolios/${scenario.portfolioId}/scenarios/${scenario._id}`} + > + <Button + color="primary" + outline + disabled + className="mr-1" + onClick={() => onChooseScenario(scenario.portfolioId, scenario._id)} + > + <FontAwesomeIcon icon={faPlay} /> + </Button> + </Link> + <Button + color="danger" + outline + disabled={idx === 0} + onClick={() => (idx !== 0 ? onDeleteScenario(scenario._id) : undefined)} + > + <FontAwesomeIcon icon={faTrash} /> + </Button> + </Col> + </Row> + ))} + <div className="pl-4 mb-2"> + <Button color="primary" outline onClick={() => onNewScenario(portfolioId)}> + <FontAwesomeIcon icon={faPlus} className="mr-1" /> + New scenario + </Button> + </div> + </> + ) +} + +ScenarioListComponent.propTypes = { + scenarios: PropTypes.arrayOf(Scenario), + portfolioId: PropTypes.string, + currentProjectId: PropTypes.string.isRequired, + currentScenarioId: PropTypes.string, + onNewScenario: PropTypes.func.isRequired, + onChooseScenario: PropTypes.func.isRequired, + onDeleteScenario: PropTypes.func.isRequired, } export default ScenarioListComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/TopologyListComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/TopologyListComponent.js index 2f42f7e4..ac58669b 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/TopologyListComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/TopologyListComponent.js @@ -1,60 +1,56 @@ import PropTypes from 'prop-types' import React from 'react' -import Shapes from '../../../../shapes' -import FontAwesome from 'react-fontawesome' +import { Topology } from '../../../../shapes' +import { Button, Col, Row } from 'reactstrap' +import classNames from 'classnames' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faPlus, faPlay, faTrash } from '@fortawesome/free-solid-svg-icons' -class TopologyListComponent extends React.Component { - static propTypes = { - topologies: PropTypes.arrayOf(Shapes.Topology), - currentTopologyId: PropTypes.string, - onChooseTopology: PropTypes.func.isRequired, - onNewTopology: PropTypes.func.isRequired, - onDeleteTopology: PropTypes.func.isRequired, - } +function TopologyListComponent({ topologies, currentTopologyId, onChooseTopology, onNewTopology, onDeleteTopology }) { + return ( + <div className="pb-3"> + <h2> + Topologies + <Button color="primary" outline className="float-right" onClick={onNewTopology}> + <FontAwesomeIcon icon={faPlus} /> + </Button> + </h2> - onChoose(id) { - this.props.onChooseTopology(id) - } - - onDelete(id) { - this.props.onDeleteTopology(id) - } - - render() { - return ( - <div className="pb-3"> - <h2> - Topologies - <button className="btn btn-outline-primary float-right" onClick={this.props.onNewTopology}> - <FontAwesome name="plus" /> - </button> - </h2> - - {this.props.topologies.map((topology, idx) => ( - <div key={topology._id} className="row mb-1"> - <div - className={ - 'col-7 align-self-center ' + - (topology._id === this.props.currentTopologyId ? 'font-weight-bold' : '') - } + {topologies.map((topology, idx) => ( + <Row key={topology._id} className="mb-1"> + <Col + xs="7" + className={classNames('align-self-center', { + 'font-weight-bold': topology._id === currentTopologyId, + })} + > + {topology.name} + </Col> + <Col xs="5" className="text-right"> + <Button color="primary" outline className="mr-1" onClick={() => onChooseTopology(topology._id)}> + <FontAwesomeIcon icon={faPlay} /> + </Button> + <Button + color="danger" + outline + disabled={idx === 0} + onClick={() => (idx !== 0 ? onDeleteTopology(topology._id) : undefined)} > - {topology.name} - </div> - <div className="col-5 text-right"> - <span - className="btn btn-outline-primary mr-1 fa fa-play" - onClick={() => this.onChoose(topology._id)} - /> - <span - className={'btn btn-outline-danger fa fa-trash ' + (idx === 0 ? 'disabled' : '')} - onClick={() => (idx !== 0 ? this.onDelete(topology._id) : undefined)} - /> - </div> - </div> - ))} - </div> - ) - } + <FontAwesomeIcon icon={faTrash} /> + </Button> + </Col> + </Row> + ))} + </div> + ) +} + +TopologyListComponent.propTypes = { + topologies: PropTypes.arrayOf(Topology), + currentTopologyId: PropTypes.string, + onChooseTopology: PropTypes.func.isRequired, + onNewTopology: PropTypes.func.isRequired, + onDeleteTopology: PropTypes.func.isRequired, } export default TopologyListComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/NameComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/NameComponent.js index 5fb0dc55..b4cbc78f 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/NameComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/NameComponent.js @@ -1,11 +1,12 @@ import React from 'react' -import FontAwesome from 'react-fontawesome' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faPencilAlt } from '@fortawesome/free-solid-svg-icons' const NameComponent = ({ name, onEdit }) => ( <h2> {name} <button className="btn btn-outline-secondary float-right" onClick={onEdit}> - <FontAwesome name="pencil" /> + <FontAwesomeIcon icon={faPencilAlt} /> </button> </h2> ) diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js index fd552c1e..b1461743 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/building/NewRoomConstructionComponent.js @@ -1,24 +1,27 @@ import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faPlus, faCheck, faTimes } from '@fortawesome/free-solid-svg-icons' +import { Button } from 'reactstrap' const NewRoomConstructionComponent = ({ onStart, onFinish, onCancel, currentRoomInConstruction }) => { if (currentRoomInConstruction === '-1') { return ( <div className="btn btn-outline-primary btn-block" onClick={onStart}> - <span className="fa fa-plus mr-2" /> + <FontAwesomeIcon icon={faPlus} className="mr-2" /> Construct a new room </div> ) } return ( <div> - <div className="btn btn-primary btn-block" onClick={onFinish}> - <span className="fa fa-check mr-2" /> + <Button color="primary" block onClick={onFinish}> + <FontAwesomeIcon icon={faCheck} className="mr-2" /> Finalize new room - </div> - <div className="btn btn-default btn-block" onClick={onCancel}> - <span className="fa fa-times mr-2" /> + </Button> + <Button color="default" block onClick={onCancel}> + <FontAwesomeIcon icon={faTimes} className="mr-2" /> Cancel construction - </div> + </Button> </div> ) } diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/BackToRackComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/BackToRackComponent.js index 70d522b2..eac99643 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/BackToRackComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/BackToRackComponent.js @@ -1,8 +1,10 @@ import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faAngleLeft } from '@fortawesome/free-solid-svg-icons' const BackToRackComponent = ({ onClick }) => ( <div className="btn btn-secondary btn-block" onClick={onClick}> - <span className="fa fa-angle-left mr-2" /> + <FontAwesomeIcon icon={faAngleLeft} className="mr-2" /> Back to rack </div> ) diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js deleted file mode 100644 index 37820316..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/DeleteMachineComponent.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' - -const DeleteMachineComponent = ({ onClick }) => ( - <div className="btn btn-outline-danger btn-block" onClick={onClick}> - <span className="fa fa-trash mr-2" /> - Delete this machine - </div> -) - -export default DeleteMachineComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineNameComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineNameComponent.js deleted file mode 100644 index 992383c4..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/MachineNameComponent.js +++ /dev/null @@ -1,5 +0,0 @@ -import React from 'react' - -const MachineNameComponent = ({ position }) => <h2>Machine at slot {position}</h2> - -export default MachineNameComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddComponent.js index 4e9dbc7e..532add37 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitAddComponent.js @@ -1,35 +1,34 @@ import PropTypes from 'prop-types' -import React from 'react' +import React, { useRef } from 'react' +import { Button, Form, FormGroup, Input } from 'reactstrap' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faPlus } from '@fortawesome/free-solid-svg-icons' -class UnitAddComponent extends React.Component { - static propTypes = { - units: PropTypes.array.isRequired, - onAdd: PropTypes.func.isRequired, - } +function UnitAddComponent({ units, onAdd }) { + const unitSelect = useRef(null) - render() { - return ( - <div className="form-inline"> - <div className="form-group w-100"> - <select className="form-control w-70 mr-1" ref={(unitSelect) => (this.unitSelect = unitSelect)}> - {this.props.units.map((unit) => ( - <option value={unit._id} key={unit._id}> - {unit.name} - </option> - ))} - </select> - <button - type="submit" - className="btn btn-outline-primary" - onClick={() => this.props.onAdd(this.unitSelect.value)} - > - <span className="fa fa-plus mr-2" /> - Add - </button> - </div> - </div> - ) - } + return ( + <Form inline> + <FormGroup className="w-100"> + <Input type="select" className="w-70 mr-1" innerRef={unitSelect}> + {units.map((unit) => ( + <option value={unit._id} key={unit._id}> + {unit.name} + </option> + ))} + </Input> + <Button color="primary" outline onClick={() => onAdd(unitSelect.current.value)}> + <FontAwesomeIcon icon={faPlus} className="mr-2" /> + Add + </Button> + </FormGroup> + </Form> + ) +} + +UnitAddComponent.propTypes = { + units: PropTypes.array.isRequired, + onAdd: PropTypes.func.isRequired, } export default UnitAddComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitComponent.js index de55e506..aa473f91 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitComponent.js @@ -1,5 +1,7 @@ import React from 'react' import { UncontrolledPopover, PopoverHeader, PopoverBody, Button } from 'reactstrap' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faTrash, faInfoCircle } from '@fortawesome/free-solid-svg-icons' function UnitComponent({ index, unitType, unit, onDelete }) { let unitInfo @@ -37,13 +39,17 @@ function UnitComponent({ index, unitType, unit, onDelete }) { <li className="d-flex list-group-item justify-content-between align-items-center"> <span style={{ maxWidth: '60%' }}>{unit.name}</span> <span> - <Button outline={true} color="info" className="mr-1 fa fa-info-circle" id={`unit-${index}`} /> + <Button outline={true} color="info" className="mr-1" id={`unit-${index}`}> + <FontAwesomeIcon icon={faInfoCircle} /> + </Button> <UncontrolledPopover trigger="focus" placement="left" target={`unit-${index}`}> <PopoverHeader>Unit Information</PopoverHeader> <PopoverBody>{unitInfo}</PopoverBody> </UncontrolledPopover> - <span className="btn btn-outline-danger fa fa-trash" onClick={onDelete} /> + <Button outline color="danger" onClick={onDelete}> + <FontAwesomeIcon icon={faTrash} /> + </Button> </span> </li> ) diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitTabsComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitTabsComponent.js index 6599fefd..ebb5f479 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitTabsComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/machine/UnitTabsComponent.js @@ -1,5 +1,5 @@ import React, { useState } from 'react' -import { Nav, NavItem, NavLink, TabContent, TabPane } from 'reactstrap' +import { Nav, NavItem, NavLink, Row, TabContent, TabPane } from 'reactstrap' import UnitAddContainer from '../../../../../containers/app/sidebars/topology/machine/UnitAddContainer' import UnitListContainer from '../../../../../containers/app/sidebars/topology/machine/UnitListContainer' @@ -10,7 +10,7 @@ const UnitTabsComponent = () => { } return ( - <div> + <div className="mt-2"> <Nav tabs> <NavItem> <NavLink @@ -55,20 +55,28 @@ const UnitTabsComponent = () => { </Nav> <TabContent activeTab={activeTab}> <TabPane tabId="cpu-units"> - <UnitAddContainer unitType="cpu" /> - <UnitListContainer unitType="cpu" /> + <div className="py-2"> + <UnitAddContainer unitType="cpu" /> + <UnitListContainer unitType="cpu" /> + </div> </TabPane> <TabPane tabId="gpu-units"> - <UnitAddContainer unitType="gpu" /> - <UnitListContainer unitType="gpu" /> + <div className="py-2"> + <UnitAddContainer unitType="gpu" /> + <UnitListContainer unitType="gpu" /> + </div> </TabPane> <TabPane tabId="memory-units"> - <UnitAddContainer unitType="memory" /> - <UnitListContainer unitType="memory" /> + <div className="py-2"> + <UnitAddContainer unitType="memory" /> + <UnitListContainer unitType="memory" /> + </div> </TabPane> <TabPane tabId="storage-units"> - <UnitAddContainer unitType="storage" /> - <UnitListContainer unitType="storage" /> + <div className="py-2"> + <UnitAddContainer unitType="storage" /> + <UnitListContainer unitType="storage" /> + </div> </TabPane> </TabContent> </div> diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabComponent.js index 75418f9d..d0218f38 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/AddPrefabComponent.js @@ -1,10 +1,13 @@ import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faSave } from '@fortawesome/free-solid-svg-icons' +import { Button } from 'reactstrap' const AddPrefabComponent = ({ onClick }) => ( - <div className="btn btn-primary btn-block" onClick={onClick}> - <span className="fa fa-floppy-o mr-2" /> + <Button color="primary" block onClick={onClick}> + <FontAwesomeIcon icon={faSave} className="mr-2" /> Save this rack to a prefab - </div> + </Button> ) export default AddPrefabComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/BackToRoomComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/BackToRoomComponent.js index c14775bf..f6a6f79b 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/BackToRoomComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/BackToRoomComponent.js @@ -1,10 +1,13 @@ import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faAngleLeft } from '@fortawesome/free-solid-svg-icons' +import { Button } from 'reactstrap' const BackToRoomComponent = ({ onClick }) => ( - <div className="btn btn-secondary btn-block mb-2" onClick={onClick}> - <span className="fa fa-angle-left mr-2" /> + <Button color="secondary" block className="mb-2" onClick={onClick}> + <FontAwesomeIcon icon={faAngleLeft} className="mr-2" /> Back to room - </div> + </Button> ) export default BackToRoomComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/DeleteRackComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/DeleteRackComponent.js deleted file mode 100644 index 23b0daac..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/DeleteRackComponent.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' - -const DeleteRackComponent = ({ onClick }) => ( - <div className="btn btn-outline-danger btn-block" onClick={onClick}> - <span className="fa fa-trash mr-2" /> - Delete this rack - </div> -) - -export default DeleteRackComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/EmptySlotComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/EmptySlotComponent.js index d7e30f1d..d6fa9dc9 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/EmptySlotComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/EmptySlotComponent.js @@ -1,13 +1,18 @@ import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faPlus } from '@fortawesome/free-solid-svg-icons' +import { ListGroupItem, Badge, Button } from 'reactstrap' const EmptySlotComponent = ({ position, onAdd }) => ( - <li className="list-group-item d-flex justify-content-between align-items-center"> - <span className="badge badge-default badge-info mr-1 disabled">{position}</span> - <button className="btn btn-outline-primary" onClick={onAdd}> - <span className="fa fa-plus mr-2" /> + <ListGroupItem className="d-flex justify-content-between align-items-center"> + <Badge color="info" className="mr-1"> + {position} + </Badge> + <Button color="primary" outline onClick={onAdd}> + <FontAwesomeIcon icon={faPlus} className="mr-2" /> Add machine - </button> - </li> + </Button> + </ListGroupItem> ) export default EmptySlotComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineComponent.js index caa3dc04..36b15c71 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineComponent.js @@ -1,13 +1,16 @@ import React from 'react' -import Shapes from '../../../../../shapes' +import Image from 'next/image' +import { Machine } from '../../../../../shapes' +import { Badge, ListGroupItem } from 'reactstrap' const UnitIcon = ({ id, type }) => ( - <div> - <img + <div className="ml-1"> + <Image src={'/img/topology/' + id + '-icon.png'} alt={'Machine contains ' + type + ' units'} - className="img-fluid ml-1" - style={{ maxHeight: '35px' }} + layout="intrinsic" + height={35} + width={35} /> </div> ) @@ -17,27 +20,28 @@ const MachineComponent = ({ position, machine, onClick }) => { machine.cpuIds.length + machine.gpuIds.length + machine.memoryIds.length + machine.storageIds.length === 0 return ( - <li - className="d-flex list-group-item list-group-item-action justify-content-between align-items-center" + <ListGroupItem + action + className="d-flex justify-content-between align-items-center" onClick={onClick} style={{ backgroundColor: 'white' }} > - <span className="badge badge-default badge-info mr-1">{position}</span> + <Badge color="info" className="mr-1"> + {position} + </Badge> <div className="d-inline-flex"> {machine.cpuIds.length > 0 ? <UnitIcon id="cpu" type="CPU" /> : undefined} {machine.gpuIds.length > 0 ? <UnitIcon id="gpu" type="GPU" /> : undefined} {machine.memoryIds.length > 0 ? <UnitIcon id="memory" type="memory" /> : undefined} {machine.storageIds.length > 0 ? <UnitIcon id="storage" type="storage" /> : undefined} - {hasNoUnits ? ( - <span className="badge badge-default badge-warning">Machine with no units</span> - ) : undefined} + {hasNoUnits ? <Badge color="warning">Machine with no units</Badge> : undefined} </div> - </li> + </ListGroupItem> ) } MachineComponent.propTypes = { - machine: Shapes.Machine, + machine: Machine, } export default MachineComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.js index 12be26bd..1c07d237 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.js @@ -1,11 +1,11 @@ import React from 'react' import EmptySlotContainer from '../../../../../containers/app/sidebars/topology/rack/EmptySlotContainer' import MachineContainer from '../../../../../containers/app/sidebars/topology/rack/MachineContainer' -import './MachineListComponent.sass' +import { machineList } from './MachineListComponent.module.scss' const MachineListComponent = ({ machineIds }) => { return ( - <ul className="list-group machine-list"> + <ul className={`list-group ${machineList}`}> {machineIds.map((machineId, index) => { if (machineId === null) { return <EmptySlotContainer key={index} position={index + 1} /> diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.module.scss b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.module.scss new file mode 100644 index 00000000..f075aac9 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.module.scss @@ -0,0 +1,3 @@ +.machineList li { + min-height: 64px; +} diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.sass b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.sass deleted file mode 100644 index 11b82c93..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/MachineListComponent.sass +++ /dev/null @@ -1,2 +0,0 @@ -.machine-list li - min-height: 64px diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackNameComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackNameComponent.js deleted file mode 100644 index b701909a..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackNameComponent.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react' -import NameComponent from '../NameComponent' - -const RackNameComponent = ({ rackName, onEdit }) => <NameComponent name={rackName} onEdit={onEdit} /> - -export default RackNameComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.js index ca41bf57..74313bf7 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.js @@ -3,19 +3,19 @@ import BackToRoomContainer from '../../../../../containers/app/sidebars/topology import DeleteRackContainer from '../../../../../containers/app/sidebars/topology/rack/DeleteRackContainer' import MachineListContainer from '../../../../../containers/app/sidebars/topology/rack/MachineListContainer' import RackNameContainer from '../../../../../containers/app/sidebars/topology/rack/RackNameContainer' -import './RackSidebarComponent.sass' +import { sidebarContainer, sidebarHeaderContainer, machineListContainer } from './RackSidebarComponent.module.scss' import AddPrefabContainer from '../../../../../containers/app/sidebars/topology/rack/AddPrefabContainer' const RackSidebarComponent = () => { return ( - <div className="rack-sidebar-container flex-column"> - <div className="rack-sidebar-header-container"> + <div className={`${sidebarContainer} flex-column`}> + <div className={sidebarHeaderContainer}> <RackNameContainer /> <BackToRoomContainer /> <AddPrefabContainer /> <DeleteRackContainer /> </div> - <div className="machine-list-container mt-2"> + <div className={`${machineListContainer} mt-2`}> <MachineListContainer /> </div> </div> diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.module.scss b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.module.scss new file mode 100644 index 00000000..8ce3836a --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.module.scss @@ -0,0 +1,14 @@ +.sidebarContainer { + display: flex; + height: 100%; + max-height: 100%; +} + +.sidebarHeaderContainer { + flex: 0; +} + +.machineListContainer { + flex: 1; + overflow-y: scroll; +} diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.sass b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.sass deleted file mode 100644 index 29fec02a..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/rack/RackSidebarComponent.sass +++ /dev/null @@ -1,11 +0,0 @@ -.rack-sidebar-container - display: flex - height: 100% - max-height: 100% - -.rack-sidebar-header-container - flex: 0 - -.machine-list-container - flex: 1 - overflow-y: scroll diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/BackToBuildingComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/BackToBuildingComponent.js index 64c0a1f6..696b345b 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/BackToBuildingComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/BackToBuildingComponent.js @@ -1,8 +1,10 @@ import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faAngleLeft } from '@fortawesome/free-solid-svg-icons' const BackToBuildingComponent = ({ onClick }) => ( <div className="btn btn-secondary btn-block mb-2" onClick={onClick}> - <span className="fa fa-angle-left mr-2" /> + <FontAwesomeIcon icon={faAngleLeft} className="mr-2" /> Back to building </div> ) diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomComponent.js index 78417359..242f7a2b 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/DeleteRoomComponent.js @@ -1,10 +1,13 @@ import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faTrash } from '@fortawesome/free-solid-svg-icons' +import { Button } from 'reactstrap' const DeleteRoomComponent = ({ onClick }) => ( - <div className="btn btn-outline-danger btn-block" onClick={onClick}> - <span className="fa fa-trash mr-2" /> + <Button color="danger" outline block onClick={onClick}> + <FontAwesomeIcon icon={faTrash} className="mr-2" /> Delete this room - </div> + </Button> ) export default DeleteRoomComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/EditRoomComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/EditRoomComponent.js deleted file mode 100644 index 857a646f..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/EditRoomComponent.js +++ /dev/null @@ -1,22 +0,0 @@ -import classNames from 'classnames' -import React from 'react' - -const EditRoomComponent = ({ onEdit, onFinish, isEditing, isInRackConstructionMode }) => - isEditing ? ( - <div className="btn btn-info btn-block" onClick={onFinish}> - <span className="fa fa-check mr-2" /> - Finish editing room - </div> - ) : ( - <div - className={classNames('btn btn-outline-info btn-block', { - disabled: isInRackConstructionMode, - })} - onClick={() => (isInRackConstructionMode ? undefined : onEdit())} - > - <span className="fa fa-pencil mr-2" /> - Edit the tiles of this room - </div> - ) - -export default EditRoomComponent diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionComponent.js index 44566f61..19d6b309 100644 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionComponent.js +++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RackConstructionComponent.js @@ -1,26 +1,29 @@ -import classNames from 'classnames' import React from 'react' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faTimes, faPlus } from '@fortawesome/free-solid-svg-icons' +import { Button } from 'reactstrap' const RackConstructionComponent = ({ onStart, onStop, inRackConstructionMode, isEditingRoom }) => { if (inRackConstructionMode) { return ( - <div className="btn btn-primary btn-block" onClick={onStop}> - <span className="fa fa-times mr-2" /> + <Button color="primary" block onClick={onStop}> + <FontAwesomeIcon icon={faTimes} className="mr-2" /> Stop rack construction - </div> + </Button> ) } return ( - <div - className={classNames('btn btn-outline-primary btn-block', { - disabled: isEditingRoom, - })} + <Button + color="primary" + outline + block + disabled={isEditingRoom} onClick={() => (isEditingRoom ? undefined : onStart())} > - <span className="fa fa-plus mr-2" /> + <FontAwesomeIcon icon={faPlus} className="mr-2" /> Start rack construction - </div> + </Button> ) } diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomNameComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomNameComponent.js deleted file mode 100644 index d637828e..00000000 --- a/opendc-web/opendc-web-ui/src/components/app/sidebars/topology/room/RoomNameComponent.js +++ /dev/null @@ -1,6 +0,0 @@ -import React from 'react' -import NameComponent from '../NameComponent' - -const RoomNameComponent = ({ roomName, onEdit }) => <NameComponent name={roomName} onEdit={onEdit} /> - -export default RoomNameComponent |
