summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-ui/src/pages
diff options
context:
space:
mode:
authorFabian Mastenbroek <mail.fabianm@gmail.com>2021-07-08 16:08:02 +0200
committerGitHub <noreply@github.com>2021-07-08 16:08:02 +0200
commit1a2416043f0b877f570e89da74e0d0a4aff1d8ae (patch)
tree1bed18bb62d223be954faca87b0736d2a571b443 /opendc-web/opendc-web-ui/src/pages
parentdfd2ded56780995cec6d91af37443b710d4ddb3b (diff)
parent2c8d675c2cf140eac05988065a9d20fd2773399a (diff)
ui: Simplify data fetching in frontend
This pull request aims to simplify the data fetching logic in the OpenDC frontend. Previously, the frontend used Redux extensively to sync the server state with the client state, which introduced a lot of unnecessary complexity. With this pull request, we move most of the data fetching logic out of Redux and instead use React Query to perform the logic for fetching and caching API requests. * Move all server data except topologies outside Redux * Use React Query for fetching server data * (De)normalize topology using Normalizr * Remove current ids state from Redux * Combine fetching of project relations
Diffstat (limited to 'opendc-web/opendc-web-ui/src/pages')
-rw-r--r--opendc-web/opendc-web-ui/src/pages/_app.js23
-rw-r--r--opendc-web/opendc-web-ui/src/pages/_document.js2
-rw-r--r--opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js16
-rw-r--r--opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js39
-rw-r--r--opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js81
-rw-r--r--opendc-web/opendc-web-ui/src/pages/projects/index.js7
6 files changed, 135 insertions, 33 deletions
diff --git a/opendc-web/opendc-web-ui/src/pages/_app.js b/opendc-web/opendc-web-ui/src/pages/_app.js
index c1adbd6e..6a7200d5 100644
--- a/opendc-web/opendc-web-ui/src/pages/_app.js
+++ b/opendc-web/opendc-web-ui/src/pages/_app.js
@@ -28,15 +28,30 @@ import '../index.scss'
import { AuthProvider, useAuth } from '../auth'
import * as Sentry from '@sentry/react'
import { Integrations } from '@sentry/tracing'
+import { QueryClient, QueryClientProvider } from 'react-query'
+import { useMemo } from 'react'
+import { configureProjectClient } from '../data/project'
+import { configureExperimentClient } from '../data/experiments'
+import { configureTopologyClient } from '../data/topology'
// This setup is necessary to forward the Auth0 context to the Redux context
const Inner = ({ Component, pageProps }) => {
const auth = useAuth()
- const store = useStore(pageProps.initialReduxState, { auth })
+
+ const queryClient = useMemo(() => {
+ const client = new QueryClient()
+ configureProjectClient(client, auth)
+ configureExperimentClient(client, auth)
+ configureTopologyClient(client, auth)
+ return client
+ }, []) // eslint-disable-line react-hooks/exhaustive-deps
+ const store = useStore(pageProps.initialReduxState, { auth, queryClient })
return (
- <Provider store={store}>
- <Component {...pageProps} />
- </Provider>
+ <QueryClientProvider client={queryClient}>
+ <Provider store={store}>
+ <Component {...pageProps} />
+ </Provider>
+ </QueryClientProvider>
)
}
diff --git a/opendc-web/opendc-web-ui/src/pages/_document.js b/opendc-web/opendc-web-ui/src/pages/_document.js
index 8e4680c0..51d8d3e0 100644
--- a/opendc-web/opendc-web-ui/src/pages/_document.js
+++ b/opendc-web/opendc-web-ui/src/pages/_document.js
@@ -25,7 +25,7 @@ import Document, { Html, Head, Main, NextScript } from 'next/document'
class OpenDCDocument extends Document {
render() {
return (
- <Html>
+ <Html lang="en">
<Head>
<meta charSet="utf-8" />
<meta name="theme-color" content="#00A6D6" />
diff --git a/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js b/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js
index 72316bc9..cce887aa 100644
--- a/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js
+++ b/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js
@@ -20,18 +20,6 @@
* SOFTWARE.
*/
-import { useRouter } from 'next/router'
-import App from '../../../containers/app/App'
+import Topology from './topologies/[topology]'
-function Project() {
- const router = useRouter()
- const { project } = router.query
-
- if (project) {
- return <App projectId={project} />
- }
-
- return <div />
-}
-
-export default Project
+export default Topology
diff --git a/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js b/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js
index 76a8d23b..d3d61271 100644
--- a/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js
+++ b/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js
@@ -21,17 +21,40 @@
*/
import { useRouter } from 'next/router'
-import App from '../../../../containers/app/App'
+import Head from 'next/head'
+import AppNavbarContainer from '../../../../containers/navigation/AppNavbarContainer'
+import React from 'react'
+import { useProject } from '../../../../data/project'
+import ProjectSidebarContainer from '../../../../containers/app/sidebars/project/ProjectSidebarContainer'
+import PortfolioResultsContainer from '../../../../containers/app/results/PortfolioResultsContainer'
+import { useDispatch } from 'react-redux'
-function Project() {
+/**
+ * Page that displays the results in a portfolio.
+ */
+function Portfolio() {
const router = useRouter()
- const { project, portfolio } = router.query
+ const { project: projectId, portfolio: portfolioId } = router.query
+
+ const project = useProject(projectId)
+ const title = project?.name ? project?.name + ' - OpenDC' : 'Simulation - OpenDC'
- if (project && portfolio) {
- return <App projectId={project} portfolioId={portfolio} />
- }
+ const dispatch = useDispatch()
- return <div />
+ return (
+ <div className="page-container full-height">
+ <Head>
+ <title>{title}</title>
+ </Head>
+ <AppNavbarContainer fullWidth={true} />
+ <div className="full-height app-page-container">
+ <ProjectSidebarContainer />
+ <div className="container-fluid full-height">
+ <PortfolioResultsContainer />
+ </div>
+ </div>
+ </div>
+ )
}
-export default Project
+export default Portfolio
diff --git a/opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js b/opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js
new file mode 100644
index 00000000..a9dfdb19
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js
@@ -0,0 +1,81 @@
+/*
+ * 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 { useRouter } from 'next/router'
+import { useProject } from '../../../../data/project'
+import { useDispatch, useSelector } from 'react-redux'
+import React, { useEffect } from 'react'
+import { HotKeys } from 'react-hotkeys'
+import { KeymapConfiguration } from '../../../../hotkeys'
+import Head from 'next/head'
+import AppNavbarContainer from '../../../../containers/navigation/AppNavbarContainer'
+import LoadingScreen from '../../../../components/app/map/LoadingScreen'
+import MapStage from '../../../../containers/app/map/MapStage'
+import ScaleIndicatorContainer from '../../../../containers/app/map/controls/ScaleIndicatorContainer'
+import ToolPanelComponent from '../../../../components/app/map/controls/ToolPanelComponent'
+import ProjectSidebarContainer from '../../../../containers/app/sidebars/project/ProjectSidebarContainer'
+import TopologySidebarContainer from '../../../../containers/app/sidebars/topology/TopologySidebarContainer'
+import { openProjectSucceeded } from '../../../../redux/actions/projects'
+
+/**
+ * Page that displays a datacenter topology.
+ */
+function Topology() {
+ const router = useRouter()
+ const { project: projectId, topology: topologyId } = router.query
+
+ const { data: project } = useProject(projectId)
+ const title = project?.name ? project?.name + ' - OpenDC' : 'Simulation - OpenDC'
+
+ const dispatch = useDispatch()
+ useEffect(() => {
+ if (projectId) {
+ dispatch(openProjectSucceeded(projectId))
+ }
+ }, [projectId, topologyId, dispatch])
+
+ const topologyIsLoading = useSelector((state) => state.currentTopologyId === '-1')
+
+ return (
+ <HotKeys keyMap={KeymapConfiguration} allowChanges={true} className="page-container full-height">
+ <Head>
+ <title>{title}</title>
+ </Head>
+ <AppNavbarContainer fullWidth={true} />
+ {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>
+ )}
+ </HotKeys>
+ )
+}
+
+export default Topology
diff --git a/opendc-web/opendc-web-ui/src/pages/projects/index.js b/opendc-web/opendc-web-ui/src/pages/projects/index.js
index 958ca622..2d8e6de7 100644
--- a/opendc-web/opendc-web-ui/src/pages/projects/index.js
+++ b/opendc-web/opendc-web-ui/src/pages/projects/index.js
@@ -1,22 +1,17 @@
-import React, { useEffect, useState } from 'react'
+import React, { useState } from 'react'
import Head from 'next/head'
-import { useDispatch } from 'react-redux'
import ProjectFilterPanel from '../../components/projects/FilterPanel'
import NewProjectContainer from '../../containers/projects/NewProjectContainer'
import ProjectListContainer from '../../containers/projects/ProjectListContainer'
import AppNavbarContainer from '../../containers/navigation/AppNavbarContainer'
import { useRequireAuth } from '../../auth'
import { Container } from 'reactstrap'
-import { fetchProjects } from '../../redux/actions/projects'
function Projects() {
useRequireAuth()
- const dispatch = useDispatch()
const [filter, setFilter] = useState('SHOW_ALL')
- useEffect(() => dispatch(fetchProjects()), [dispatch])
-
return (
<>
<Head>