diff options
Diffstat (limited to 'opendc-web/opendc-web-ui/src/pages')
14 files changed, 328 insertions, 184 deletions
diff --git a/opendc-web/opendc-web-ui/src/pages/404.js b/opendc-web/opendc-web-ui/src/pages/404.js new file mode 100644 index 00000000..cc9281fc --- /dev/null +++ b/opendc-web/opendc-web-ui/src/pages/404.js @@ -0,0 +1,19 @@ +import React from 'react' +import Head from 'next/head' +import TerminalWindow from '../components/not-found/TerminalWindow' +import style from './404.module.scss' + +const NotFound = () => { + return ( + <> + <Head> + <title>Page Not Found - OpenDC</title> + </Head> + <div className={style['not-found-backdrop']}> + <TerminalWindow /> + </div> + </> + ) +} + +export default NotFound diff --git a/opendc-web/opendc-web-ui/src/pages/NotFound.module.scss b/opendc-web/opendc-web-ui/src/pages/404.module.scss index e91c2780..e91c2780 100644 --- a/opendc-web/opendc-web-ui/src/pages/NotFound.module.scss +++ b/opendc-web/opendc-web-ui/src/pages/404.module.scss diff --git a/opendc-web/opendc-web-ui/src/pages/App.js b/opendc-web/opendc-web-ui/src/pages/App.js deleted file mode 100644 index ea62e8dc..00000000 --- a/opendc-web/opendc-web-ui/src/pages/App.js +++ /dev/null @@ -1,103 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useEffect } from 'react' -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 { useDocumentTitle } from '../util/hooks' - -const App = ({ projectId, portfolioId, scenarioId }) => { - const projectName = useSelector( - (state) => - state.currentProjectId !== '-1' && - state.objects.project[state.currentProjectId] && - state.objects.project[state.currentProjectId].name - ) - const topologyIsLoading = useSelector((state) => state.currentTopologyId === '-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> - ) - - useDocumentTitle(projectName ? projectName + ' - OpenDC' : 'Simulation - OpenDC') - - return ( - <HotKeys keyMap={KeymapConfiguration} className="page-container full-height"> - <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/pages/NotFound.js b/opendc-web/opendc-web-ui/src/pages/NotFound.js deleted file mode 100644 index 409ffa0e..00000000 --- a/opendc-web/opendc-web-ui/src/pages/NotFound.js +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react' -import TerminalWindow from '../components/not-found/TerminalWindow' -import style from './NotFound.module.scss' -import { useDocumentTitle } from '../util/hooks' - -const NotFound = () => { - useDocumentTitle('Page Not Found - OpenDC') - return ( - <div className={style['not-found-backdrop']}> - <TerminalWindow /> - </div> - ) -} - -export default NotFound diff --git a/opendc-web/opendc-web-ui/src/pages/Profile.js b/opendc-web/opendc-web-ui/src/pages/Profile.js deleted file mode 100644 index ea781686..00000000 --- a/opendc-web/opendc-web-ui/src/pages/Profile.js +++ /dev/null @@ -1,31 +0,0 @@ -import React from 'react' -import { useDispatch } from 'react-redux' -import { openDeleteProfileModal } from '../actions/modals/profile' -import DeleteProfileModal from '../containers/modals/DeleteProfileModal' -import AppNavbarContainer from '../containers/navigation/AppNavbarContainer' -import { useDocumentTitle } from '../util/hooks' - -const Profile = () => { - const dispatch = useDispatch() - const onDelete = () => dispatch(openDeleteProfileModal()) - - useDocumentTitle('My Profile - OpenDC') - return ( - <div className="full-height"> - <AppNavbarContainer fullWidth={false} /> - <div className="container text-page-container full-height"> - <button className="btn btn-danger mb-2 ml-auto mr-auto" style={{ maxWidth: 300 }} onClick={onDelete}> - Delete my account on OpenDC - </button> - <p className="text-muted text-center"> - This does not delete your Google account, but simply disconnects it from the OpenDC platform and - deletes any project info that is associated with you (projects you own and any authorizations you - may have on other projects). - </p> - </div> - <DeleteProfileModal /> - </div> - ) -} - -export default Profile diff --git a/opendc-web/opendc-web-ui/src/pages/Projects.js b/opendc-web/opendc-web-ui/src/pages/Projects.js deleted file mode 100644 index 5e642a03..00000000 --- a/opendc-web/opendc-web-ui/src/pages/Projects.js +++ /dev/null @@ -1,30 +0,0 @@ -import React, { useEffect } from 'react' -import { useDispatch } from 'react-redux' -import { fetchAuthorizationsOfCurrentUser } from '../actions/users' -import ProjectFilterPanel from '../components/projects/FilterPanel' -import NewProjectModal from '../containers/modals/NewProjectModal' -import NewProjectButtonContainer from '../containers/projects/NewProjectButtonContainer' -import VisibleProjectList from '../containers/projects/VisibleProjectAuthList' -import AppNavbarContainer from '../containers/navigation/AppNavbarContainer' -import { useDocumentTitle } from '../util/hooks' - -function Projects() { - const dispatch = useDispatch() - - useEffect(() => dispatch(fetchAuthorizationsOfCurrentUser())) - useDocumentTitle('My Projects - OpenDC') - - return ( - <div className="full-height"> - <AppNavbarContainer fullWidth={false} /> - <div className="container text-page-container full-height"> - <ProjectFilterPanel /> - <VisibleProjectList /> - <NewProjectButtonContainer /> - </div> - <NewProjectModal /> - </div> - ) -} - -export default Projects diff --git a/opendc-web/opendc-web-ui/src/pages/_app.js b/opendc-web/opendc-web-ui/src/pages/_app.js new file mode 100644 index 00000000..77ffa698 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/pages/_app.js @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import Head from 'next/head' +import { Provider } from 'react-redux' +import { useStore } from '../store/configure-store' +import '../index.scss' + +export default function App({ Component, pageProps }) { + const store = useStore(pageProps.initialReduxState) + + return ( + <> + <Head> + <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> + <meta name="theme-color" content="#00A6D6" /> + </Head> + <Provider store={store}> + <Component {...pageProps} /> + </Provider> + </> + ) +} diff --git a/opendc-web/opendc-web-ui/src/pages/_document.js b/opendc-web/opendc-web-ui/src/pages/_document.js new file mode 100644 index 00000000..943ae395 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/pages/_document.js @@ -0,0 +1,96 @@ +/* + * 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 Document, { Html, Head, Main, NextScript } from 'next/document' + +class OpenDCDocument extends Document { + render() { + return ( + <Html> + <Head> + <meta charSet="utf-8" /> + <meta name="theme-color" content="#00A6D6" /> + <meta + name="description" + content="Collaborative Datacenter Simulation and Exploration for Everybody" + /> + <meta name="author" content="@Large Research" /> + <meta + name="keywords" + content="OpenDC, Datacenter, Simulation, Simulator, Collaborative, Distributed, Cluster" + /> + <link rel="manifest" href="/manifest.json" /> + <link rel="shortcut icon" href="/favicon.ico" /> + + {/* Twitter Card data */} + <meta name="twitter:card" content="summary" /> + <meta name="twitter:site" content="@LargeResearch" /> + <meta name="twitter:title" content="OpenDC" /> + <meta + name="twitter:description" + content="Collaborative Datacenter Simulation and Exploration for Everybody" + /> + <meta name="twitter:creator" content="@LargeResearch" /> + <meta name="twitter:image" content="http://opendc.org/img/logo.png" /> + + {/* OpenGraph meta tags */} + <meta property="og:title" content="OpenDC" /> + <meta property="og:site_name" content="OpenDC" /> + <meta property="og:type" content="website" /> + <meta property="og:image" content="http://opendc.org/img/logo.png" /> + <meta property="og:url" content="http://opendc.org/" /> + <meta + property="og:description" + content="OpenDC provides collaborative online datacenter modeling, diverse and effective datacenter simulation, and exploratory datacenter performance feedback." + /> + <meta property="og:locale" content="en_US" /> + + {/* CDN Dependencies */} + <link + href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" + rel="stylesheet" + /> + <script async src="https://use.fontawesome.com/ece66a2e7c.js" /> + + {/* Google Analytics */} + <script async src="https://www.googletagmanager.com/gtag/js?id=UA-84285092-3" /> + <script + dangerouslySetInnerHTML={{ + __html: ` + window.dataLayer = window.dataLayer || []; + function gtag(){dataLayer.push(arguments);} + gtag('js', new Date()); + gtag('config', 'UA-84285092-3'); + `, + }} + /> + </Head> + <body> + <Main /> + <NextScript /> + </body> + </Html> + ) + } +} + +export default OpenDCDocument diff --git a/opendc-web/opendc-web-ui/src/pages/Home.js b/opendc-web/opendc-web-ui/src/pages/index.js index ee930fbe..bb904eb6 100644 --- a/opendc-web/opendc-web-ui/src/pages/Home.js +++ b/opendc-web/opendc-web-ui/src/pages/index.js @@ -1,4 +1,5 @@ import React from 'react' +import Head from 'next/head' import ContactSection from '../components/home/ContactSection' import IntroSection from '../components/home/IntroSection' import JumbotronHeader from '../components/home/JumbotronHeader' @@ -15,13 +16,14 @@ import { simulationSection, technologiesSection, teamSection, -} from './Home.module.scss' -import { useDocumentTitle } from '../util/hooks' +} from './index.module.scss' function Home() { - useDocumentTitle('OpenDC') return ( - <div> + <> + <Head> + <title>OpenDC</title> + </Head> <HomeNavbar /> <div className="body-wrapper page-container"> <JumbotronHeader /> @@ -33,7 +35,7 @@ function Home() { <TeamSection className={teamSection} /> <ContactSection /> </div> - </div> + </> ) } diff --git a/opendc-web/opendc-web-ui/src/pages/Home.module.scss b/opendc-web/opendc-web-ui/src/pages/index.module.scss index aed1d88f..aed1d88f 100644 --- a/opendc-web/opendc-web-ui/src/pages/Home.module.scss +++ b/opendc-web/opendc-web-ui/src/pages/index.module.scss diff --git a/opendc-web/opendc-web-ui/src/pages/profile.js b/opendc-web/opendc-web-ui/src/pages/profile.js new file mode 100644 index 00000000..de60037b --- /dev/null +++ b/opendc-web/opendc-web-ui/src/pages/profile.js @@ -0,0 +1,54 @@ +import React, { useState } from 'react' +import Head from 'next/head' +import { useDispatch } from 'react-redux' +import AppNavbarContainer from '../containers/navigation/AppNavbarContainer' +import ConfirmationModal from '../components/modals/ConfirmationModal' +import { deleteCurrentUser } from '../actions/users' +import { useRequireAuth } from '../auth/hook' + +const Profile = () => { + useRequireAuth() + + const dispatch = useDispatch() + + const [isDeleteModalOpen, setDeleteModalOpen] = useState(false) + const onDelete = (isConfirmed) => { + if (isConfirmed) { + dispatch(deleteCurrentUser()) + } + setDeleteModalOpen(false) + } + + return ( + <> + <Head> + <title>My Profile - OpenDC</title> + </Head> + <div className="full-height"> + <AppNavbarContainer fullWidth={false} /> + <div className="container text-page-container full-height"> + <button + className="btn btn-danger mb-2 ml-auto mr-auto" + style={{ maxWidth: 300 }} + onClick={() => setDeleteModalOpen(true)} + > + Delete my account on OpenDC + </button> + <p className="text-muted text-center"> + This does not delete your Google account, but simply disconnects it from the OpenDC platform and + deletes any project info that is associated with you (projects you own and any authorizations + you may have on other projects). + </p> + </div> + <ConfirmationModal + title="Delete my account" + message="Are you sure you want to delete your OpenDC account?" + show={isDeleteModalOpen} + callback={onDelete} + /> + </div> + </> + ) +} + +export default Profile 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 new file mode 100644 index 00000000..72316bc9 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { useRouter } from 'next/router' +import App from '../../../containers/app/App' + +function Project() { + const router = useRouter() + const { project } = router.query + + if (project) { + return <App projectId={project} /> + } + + return <div /> +} + +export default Project 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 new file mode 100644 index 00000000..76a8d23b --- /dev/null +++ b/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2021 AtLarge Research + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +import { useRouter } from 'next/router' +import App from '../../../../containers/app/App' + +function Project() { + const router = useRouter() + const { project, portfolio } = router.query + + if (project && portfolio) { + return <App projectId={project} portfolioId={portfolio} /> + } + + return <div /> +} + +export default Project diff --git a/opendc-web/opendc-web-ui/src/pages/projects/index.js b/opendc-web/opendc-web-ui/src/pages/projects/index.js new file mode 100644 index 00000000..bea9ad93 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/pages/projects/index.js @@ -0,0 +1,36 @@ +import React, { useEffect } from 'react' +import Head from 'next/head' +import { useDispatch } from 'react-redux' +import { fetchAuthorizationsOfCurrentUser } from '../../actions/users' +import ProjectFilterPanel from '../../components/projects/FilterPanel' +import NewProjectModal from '../../containers/modals/NewProjectModal' +import NewProjectButtonContainer from '../../containers/projects/NewProjectButtonContainer' +import VisibleProjectList from '../../containers/projects/VisibleProjectAuthList' +import AppNavbarContainer from '../../containers/navigation/AppNavbarContainer' +import { useRequireAuth } from '../../auth/hook' + +function Projects() { + const dispatch = useDispatch() + + useRequireAuth() + useEffect(() => dispatch(fetchAuthorizationsOfCurrentUser())) + + return ( + <> + <Head> + <title>My Projects - OpenDC</title> + </Head> + <div className="full-height"> + <AppNavbarContainer fullWidth={false} /> + <div className="container text-page-container full-height"> + <ProjectFilterPanel /> + <VisibleProjectList /> + <NewProjectButtonContainer /> + </div> + <NewProjectModal /> + </div> + </> + ) +} + +export default Projects |
