From 4397a959e806bf476be4c81bc804616adf58b969 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 12 May 2021 22:42:12 +0200 Subject: 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. --- .../src/containers/app/sidebars/project/PortfolioListContainer.js | 6 +++--- .../containers/app/sidebars/project/ProjectSidebarContainer.js | 6 +++--- .../src/containers/app/sidebars/project/TopologyListContainer.js | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'opendc-web/opendc-web-ui/src/containers/app/sidebars/project') 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 + const router = useRouter() + return } 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}`) } } -- cgit v1.2.3 From 1edbae1a0224e30bafb98638f419e1f967a9286f Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Thu, 13 May 2021 17:42:53 +0200 Subject: ui: Move modal state outside of Redux This change updates the frontend so that the modal state is not stored inside Redux but instead is stored using the useState hook. This simplifies the design of the modal components. --- .../app/sidebars/project/PortfolioListContainer.js | 56 ++++++++------- .../app/sidebars/project/ScenarioListContainer.js | 79 ++++++++++++++-------- .../app/sidebars/project/TopologyListContainer.js | 63 ++++++++++------- 3 files changed, 122 insertions(+), 76 deletions(-) (limited to 'opendc-web/opendc-web-ui/src/containers/app/sidebars/project') 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 ce295f03..dca7d7db 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,34 +1,23 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useState } from 'react' +import { useDispatch } from 'react-redux' 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' +import { addPortfolio, deletePortfolio, setCurrentPortfolio } from '../../../../actions/portfolios' import { getState } from '../../../../util/state-utils' import { setCurrentTopology } from '../../../../actions/topology/building' +import NewPortfolioModalComponent from '../../../../components/modals/custom-components/NewPortfolioModalComponent' +import { useActivePortfolio, useActiveProject, usePortfolios } from '../../../../store/hooks/project' -const PortfolioListContainer = (props) => { - const state = useSelector((state) => { - let portfolios = state.objects.project[state.currentProjectId] - ? state.objects.project[state.currentProjectId].portfolioIds.map((t) => state.objects.portfolio[t]) - : [] - if (portfolios.filter((t) => !t).length > 0) { - portfolios = [] - } - - return { - currentProjectId: state.currentProjectId, - currentPortfolioId: state.currentPortfolioId, - portfolios, - } - }) +const PortfolioListContainer = () => { + const currentProjectId = useActiveProject()?._id + const currentPortfolioId = useActivePortfolio()?._id + const portfolios = usePortfolios(currentProjectId) const dispatch = useDispatch() + const [isVisible, setVisible] = useState(false) const router = useRouter() const actions = { - onNewPortfolio: () => { - dispatch(openNewPortfolioModal()) - }, + onNewPortfolio: () => setVisible(true), onChoosePortfolio: (portfolioId) => { dispatch(setCurrentPortfolio(portfolioId)) }, @@ -41,7 +30,28 @@ const PortfolioListContainer = (props) => { } }, } - return + const callback = (name, targets) => { + if (name) { + dispatch( + addPortfolio({ + name, + targets, + }) + ) + } + setVisible(false) + } + return ( + <> + + + + ) } export default PortfolioListContainer diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js index 18d0735e..5d747820 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js @@ -1,28 +1,27 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useState } from 'react' +import { useDispatch } from 'react-redux' import ScenarioListComponent from '../../../../components/app/sidebars/project/ScenarioListComponent' -import { openNewScenarioModal } from '../../../../actions/modals/scenarios' -import { deleteScenario, setCurrentScenario } from '../../../../actions/scenarios' +import { addScenario, deleteScenario, setCurrentScenario } from '../../../../actions/scenarios' import { setCurrentPortfolio } from '../../../../actions/portfolios' +import NewScenarioModalComponent from '../../../../components/modals/custom-components/NewScenarioModalComponent' +import { useProjectTopologies } from '../../../../store/hooks/topology' +import { useActiveScenario, useActiveProject, useScenarios } from '../../../../store/hooks/project' +import { useSchedulers, useTraces } from '../../../../store/hooks/experiments' -const ScenarioListContainer = ({ portfolioId, children }) => { - const currentProjectId = useSelector((state) => state.currentProjectId) - const currentScenarioId = useSelector((state) => state.currentScenarioId) - const scenarios = useSelector((state) => { - let scenarios = state.objects.portfolio[portfolioId] - ? state.objects.portfolio[portfolioId].scenarioIds.map((t) => state.objects.scenario[t]) - : [] - if (scenarios.filter((t) => !t).length > 0) { - scenarios = [] - } - - return scenarios - }) +const ScenarioListContainer = ({ portfolioId }) => { + const currentProjectId = useActiveProject()?._id + const currentScenarioId = useActiveScenario()?._id + const scenarios = useScenarios(portfolioId) + const topologies = useProjectTopologies() + const traces = useTraces() + const schedulers = useSchedulers() const dispatch = useDispatch() + const [isVisible, setVisible] = useState(false) + const onNewScenario = (currentPortfolioId) => { dispatch(setCurrentPortfolio(currentPortfolioId)) - dispatch(openNewScenarioModal()) + setVisible(true) } const onChooseScenario = (portfolioId, scenarioId) => { dispatch(setCurrentScenario(portfolioId, scenarioId)) @@ -32,17 +31,43 @@ const ScenarioListContainer = ({ portfolioId, children }) => { dispatch(deleteScenario(id)) } } + const callback = (name, portfolioId, trace, topology, operational) => { + if (name) { + dispatch( + addScenario({ + portfolioId, + name, + trace, + topology, + operational, + }) + ) + } + + setVisible(false) + } return ( - + <> + + s._id)} + traces={traces} + schedulers={schedulers} + topologies={topologies} + callback={callback} + /> + ) } 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 e9c05f17..3779705a 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 @@ -1,36 +1,25 @@ -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' +import React, { useState } from 'react' +import { useDispatch } from 'react-redux' import TopologyListComponent from '../../../../components/app/sidebars/project/TopologyListComponent' import { setCurrentTopology } from '../../../../actions/topology/building' -import { openNewTopologyModal } from '../../../../actions/modals/topology' import { useRouter } from 'next/router' import { getState } from '../../../../util/state-utils' -import { deleteTopology } from '../../../../actions/topologies' +import { addTopology, deleteTopology } from '../../../../actions/topologies' +import NewTopologyModalComponent from '../../../../components/modals/custom-components/NewTopologyModalComponent' +import { useActiveTopology, useProjectTopologies } from '../../../../store/hooks/topology' const TopologyListContainer = () => { const dispatch = useDispatch() const router = useRouter() - - const topologies = useSelector((state) => { - let topologies = state.objects.project[state.currentProjectId] - ? state.objects.project[state.currentProjectId].topologyIds.map((t) => state.objects.topology[t]) - : [] - if (topologies.filter((t) => !t).length > 0) { - topologies = [] - } - - return topologies - }) - const currentTopologyId = useSelector((state) => state.currentTopologyId) + const topologies = useProjectTopologies() + const currentTopologyId = useActiveTopology()?._id + const [isVisible, setVisible] = useState(false) const onChooseTopology = async (id) => { dispatch(setCurrentTopology(id)) const state = await getState(dispatch) router.push(`/projects/${state.currentProjectId}`) } - const onNewTopology = () => { - dispatch(openNewTopologyModal()) - } const onDeleteTopology = async (id) => { if (id) { const state = await getState(dispatch) @@ -39,15 +28,37 @@ const TopologyListContainer = () => { router.push(`/projects/${state.currentProjectId}`) } } + const onCreateTopology = (name) => { + if (name) { + dispatch(addTopology(name, undefined)) + } + setVisible(false) + } + const onDuplicateTopology = (name, id) => { + if (name) { + dispatch(addTopology(name, id)) + } + setVisible(false) + } + const onCancel = () => setVisible(false) return ( - + <> + setVisible(true)} + onDeleteTopology={onDeleteTopology} + /> + + ) } -- cgit v1.2.3 From d9e65dceb38cdb8dc4e464d388755f9456620566 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Sun, 16 May 2021 17:07:58 +0200 Subject: ui: Restructure OpenDC frontend This change updates the structure of the OpenDC frontend in order to improve the maintainability of the frontend. --- .../containers/app/sidebars/project/PortfolioListContainer.js | 6 +++--- .../containers/app/sidebars/project/ScenarioListContainer.js | 10 +++++----- .../containers/app/sidebars/project/TopologyListContainer.js | 6 +++--- 3 files changed, 11 insertions(+), 11 deletions(-) (limited to 'opendc-web/opendc-web-ui/src/containers/app/sidebars/project') 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 dca7d7db..a36997ff 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 @@ -2,11 +2,11 @@ import React, { useState } from 'react' import { useDispatch } from 'react-redux' import { useRouter } from 'next/router' import PortfolioListComponent from '../../../../components/app/sidebars/project/PortfolioListComponent' -import { addPortfolio, deletePortfolio, setCurrentPortfolio } from '../../../../actions/portfolios' +import { addPortfolio, deletePortfolio, setCurrentPortfolio } from '../../../../redux/actions/portfolios' import { getState } from '../../../../util/state-utils' -import { setCurrentTopology } from '../../../../actions/topology/building' +import { setCurrentTopology } from '../../../../redux/actions/topology/building' import NewPortfolioModalComponent from '../../../../components/modals/custom-components/NewPortfolioModalComponent' -import { useActivePortfolio, useActiveProject, usePortfolios } from '../../../../store/hooks/project' +import { useActivePortfolio, useActiveProject, usePortfolios } from '../../../../data/project' const PortfolioListContainer = () => { const currentProjectId = useActiveProject()?._id diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js index 5d747820..7aaa1886 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js @@ -1,12 +1,12 @@ import React, { useState } from 'react' import { useDispatch } from 'react-redux' import ScenarioListComponent from '../../../../components/app/sidebars/project/ScenarioListComponent' -import { addScenario, deleteScenario, setCurrentScenario } from '../../../../actions/scenarios' -import { setCurrentPortfolio } from '../../../../actions/portfolios' +import { addScenario, deleteScenario, setCurrentScenario } from '../../../../redux/actions/scenarios' +import { setCurrentPortfolio } from '../../../../redux/actions/portfolios' import NewScenarioModalComponent from '../../../../components/modals/custom-components/NewScenarioModalComponent' -import { useProjectTopologies } from '../../../../store/hooks/topology' -import { useActiveScenario, useActiveProject, useScenarios } from '../../../../store/hooks/project' -import { useSchedulers, useTraces } from '../../../../store/hooks/experiments' +import { useProjectTopologies } from '../../../../data/topology' +import { useActiveScenario, useActiveProject, useScenarios } from '../../../../data/project' +import { useSchedulers, useTraces } from '../../../../data/experiments' const ScenarioListContainer = ({ portfolioId }) => { const currentProjectId = useActiveProject()?._id 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 3779705a..266ca495 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 @@ -1,12 +1,12 @@ import React, { useState } from 'react' import { useDispatch } from 'react-redux' import TopologyListComponent from '../../../../components/app/sidebars/project/TopologyListComponent' -import { setCurrentTopology } from '../../../../actions/topology/building' +import { setCurrentTopology } from '../../../../redux/actions/topology/building' import { useRouter } from 'next/router' import { getState } from '../../../../util/state-utils' -import { addTopology, deleteTopology } from '../../../../actions/topologies' +import { addTopology, deleteTopology } from '../../../../redux/actions/topologies' import NewTopologyModalComponent from '../../../../components/modals/custom-components/NewTopologyModalComponent' -import { useActiveTopology, useProjectTopologies } from '../../../../store/hooks/topology' +import { useActiveTopology, useProjectTopologies } from '../../../../data/topology' const TopologyListContainer = () => { const dispatch = useDispatch() -- cgit v1.2.3 From a6865b86cc8d710374fc0b6cfcbd2b863f1942a9 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Sun, 16 May 2021 23:18:02 +0200 Subject: ui: Migrate to Auth0 as Identity Provider This change updates the frontend codebase to move away from the Google login and instead use Auth0 as generic Identity Provider. This allows users to login with other accounts as well. Since Auth0 has a free tier, users can experiment themselves with OpenDC locally without having to pay for the login functionality. The code has been written so that we should be able to migrate away from Auth0 once it is not a suitable Identity Provider for OpenDC anymore. --- .../src/containers/app/sidebars/project/ScenarioListContainer.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'opendc-web/opendc-web-ui/src/containers/app/sidebars/project') diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js index 7aaa1886..e1be51dc 100644 --- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js +++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js @@ -60,7 +60,7 @@ const ScenarioListContainer = ({ portfolioId }) => { /> s._id)} traces={traces} schedulers={schedulers} -- cgit v1.2.3