summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-ui/src/containers/app
diff options
context:
space:
mode:
authorFabian Mastenbroek <mail.fabianm@gmail.com>2021-05-12 22:42:12 +0200
committerFabian Mastenbroek <mail.fabianm@gmail.com>2021-05-17 17:06:47 +0200
commit4397a959e806bf476be4c81bc804616adf58b969 (patch)
treea2bfdcb314594433413a235b516d18fa5416bf6d /opendc-web/opendc-web-ui/src/containers/app
parentd21606bd238702645690586df5ad5b5075ca09c9 (diff)
ui: Migrate from CRA to Next.js
This change updates the web frontend to use Next.js instead of Create React App (CRA). Next.js enables the possibility of rendering pages on the server side (which reduces the time to first frame) and overall provides a better development experience. Future commits will try to futher optimize the implementation for Next.js.
Diffstat (limited to 'opendc-web/opendc-web-ui/src/containers/app')
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/App.js132
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js4
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js6
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js6
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js8
5 files changed, 145 insertions, 11 deletions
diff --git a/opendc-web/opendc-web-ui/src/containers/app/App.js b/opendc-web/opendc-web-ui/src/containers/app/App.js
new file mode 100644
index 00000000..df159cc2
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/app/App.js
@@ -0,0 +1,132 @@
+/*
+ * 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, { useEffect } from 'react'
+import Head from 'next/head'
+import { useRouter } from 'next/router'
+import { HotKeys } from 'react-hotkeys'
+import { useDispatch, useSelector } from 'react-redux'
+import { openPortfolioSucceeded } from '../../actions/portfolios'
+import { openProjectSucceeded } from '../../actions/projects'
+import ToolPanelComponent from '../../components/app/map/controls/ToolPanelComponent'
+import LoadingScreen from '../../components/app/map/LoadingScreen'
+import ScaleIndicatorContainer from '../../containers/app/map/controls/ScaleIndicatorContainer'
+import MapStage from '../../containers/app/map/MapStage'
+import TopologySidebarContainer from '../../containers/app/sidebars/topology/TopologySidebarContainer'
+import DeleteMachineModal from '../../containers/modals/DeleteMachineModal'
+import DeleteRackModal from '../../containers/modals/DeleteRackModal'
+import DeleteRoomModal from '../../containers/modals/DeleteRoomModal'
+import EditRackNameModal from '../../containers/modals/EditRackNameModal'
+import EditRoomNameModal from '../../containers/modals/EditRoomNameModal'
+import NewTopologyModal from '../../containers/modals/NewTopologyModal'
+import AppNavbarContainer from '../../containers/navigation/AppNavbarContainer'
+import ProjectSidebarContainer from '../../containers/app/sidebars/project/ProjectSidebarContainer'
+import { openScenarioSucceeded } from '../../actions/scenarios'
+import NewPortfolioModal from '../../containers/modals/NewPortfolioModal'
+import NewScenarioModal from '../../containers/modals/NewScenarioModal'
+import PortfolioResultsContainer from '../../containers/app/results/PortfolioResultsContainer'
+import KeymapConfiguration from '../../shortcuts/keymap'
+import { useRequireAuth } from '../../auth/hook'
+
+const App = ({ projectId, portfolioId, scenarioId }) => {
+ useRequireAuth()
+
+ const projectName = useSelector(
+ (state) =>
+ state.currentProjectId !== '-1' &&
+ state.objects.project[state.currentProjectId] &&
+ state.objects.project[state.currentProjectId].name
+ )
+ const topologyIsLoading = useSelector((state) => state.currentTopologyIdd === '-1')
+
+ const dispatch = useDispatch()
+ useEffect(() => {
+ if (scenarioId) {
+ dispatch(openScenarioSucceeded(projectId, portfolioId, scenarioId))
+ } else if (portfolioId) {
+ dispatch(openPortfolioSucceeded(projectId, portfolioId))
+ } else {
+ dispatch(openProjectSucceeded(projectId))
+ }
+ }, [projectId, portfolioId, scenarioId, dispatch])
+
+ const constructionElements = topologyIsLoading ? (
+ <div className="full-height d-flex align-items-center justify-content-center">
+ <LoadingScreen />
+ </div>
+ ) : (
+ <div className="full-height">
+ <MapStage />
+ <ScaleIndicatorContainer />
+ <ToolPanelComponent />
+ <ProjectSidebarContainer />
+ <TopologySidebarContainer />
+ </div>
+ )
+
+ const portfolioElements = (
+ <div className="full-height app-page-container">
+ <ProjectSidebarContainer />
+ <div className="container-fluid full-height">
+ <PortfolioResultsContainer />
+ </div>
+ </div>
+ )
+
+ const scenarioElements = (
+ <div className="full-height app-page-container">
+ <ProjectSidebarContainer />
+ <div className="container-fluid full-height">
+ <h2>Scenario loading</h2>
+ </div>
+ </div>
+ )
+
+ const title = projectName ? projectName + ' - OpenDC' : 'Simulation - OpenDC'
+
+ return (
+ <HotKeys keyMap={KeymapConfiguration} allowChanges={true} className="page-container full-height">
+ <Head>
+ <title>{title}</title>
+ </Head>
+ <AppNavbarContainer fullWidth={true} />
+ {scenarioId ? scenarioElements : portfolioId ? portfolioElements : constructionElements}
+ <NewTopologyModal />
+ <NewPortfolioModal />
+ <NewScenarioModal />
+ <EditRoomNameModal />
+ <DeleteRoomModal />
+ <EditRackNameModal />
+ <DeleteRackModal />
+ <DeleteMachineModal />
+ </HotKeys>
+ )
+}
+
+App.propTypes = {
+ projectId: PropTypes.string.isRequired,
+ portfolioId: PropTypes.string,
+ scenarioId: PropTypes.string,
+}
+
+export default App
diff --git a/opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js b/opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js
index 61d123e8..9394238d 100644
--- a/opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js
+++ b/opendc-web/opendc-web-ui/src/containers/app/map/MapStage.js
@@ -4,11 +4,13 @@ import { setMapDimensions, setMapPositionWithBoundsCheck, zoomInOnPosition } fro
import MapStageComponent from '../../../components/app/map/MapStageComponent'
const MapStage = () => {
- const { position, dimensions } = useSelector((state) => state.map)
+ const position = useSelector((state) => state.map.position)
+ const dimensions = useSelector((state) => state.map.dimensions)
const dispatch = useDispatch()
const zoomInOnPositionA = (zoomIn, x, y) => dispatch(zoomInOnPosition(zoomIn, x, y))
const setMapPositionWithBoundsCheckA = (x, y) => dispatch(setMapPositionWithBoundsCheck(x, y))
const setMapDimensionsA = (width, height) => dispatch(setMapDimensions(width, height))
+
return (
<MapStageComponent
mapPosition={position}
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js
index 86f465b6..ce295f03 100644
--- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js
@@ -1,6 +1,6 @@
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
-import { useHistory } from 'react-router-dom'
+import { useRouter } from 'next/router'
import PortfolioListComponent from '../../../../components/app/sidebars/project/PortfolioListComponent'
import { deletePortfolio, setCurrentPortfolio } from '../../../../actions/portfolios'
import { openNewPortfolioModal } from '../../../../actions/modals/portfolios'
@@ -24,7 +24,7 @@ const PortfolioListContainer = (props) => {
})
const dispatch = useDispatch()
- const history = useHistory()
+ const router = useRouter()
const actions = {
onNewPortfolio: () => {
dispatch(openNewPortfolioModal())
@@ -37,7 +37,7 @@ const PortfolioListContainer = (props) => {
const state = await getState(dispatch)
dispatch(deletePortfolio(id))
dispatch(setCurrentTopology(state.objects.project[state.currentProjectId].topologyIds[0]))
- history.push(`/projects/${state.currentProjectId}`)
+ router.push(`/projects/${state.currentProjectId}`)
}
},
}
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js
index 35e6c52b..06c7f0f7 100644
--- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ProjectSidebarContainer.js
@@ -1,11 +1,11 @@
import React from 'react'
-import { useLocation } from 'react-router-dom'
+import { useRouter } from 'next/router'
import ProjectSidebarComponent from '../../../../components/app/sidebars/project/ProjectSidebarComponent'
import { isCollapsible } from '../../../../util/sidebar-space'
const ProjectSidebarContainer = (props) => {
- const location = useLocation()
- return <ProjectSidebarComponent collapsible={isCollapsible(location)} {...props} />
+ const router = useRouter()
+ return <ProjectSidebarComponent collapsible={isCollapsible(router)} {...props} />
}
export default ProjectSidebarContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js
index 954284a6..e9c05f17 100644
--- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js
@@ -3,13 +3,13 @@ import { useDispatch, useSelector } from 'react-redux'
import TopologyListComponent from '../../../../components/app/sidebars/project/TopologyListComponent'
import { setCurrentTopology } from '../../../../actions/topology/building'
import { openNewTopologyModal } from '../../../../actions/modals/topology'
-import { useHistory } from 'react-router-dom'
+import { useRouter } from 'next/router'
import { getState } from '../../../../util/state-utils'
import { deleteTopology } from '../../../../actions/topologies'
const TopologyListContainer = () => {
const dispatch = useDispatch()
- const history = useHistory()
+ const router = useRouter()
const topologies = useSelector((state) => {
let topologies = state.objects.project[state.currentProjectId]
@@ -26,7 +26,7 @@ const TopologyListContainer = () => {
const onChooseTopology = async (id) => {
dispatch(setCurrentTopology(id))
const state = await getState(dispatch)
- history.push(`/projects/${state.currentProjectId}`)
+ router.push(`/projects/${state.currentProjectId}`)
}
const onNewTopology = () => {
dispatch(openNewTopologyModal())
@@ -36,7 +36,7 @@ const TopologyListContainer = () => {
const state = await getState(dispatch)
dispatch(deleteTopology(id))
dispatch(setCurrentTopology(state.objects.project[state.currentProjectId].topologyIds[0]))
- history.push(`/projects/${state.currentProjectId}`)
+ router.push(`/projects/${state.currentProjectId}`)
}
}