diff options
| author | vincent van beek <vincent@vlogic.nl> | 2026-03-26 14:02:54 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-03-26 13:02:54 +0000 |
| commit | 0ffde21b0337c606e2d0ece5bd5434a930a87dcd (patch) | |
| tree | 4fd071728a8da6dcf3e6fc9fe9dca5a492100d34 /opendc-web/opendc-web-ui/src | |
| parent | 8bb98c773bc982a0dab9cf9fb20d62f60a36a5d7 (diff) | |
Use Quarkus Quinoa for serving web UI (#391)
* refactor(web): Migrate to Quarkus 3
This commit updates the OpenDC web server to use Quarkus 3, which
changes annotations to use the Jakarta namespace for annotations.
* refactor(web): Configure runtime variables for web UI
This commit updates the web UI to propagate runtime variables via the
next-runtime-env package. Before, we would need to replace the variables
in the generated sources by Next.js, next-runtime-env works by loading a
JavaScript file when opening the OpenDC web UI which contains the
configuration of the web app.
* refactor(web): Migrate to Quarkus Quinoa
This commit updates the OpenDC web server to make use of Quarkus Quinoa
for serving the web UI. This allows us to deprecate the complex Quarkus
extension for serving the web UI.
* refactor(web): Move web UI into Quarkus web app
This commit moves the web UI into the Quarkus web server module to
ensure we follow Quarkus Quinoa's conventions.
* refactor(web): Merge Quarkus extension into single module
This commit merges the existing Quarkus extensions into a single module
to prevent build complexity.
* refactor(web): Migrate web proto to Java
This commit migrates the web protocol to Java and removes the dependency
on Jandex Gradle.
* refactor(web): Migrate to Quarkus 3
This commit updates the OpenDC web server to use Quarkus 3, which
changes annotations to use the Jakarta namespace for annotations.
* enable DB schema migration on DEV server
* webui is not needed anymore
* remove MAINTAINERS is depricated
* fix quarkus.quinoa properties
* revert properties change, install npm in docker image to allow building the frontend
* pin postgres version, this is a best practice. Fix some properties the old ones are depricated. Added properties for local testing
* fix build error
* :opendc-web:opendc-web-proto:spotlessApply
* fix database schema
---------
Co-authored-by: Fabian Mastenbroek <mail.fabianm@gmail.com>
Diffstat (limited to 'opendc-web/opendc-web-ui/src')
152 files changed, 0 insertions, 8895 deletions
diff --git a/opendc-web/opendc-web-ui/src/api/index.js b/opendc-web/opendc-web-ui/src/api/index.js deleted file mode 100644 index 3411b96e..00000000 --- a/opendc-web/opendc-web-ui/src/api/index.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 { apiUrl } from '../config' - -/** - * Send the specified request to the OpenDC API. - * - * @param auth The authentication context. - * @param path Relative path for the API. - * @param method The method to use for the request. - * @param body The body of the request. - */ -export async function request(auth, path, method = 'GET', body) { - const headers = { - 'Content-Type': 'application/json', - } - - const { getAccessTokenSilently } = auth - if (getAccessTokenSilently) { - const token = await getAccessTokenSilently() - headers['Authorization'] = `Bearer ${token}` - } - - const response = await fetch(`${apiUrl}/${path}`, { - method: method, - headers: headers, - body: body && JSON.stringify(body), - }) - const json = await response.json() - - if (!response.ok) { - throw json.message - } - - return json -} diff --git a/opendc-web/opendc-web-ui/src/api/portfolios.js b/opendc-web/opendc-web-ui/src/api/portfolios.js deleted file mode 100644 index d818876f..00000000 --- a/opendc-web/opendc-web-ui/src/api/portfolios.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 { request } from './index' - -export function fetchPortfolio(auth, projectId, number) { - return request(auth, `projects/${projectId}/portfolios/${number}`) -} - -export function fetchPortfolios(auth, projectId) { - return request(auth, `projects/${projectId}/portfolios`) -} - -export function addPortfolio(auth, projectId, portfolio) { - return request(auth, `projects/${projectId}/portfolios`, 'POST', portfolio) -} - -export function deletePortfolio(auth, projectId, number) { - return request(auth, `projects/${projectId}/portfolios/${number}`, 'DELETE') -} diff --git a/opendc-web/opendc-web-ui/src/api/projects.js b/opendc-web/opendc-web-ui/src/api/projects.js deleted file mode 100644 index e7e095da..00000000 --- a/opendc-web/opendc-web-ui/src/api/projects.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 { request } from './index' - -export function fetchProjects(auth) { - return request(auth, `projects/`) -} - -export function fetchProject(auth, projectId) { - return request(auth, `projects/${projectId}`) -} - -export function addProject(auth, project) { - return request(auth, 'projects/', 'POST', project) -} - -export function deleteProject(auth, projectId) { - return request(auth, `projects/${projectId}`, 'DELETE') -} diff --git a/opendc-web/opendc-web-ui/src/api/scenarios.js b/opendc-web/opendc-web-ui/src/api/scenarios.js deleted file mode 100644 index 7eeb8f28..00000000 --- a/opendc-web/opendc-web-ui/src/api/scenarios.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 { request } from './index' - -export function fetchScenario(auth, projectId, scenarioId) { - return request(auth, `projects/${projectId}/scenarios/${scenarioId}`) -} - -export function fetchScenariosOfPortfolio(auth, projectId, portfolioId) { - return request(auth, `projects/${projectId}/portfolios/${portfolioId}/scenarios`) -} - -export function addScenario(auth, projectId, portfolioId, scenario) { - return request(auth, `projects/${projectId}/portfolios/${portfolioId}/scenarios`, 'POST', scenario) -} - -export function deleteScenario(auth, projectId, scenarioId) { - return request(auth, `projects/${projectId}/scenarios/${scenarioId}`, 'DELETE') -} diff --git a/opendc-web/opendc-web-ui/src/api/schedulers.js b/opendc-web/opendc-web-ui/src/api/schedulers.js deleted file mode 100644 index 0b8b8153..00000000 --- a/opendc-web/opendc-web-ui/src/api/schedulers.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 { request } from './index' - -export function fetchSchedulers(auth) { - return request(auth, 'schedulers/') -} diff --git a/opendc-web/opendc-web-ui/src/api/topologies.js b/opendc-web/opendc-web-ui/src/api/topologies.js deleted file mode 100644 index 0509c6d0..00000000 --- a/opendc-web/opendc-web-ui/src/api/topologies.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 { request } from './index' - -export function fetchTopology(auth, projectId, number) { - return request(auth, `projects/${projectId}/topologies/${number}`) -} - -export function fetchTopologies(auth, projectId) { - return request(auth, `projects/${projectId}/topologies`) -} - -export function addTopology(auth, projectId, topology) { - return request(auth, `projects/${projectId}/topologies`, 'POST', topology) -} - -export function updateTopology(auth, topology) { - const { project, number, rooms } = topology - return request(auth, `projects/${project.id}/topologies/${number}`, 'PUT', { rooms }) -} - -export function deleteTopology(auth, projectId, number) { - return request(auth, `projects/${projectId}/topologies/${number}`, 'DELETE') -} diff --git a/opendc-web/opendc-web-ui/src/api/traces.js b/opendc-web/opendc-web-ui/src/api/traces.js deleted file mode 100644 index fd637ac3..00000000 --- a/opendc-web/opendc-web-ui/src/api/traces.js +++ /dev/null @@ -1,27 +0,0 @@ -/* - * 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 { request } from './index' - -export function fetchTraces(auth) { - return request(auth, 'traces/') -} diff --git a/opendc-web/opendc-web-ui/src/api/users.js b/opendc-web/opendc-web-ui/src/api/users.js deleted file mode 100644 index 12a9be05..00000000 --- a/opendc-web/opendc-web-ui/src/api/users.js +++ /dev/null @@ -1,32 +0,0 @@ -/* - * 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 { request } from './index' - -/** - * Fetch information about the user from the web server. - * - * @param auth The authentication object. - */ -export function fetchUser(auth) { - return request(auth, `users/me`) -} diff --git a/opendc-web/opendc-web-ui/src/auth.js b/opendc-web/opendc-web-ui/src/auth.js deleted file mode 100644 index 8c88f526..00000000 --- a/opendc-web/opendc-web-ui/src/auth.js +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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 { Auth0Provider, useAuth0 } from '@auth0/auth0-react' -import { useEffect } from 'react' -import { auth } from './config' - -/** - * Helper function to provide the authentication context in case Auth0 is not - * configured and the user is anonymous. - */ -function useAnonymousAuth() { - return { - isAnonymous: true, - isAuthenticated: false, - isLoading: false, - logout: () => {}, - loginWithRedirect: () => {}, - } -} - -/** - * Determine whether the auth domain is anonymous. - */ -function isAnonymousDomain(config) { - return !config.domain || config.domain === '%%NEXT_PUBLIC_AUTH0_DOMAIN%%' -} - -/** - * Force the user to be authenticated or redirect to the homepage. - */ -function useRequireAuth0() { - const auth = useAuth0() - const { loginWithRedirect, isLoading, isAuthenticated } = auth - - useEffect(() => { - if (!isLoading && !isAuthenticated) { - loginWithRedirect() - } - }, [loginWithRedirect, isLoading, isAuthenticated]) -} - -/** - * Obtain the authentication context. - */ -export const useAuth = isAnonymousDomain(auth) ? useAnonymousAuth : useAuth0 - -/** - * Force the user to be authenticated or redirect to the homepage. - */ -export const useRequireAuth = isAnonymousDomain(auth) ? () => {} : useRequireAuth0 - -/** - * AuthProvider which provides an authentication context. - */ -export function AuthProvider({ children }) { - const authConfig = auth - - if (!isAnonymousDomain(authConfig)) { - return ( - <Auth0Provider - domain={authConfig.domain} - clientId={authConfig.clientId} - redirectUri={authConfig.redirectUri} - audience={authConfig.audience} - > - {children} - </Auth0Provider> - ) - } - - return children -} - -AuthProvider.propTypes = { - children: PropTypes.node, -} diff --git a/opendc-web/opendc-web-ui/src/components/AppHeader.js b/opendc-web/opendc-web-ui/src/components/AppHeader.js deleted file mode 100644 index 514dce2a..00000000 --- a/opendc-web/opendc-web-ui/src/components/AppHeader.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 Image from 'next/image' -import PropTypes from 'prop-types' -import React from 'react' -import { - Masthead, - MastheadMain, - MastheadBrand, - MastheadContent, - Toolbar, - ToolbarContent, - ToolbarItem, -} from '@patternfly/react-core' -import Link from 'next/link' -import AppHeaderTools from './AppHeaderTools' -import AppHeaderUser from './AppHeaderUser' -import ProjectSelector from './context/ProjectSelector' - -import styles from './AppHeader.module.css' - -export default function AppHeader({ nav }) { - return ( - <Masthead id="app-header" className={styles.header}> - <MastheadMain> - <MastheadBrand className={styles.logo} component={(props) => <Link href="/projects" {...props} />}> - <Image src="/img/logo.svg" alt="OpenDC logo" width={25} height={25} /> - <span>OpenDC</span> - </MastheadBrand> - </MastheadMain> - <MastheadContent> - <Toolbar id="toolbar" isFullHeight isStatic> - <ToolbarContent> - <ToolbarItem> - <ProjectSelector /> - </ToolbarItem> - {nav && <ToolbarItem>{nav}</ToolbarItem>} - <AppHeaderTools /> - <AppHeaderUser /> - </ToolbarContent> - </Toolbar> - </MastheadContent> - </Masthead> - ) -} - -AppHeader.propTypes = { - nav: PropTypes.node, -} diff --git a/opendc-web/opendc-web-ui/src/components/AppHeader.module.css b/opendc-web/opendc-web-ui/src/components/AppHeader.module.css deleted file mode 100644 index 9d5dbed1..00000000 --- a/opendc-web/opendc-web-ui/src/components/AppHeader.module.css +++ /dev/null @@ -1,42 +0,0 @@ -/*! - * 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. - */ - -.header.header { - /* Increase precedence */ - --pf-c-masthead--m-display-inline__content--MinHeight: 3rem; - --pf-c-masthead--m-display-inline__main--MinHeight: 3rem; - - --pf-c-masthead--c-context-selector--Width: 200px; -} - -.logo > span { - margin-left: 8px; - color: #fff; - align-self: center; - font-weight: 600; - font-size: 0.9rem; -} - -.logo:hover, -.logo:focus > span { - --pf-global--link--TextDecoration: none; -} diff --git a/opendc-web/opendc-web-ui/src/components/AppHeaderTools.js b/opendc-web/opendc-web-ui/src/components/AppHeaderTools.js deleted file mode 100644 index 499bceef..00000000 --- a/opendc-web/opendc-web-ui/src/components/AppHeaderTools.js +++ /dev/null @@ -1,93 +0,0 @@ -/* - * 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 { - Button, - ButtonVariant, - Dropdown, - DropdownItem, - KebabToggle, - ToolbarGroup, - ToolbarItem, -} from '@patternfly/react-core' -import { useReducer } from 'react' -import { GithubIcon, HelpIcon } from '@patternfly/react-icons' - -function AppHeaderTools() { - const [isKebabDropdownOpen, toggleKebabDropdown] = useReducer((t) => !t, false) - const kebabDropdownItems = [ - <DropdownItem - key={0} - component={ - <a href="https://opendc.org" target="_blank" rel="noreferrer"> - <HelpIcon /> Help - </a> - } - />, - ] - - return ( - <ToolbarGroup - variant="icon-button-group" - alignment={{ default: 'alignRight' }} - spacer={{ default: 'spacerNone', md: 'spacerMd' }} - > - <ToolbarGroup variant="icon-button-group" visibility={{ default: 'hidden', lg: 'visible' }}> - <ToolbarItem> - <Button - component="a" - href="https://github.com/atlarge-research/opendc" - target="_blank" - aria-label="Source code" - variant={ButtonVariant.plain} - > - <GithubIcon /> - </Button> - </ToolbarItem> - <ToolbarItem> - <Button - component="a" - href="https://opendc.org/" - target="_blank" - aria-label="Help actions" - variant={ButtonVariant.plain} - > - <HelpIcon /> - </Button> - </ToolbarItem> - </ToolbarGroup> - <ToolbarItem visibility={{ lg: 'hidden' }}> - <Dropdown - isPlain - position="right" - toggle={<KebabToggle onToggle={toggleKebabDropdown} />} - isOpen={isKebabDropdownOpen} - dropdownItems={kebabDropdownItems} - /> - </ToolbarItem> - </ToolbarGroup> - ) -} - -AppHeaderTools.propTypes = {} - -export default AppHeaderTools diff --git a/opendc-web/opendc-web-ui/src/components/AppHeaderUser.js b/opendc-web/opendc-web-ui/src/components/AppHeaderUser.js deleted file mode 100644 index 3a73d9ba..00000000 --- a/opendc-web/opendc-web-ui/src/components/AppHeaderUser.js +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2022 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 { - Dropdown, - DropdownToggle, - Skeleton, - ToolbarItem, - DropdownItem, - DropdownGroup, - Avatar, - Progress, - ProgressSize, - DropdownSeparator, -} from '@patternfly/react-core' -import { useReducer } from 'react' -import { useAuth } from '../auth' -import useUser from '../data/user' - -export default function AppHeaderUser() { - const { logout, user, isAuthenticated, isLoading } = useAuth() - const username = isAuthenticated || isLoading ? user?.name : 'Anonymous' - const avatar = isAuthenticated || isLoading ? user?.picture : '/img/avatar.svg' - - const { data } = useUser() - const simulationBudget = data?.accounting?.simulationTimeBudget ?? 3600 - const simulationTime = data?.accounting?.simulationTime | 0 - - const [isDropdownOpen, toggleDropdown] = useReducer((t) => !t, false) - const userDropdownItems = [ - <DropdownGroup key="budget" label="Monthly Simulation Budget"> - <DropdownItem isDisabled> - <Progress - min={0} - max={simulationBudget} - value={simulationTime} - title={`${Math.ceil(simulationTime / 60)} of ${Math.ceil(simulationBudget / 60)} minutes`} - size={ProgressSize.sm} - /> - </DropdownItem> - </DropdownGroup>, - <DropdownSeparator key="separator" />, - <DropdownItem - key="group 2 logout" - isDisabled={!isAuthenticated} - onClick={() => logout({ returnTo: window.location.origin })} - > - Logout - </DropdownItem>, - ] - - const avatarComponent = avatar ? ( - <Avatar src={avatar} alt="Avatar image" size="sm" /> - ) : ( - <Skeleton className="pf-c-avatar" shape="circle" width="24px" screenreaderText="Loading avatar" /> - ) - - return ( - <ToolbarItem visibility={{ default: 'hidden', md: 'visible' }}> - <Dropdown - isFullHeight - position="right" - isOpen={isDropdownOpen} - toggle={ - <DropdownToggle onToggle={toggleDropdown} icon={avatarComponent}> - {username ?? ( - <Skeleton - fontSize="xs" - width="150px" - className="pf-u-display-inline-flex" - screenreaderText="Loading username" - /> - )} - </DropdownToggle> - } - dropdownItems={userDropdownItems} - /> - </ToolbarItem> - ) -} diff --git a/opendc-web/opendc-web-ui/src/components/AppPage.js b/opendc-web/opendc-web-ui/src/components/AppPage.js deleted file mode 100644 index 2893146e..00000000 --- a/opendc-web/opendc-web-ui/src/components/AppPage.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 AppHeader from './AppHeader' -import React from 'react' -import { Page, PageGroup, PageBreadcrumb } from '@patternfly/react-core' - -export function AppPage({ children, breadcrumb, contextSelectors }) { - return ( - <Page header={<AppHeader />}> - <PageGroup> - {contextSelectors} - {breadcrumb && <PageBreadcrumb>{breadcrumb}</PageBreadcrumb>} - </PageGroup> - {children} - </Page> - ) -} - -AppPage.propTypes = { - breadcrumb: PropTypes.node, - contextSelectors: PropTypes.node, - children: PropTypes.node, -} diff --git a/opendc-web/opendc-web-ui/src/components/context/ContextSelectionSection.js b/opendc-web/opendc-web-ui/src/components/context/ContextSelectionSection.js deleted file mode 100644 index f3c25b79..00000000 --- a/opendc-web/opendc-web-ui/src/components/context/ContextSelectionSection.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 { contextSelectionSection } from './ContextSelectionSection.module.css' - -function ContextSelectionSection({ children }) { - return <section className={contextSelectionSection}>{children}</section> -} - -ContextSelectionSection.propTypes = { - children: PropTypes.node, -} - -export default ContextSelectionSection diff --git a/opendc-web/opendc-web-ui/src/components/context/ContextSelectionSection.module.css b/opendc-web/opendc-web-ui/src/components/context/ContextSelectionSection.module.css deleted file mode 100644 index 0e902af0..00000000 --- a/opendc-web/opendc-web-ui/src/components/context/ContextSelectionSection.module.css +++ /dev/null @@ -1,28 +0,0 @@ -/*! - * 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. - */ - -.contextSelectionSection { - padding-left: var(--pf-c-page__main-breadcrumb--PaddingLeft); - flex-shrink: 0; - border-bottom: var(--pf-global--BorderWidth--sm) solid var(--pf-global--BorderColor--100); - background-color: var(--pf-c-page__main-breadcrumb--BackgroundColor); -} diff --git a/opendc-web/opendc-web-ui/src/components/context/ContextSelector.js b/opendc-web/opendc-web-ui/src/components/context/ContextSelector.js deleted file mode 100644 index d2601008..00000000 --- a/opendc-web/opendc-web-ui/src/components/context/ContextSelector.js +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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 { ContextSelector as PFContextSelector, ContextSelectorItem } from '@patternfly/react-core' -import { useMemo, useState } from 'react' - -import styles from './ContextSelector.module.css' - -function ContextSelector({ id, type = 'page', toggleText, items, onSelect, onToggle, isOpen, isFullHeight }) { - const [searchValue, setSearchValue] = useState('') - const filteredItems = useMemo( - () => items.filter(({ name }) => name.toLowerCase().indexOf(searchValue.toLowerCase()) !== -1) || items, - [items, searchValue] - ) - - return ( - <PFContextSelector - id={id} - className={type === 'page' && styles.pageSelector} - toggleText={toggleText} - onSearchInputChange={(value) => setSearchValue(value)} - searchInputValue={searchValue} - onToggle={(_, isOpen) => onToggle(isOpen)} - onSelect={(event) => { - const targetId = +event.target.value - const target = items.find((item) => item.id === targetId) - - onSelect(target) - onToggle(!isOpen) - }} - isOpen={isOpen} - isFullHeight={isFullHeight} - > - {filteredItems.map((item) => ( - <ContextSelectorItem key={item.id} value={item.id}> - {item.name} - </ContextSelectorItem> - ))} - </PFContextSelector> - ) -} - -const Item = PropTypes.shape({ - id: PropTypes.any.isRequired, - name: PropTypes.string.isRequired, -}) - -ContextSelector.propTypes = { - id: PropTypes.string, - type: PropTypes.oneOf(['app', 'page']), - items: PropTypes.arrayOf(Item).isRequired, - toggleText: PropTypes.string, - onSelect: PropTypes.func.isRequired, - onToggle: PropTypes.func.isRequired, - isOpen: PropTypes.bool, - isFullHeight: PropTypes.bool, -} - -export default ContextSelector diff --git a/opendc-web/opendc-web-ui/src/components/context/ContextSelector.module.css b/opendc-web/opendc-web-ui/src/components/context/ContextSelector.module.css deleted file mode 100644 index 7662d00c..00000000 --- a/opendc-web/opendc-web-ui/src/components/context/ContextSelector.module.css +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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. - */ - -.pageSelector.pageSelector { - /* Ensure this selector has precedence over the default one */ - margin-right: 20px; - - --pf-c-context-selector__menu--ZIndex: var(--pf-global--ZIndex--lg); - --pf-c-context-selector__toggle--PaddingTop: var(--pf-global--spacer--sm); - --pf-c-context-selector__toggle--PaddingRight: 0; - --pf-c-context-selector__toggle--PaddingBottom: var(--pf-global--spacer--sm); - --pf-c-context-selector__toggle--PaddingLeft: 0; - --pf-c-context-selector__toggle--BorderWidth: 0; - --pf-c-context-selector__toggle-text--FontSize: var(--pf-global--FontSize--sm); -} - -.pageSelector.pageSelector :global(.pf-c-context-selector__toggle):active, -.pageSelector.pageSelector :global(.pf-c-context-selector__toggle):focus-within, -.pageSelector.pageSelector :global(.pf-c-context-selector__toggle):global(.pf-m-active) { - --pf-c-context-selector__toggle--after--BorderBottomWidth: 0; -} - -.pageSelector.pageSelector:global(.pf-m-expanded) > :global(.pf-c-context-selector__toggle) { - --pf-c-context-selector__toggle--after--BorderBottomWidth: 0; -} diff --git a/opendc-web/opendc-web-ui/src/components/context/PortfolioSelector.js b/opendc-web/opendc-web-ui/src/components/context/PortfolioSelector.js deleted file mode 100644 index e401e6fc..00000000 --- a/opendc-web/opendc-web-ui/src/components/context/PortfolioSelector.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 { useState } from 'react' -import { usePortfolios } from '../../data/project' -import { Portfolio } from '../../shapes' -import ContextSelector from './ContextSelector' - -function PortfolioSelector({ activePortfolio }) { - const router = useRouter() - - const [isOpen, setOpen] = useState(false) - const { data: portfolios = [] } = usePortfolios(activePortfolio?.project?.id, { enabled: isOpen }) - - return ( - <ContextSelector - id="portfolio" - toggleText={activePortfolio ? `Portfolio: ${activePortfolio.name}` : 'Select portfolio'} - activeItem={activePortfolio} - items={portfolios} - onSelect={(portfolio) => router.push(`/projects/${portfolio.project.id}/portfolios/${portfolio.number}`)} - onToggle={setOpen} - isOpen={isOpen} - /> - ) -} - -PortfolioSelector.propTypes = { - activePortfolio: Portfolio, -} - -export default PortfolioSelector diff --git a/opendc-web/opendc-web-ui/src/components/context/ProjectSelector.js b/opendc-web/opendc-web-ui/src/components/context/ProjectSelector.js deleted file mode 100644 index f2791b38..00000000 --- a/opendc-web/opendc-web-ui/src/components/context/ProjectSelector.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - * 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 { useState } from 'react' -import { useProjects, useProject } from '../../data/project' -import { Project } from '../../shapes' -import ContextSelector from './ContextSelector' - -function ProjectSelector() { - const router = useRouter() - const projectId = +router.query['project'] - - const [isOpen, setOpen] = useState(false) - const { data: activeProject } = useProject(+projectId) - const { data: projects = [] } = useProjects({ enabled: isOpen }) - - return ( - <ContextSelector - id="project" - type="app" - toggleText={activeProject ? activeProject.name : 'Select project'} - items={projects} - onSelect={(project) => router.push(`/projects/${project.id}`)} - onToggle={setOpen} - isOpen={isOpen} - isFullHeight - /> - ) -} - -ProjectSelector.propTypes = { - activeProject: Project, -} - -export default ProjectSelector diff --git a/opendc-web/opendc-web-ui/src/components/context/TopologySelector.js b/opendc-web/opendc-web-ui/src/components/context/TopologySelector.js deleted file mode 100644 index 355d9f4b..00000000 --- a/opendc-web/opendc-web-ui/src/components/context/TopologySelector.js +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 { useState } from 'react' -import { useTopologies } from '../../data/topology' -import { Topology } from '../../shapes' -import ContextSelector from './ContextSelector' - -function TopologySelector({ activeTopology }) { - const router = useRouter() - - const [isOpen, setOpen] = useState(false) - const { data: topologies = [] } = useTopologies(activeTopology?.project?.id, { enabled: isOpen }) - - return ( - <ContextSelector - id="topology" - toggleText={activeTopology ? `Topology: ${activeTopology.name}` : 'Select topology'} - activeItem={activeTopology} - items={topologies} - onSelect={(topology) => router.push(`/projects/${topology.project.id}/topologies/${topology.number}`)} - onToggle={setOpen} - isOpen={isOpen} - /> - ) -} - -TopologySelector.propTypes = { - activeTopology: Topology, -} - -export default TopologySelector diff --git a/opendc-web/opendc-web-ui/src/components/portfolios/NewScenario.js b/opendc-web/opendc-web-ui/src/components/portfolios/NewScenario.js deleted file mode 100644 index fd9a72d2..00000000 --- a/opendc-web/opendc-web-ui/src/components/portfolios/NewScenario.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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 { PlusIcon } from '@patternfly/react-icons' -import { Button } from '@patternfly/react-core' -import { useState } from 'react' -import { useNewScenario } from '../../data/project' -import NewScenarioModal from './NewScenarioModal' - -function NewScenario({ projectId, portfolioId }) { - const [isVisible, setVisible] = useState(false) - const { mutate: addScenario } = useNewScenario() - - const onSubmit = (projectId, portfolioNumber, data) => { - addScenario({ projectId, portfolioNumber, data }) - setVisible(false) - } - - return ( - <> - <Button icon={<PlusIcon />} isSmall onClick={() => setVisible(true)}> - New Scenario - </Button> - <NewScenarioModal - projectId={projectId} - portfolioId={portfolioId} - isOpen={isVisible} - onSubmit={onSubmit} - onCancel={() => setVisible(false)} - /> - </> - ) -} - -NewScenario.propTypes = { - projectId: PropTypes.number, - portfolioId: PropTypes.number, -} - -export default NewScenario diff --git a/opendc-web/opendc-web-ui/src/components/portfolios/NewScenarioModal.js b/opendc-web/opendc-web-ui/src/components/portfolios/NewScenarioModal.js deleted file mode 100644 index ed35c163..00000000 --- a/opendc-web/opendc-web-ui/src/components/portfolios/NewScenarioModal.js +++ /dev/null @@ -1,157 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useRef, useState } from 'react' -import Modal from '../util/modals/Modal' -import { - Checkbox, - Form, - FormGroup, - FormSection, - FormSelect, - FormSelectOption, - NumberInput, - TextInput, -} from '@patternfly/react-core' -import { useSchedulers, useTraces } from '../../data/experiments' -import { useTopologies } from '../../data/topology' -import { usePortfolio } from '../../data/project' - -function NewScenarioModal({ projectId, portfolioId, isOpen, onSubmit: onSubmitUpstream, onCancel: onCancelUpstream }) { - const { data: portfolio } = usePortfolio(projectId, portfolioId) - const { data: topologies = [] } = useTopologies(projectId, { enabled: isOpen }) - const { data: traces = [] } = useTraces({ enabled: isOpen }) - const { data: schedulers = [] } = useSchedulers({ enabled: isOpen }) - - // eslint-disable-next-line no-unused-vars - const [isSubmitted, setSubmitted] = useState(false) - const [traceLoad, setTraceLoad] = useState(100) - const [trace, setTrace] = useState(undefined) - const [topology, setTopology] = useState(undefined) - const [scheduler, setScheduler] = useState(undefined) - const [failuresEnabled, setFailuresEnabled] = useState(false) - const [opPhenEnabled, setOpPhenEnabled] = useState(false) - const nameInput = useRef(null) - - const resetState = () => { - setSubmitted(false) - setTraceLoad(100) - setTrace(undefined) - setTopology(undefined) - setScheduler(undefined) - setFailuresEnabled(false) - setOpPhenEnabled(false) - nameInput.current.value = '' - } - - const onSubmit = (event) => { - setSubmitted(true) - - if (event) { - event.preventDefault() - } - - const name = nameInput.current.value - - onSubmitUpstream(portfolio.project.id, portfolio.number, { - name, - workload: { - trace: trace || traces[0].id, - samplingFraction: traceLoad / 100, - }, - topology: topology || topologies[0].number, - phenomena: { - failures: failuresEnabled, - interference: opPhenEnabled, - }, - schedulerName: scheduler || schedulers[0], - }) - - resetState() - return true - } - const onCancel = () => { - onCancelUpstream() - resetState() - } - - return ( - <Modal title="New Scenario" isOpen={isOpen} onSubmit={onSubmit} onCancel={onCancel}> - <Form onSubmit={onSubmit}> - <FormGroup label="Name" fieldId="name" isRequired> - <TextInput - id="name" - name="name" - type="text" - isDisabled={portfolio?.scenarios?.length === 0} - defaultValue={portfolio?.scenarios?.length === 0 ? 'Base scenario' : ''} - ref={nameInput} - /> - </FormGroup> - <FormSection title="Workload"> - <FormGroup label="Trace" fieldId="trace" isRequired> - <FormSelect id="trace" name="trace" value={trace} onChange={setTrace}> - {traces.map((trace) => ( - <FormSelectOption value={trace.id} key={trace.id} label={trace.name} /> - ))} - </FormSelect> - </FormGroup> - <FormGroup label="Load Sampling Fraction" fieldId="trace-load" isRequired> - <NumberInput - name="trace-load" - type="number" - min={0} - max={100} - value={traceLoad} - onMinus={() => setTraceLoad((load) => load - 1)} - onPlus={() => setTraceLoad((load) => load + 1)} - onChange={(e) => setTraceLoad(Number(e.target.value))} - unit="%" - /> - </FormGroup> - </FormSection> - <FormSection title="Topology"> - <FormGroup label="Topology" fieldId="topology" isRequired> - <FormSelect id="topology" name="topology" value={topology} onChange={setTopology}> - {topologies.map((topology) => ( - <FormSelectOption value={topology.number} key={topology.number} label={topology.name} /> - ))} - </FormSelect> - </FormGroup> - - <FormGroup label="Scheduler" fieldId="scheduler" isRequired> - <FormSelect id="scheduler" name="scheduler" value={scheduler} onChange={setScheduler}> - {schedulers.map((scheduler) => ( - <FormSelectOption value={scheduler} key={scheduler} label={scheduler} /> - ))} - </FormSelect> - </FormGroup> - </FormSection> - <FormSection title="Operational Phenomena"> - <Checkbox - label="Failures" - id="failures" - name="failures" - isChecked={failuresEnabled} - onChange={() => setFailuresEnabled((e) => !e)} - /> - <Checkbox - label="Performance Interference" - id="perf-interference" - name="perf-interference" - isChecked={opPhenEnabled} - onChange={() => setOpPhenEnabled((e) => !e)} - /> - </FormSection> - </Form> - </Modal> - ) -} - -NewScenarioModal.propTypes = { - projectId: PropTypes.number, - portfolioId: PropTypes.number, - isOpen: PropTypes.bool.isRequired, - onSubmit: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired, -} - -export default NewScenarioModal diff --git a/opendc-web/opendc-web-ui/src/components/portfolios/PortfolioOverview.js b/opendc-web/opendc-web-ui/src/components/portfolios/PortfolioOverview.js deleted file mode 100644 index e561b655..00000000 --- a/opendc-web/opendc-web-ui/src/components/portfolios/PortfolioOverview.js +++ /dev/null @@ -1,120 +0,0 @@ -/* - * 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 { - Card, - CardActions, - CardBody, - CardHeader, - CardTitle, - Chip, - ChipGroup, - DescriptionList, - DescriptionListDescription, - DescriptionListGroup, - DescriptionListTerm, - Grid, - GridItem, - Skeleton, -} from '@patternfly/react-core' -import React from 'react' -import { usePortfolio } from '../../data/project' -import { METRIC_NAMES } from '../../util/available-metrics' -import NewScenario from './NewScenario' -import ScenarioTable from './ScenarioTable' - -function PortfolioOverview({ projectId, portfolioId }) { - const { status, data: portfolio } = usePortfolio(projectId, portfolioId) - - return ( - <Grid hasGutter> - <GridItem md={2}> - <Card> - <CardTitle>Details</CardTitle> - <CardBody> - <DescriptionList> - <DescriptionListGroup> - <DescriptionListTerm>Name</DescriptionListTerm> - <DescriptionListDescription> - {portfolio?.name ?? <Skeleton screenreaderText="Loading portfolio" />} - </DescriptionListDescription> - </DescriptionListGroup> - <DescriptionListGroup> - <DescriptionListTerm>Scenarios</DescriptionListTerm> - <DescriptionListDescription> - {portfolio?.scenarios?.length ?? <Skeleton screenreaderText="Loading portfolio" />} - </DescriptionListDescription> - </DescriptionListGroup> - <DescriptionListGroup> - <DescriptionListTerm>Metrics</DescriptionListTerm> - <DescriptionListDescription> - {portfolio ? ( - portfolio.targets.metrics.length > 0 ? ( - <ChipGroup> - {portfolio.targets.metrics.map((metric) => ( - <Chip isReadOnly key={metric}> - {METRIC_NAMES[metric]} - </Chip> - ))} - </ChipGroup> - ) : ( - 'No metrics enabled' - ) - ) : ( - <Skeleton screenreaderText="Loading portfolio" /> - )} - </DescriptionListDescription> - </DescriptionListGroup> - <DescriptionListGroup> - <DescriptionListTerm>Repeats per Scenario</DescriptionListTerm> - <DescriptionListDescription> - {portfolio?.targets?.repeats ?? <Skeleton screenreaderText="Loading portfolio" />} - </DescriptionListDescription> - </DescriptionListGroup> - </DescriptionList> - </CardBody> - </Card> - </GridItem> - <GridItem md={6}> - <Card> - <CardHeader> - <CardActions> - <NewScenario projectId={projectId} portfolioId={portfolioId} /> - </CardActions> - <CardTitle>Scenarios</CardTitle> - </CardHeader> - <CardBody> - <ScenarioTable portfolio={portfolio} status={status} /> - </CardBody> - </Card> - </GridItem> - </Grid> - ) -} - -PortfolioOverview.propTypes = { - projectId: PropTypes.number, - portfolioId: PropTypes.number, -} - -export default PortfolioOverview diff --git a/opendc-web/opendc-web-ui/src/components/portfolios/PortfolioResultInfo.js b/opendc-web/opendc-web-ui/src/components/portfolios/PortfolioResultInfo.js deleted file mode 100644 index dbfa928f..00000000 --- a/opendc-web/opendc-web-ui/src/components/portfolios/PortfolioResultInfo.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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 { Tooltip } from '@patternfly/react-core' -import { OutlinedQuestionCircleIcon } from '@patternfly/react-icons' -import { METRIC_DESCRIPTIONS } from '../../util/available-metrics' - -function PortfolioResultInfo({ metric }) { - return ( - <Tooltip position="top" content={<div>{METRIC_DESCRIPTIONS[metric]}</div>}> - <OutlinedQuestionCircleIcon title="Metric information" /> - </Tooltip> - ) -} - -PortfolioResultInfo.propTypes = { - metric: PropTypes.string.isRequired, -} - -export default PortfolioResultInfo diff --git a/opendc-web/opendc-web-ui/src/components/portfolios/PortfolioResults.js b/opendc-web/opendc-web-ui/src/components/portfolios/PortfolioResults.js deleted file mode 100644 index 62150fa7..00000000 --- a/opendc-web/opendc-web-ui/src/components/portfolios/PortfolioResults.js +++ /dev/null @@ -1,180 +0,0 @@ -/* - * 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 { mean, std } from 'mathjs' -import React, { useMemo } from 'react' -import PropTypes from 'prop-types' -import { VictoryErrorBar } from 'victory-errorbar' -import { METRIC_NAMES, METRIC_UNITS, AVAILABLE_METRICS } from '../../util/available-metrics' -import { - Bullseye, - Card, - CardActions, - CardBody, - CardHeader, - CardTitle, - EmptyState, - EmptyStateBody, - EmptyStateIcon, - Grid, - GridItem, - Spinner, - Title, -} from '@patternfly/react-core' -import { Chart, ChartAxis, ChartBar, ChartTooltip } from '@patternfly/react-charts' -import { ErrorCircleOIcon, CubesIcon } from '@patternfly/react-icons' -import { usePortfolio } from '../../data/project' -import PortfolioResultInfo from './PortfolioResultInfo' -import NewScenario from './NewScenario' - -function PortfolioResults({ projectId, portfolioId }) { - const { status, data: portfolio } = usePortfolio(projectId, portfolioId) - const scenarios = useMemo(() => portfolio?.scenarios ?? [], [portfolio]) - - const label = ({ datum }) => - `${datum.x}: ${datum.y.toLocaleString()} ± ${datum.errorY.toLocaleString()} ${METRIC_UNITS[datum.metric]}` - const selectedMetrics = new Set(portfolio?.targets?.metrics ?? []) - const dataPerMetric = useMemo(() => { - const dataPerMetric = {} - AVAILABLE_METRICS.forEach((metric) => { - dataPerMetric[metric] = scenarios - .filter((scenario) => scenario.jobs && scenario.jobs[scenario.jobs.length - 1].results) - .map((scenario) => { - const job = scenario.jobs[scenario.jobs.length - 1] - return { - metric, - x: scenario.name, - y: mean(job.results[metric]), - errorY: std(job.results[metric]), - label, - } - }) - }) - return dataPerMetric - }, [scenarios]) - - const categories = useMemo(() => ({ x: scenarios.map((s) => s.name).reverse() }), [scenarios]) - - if (status === 'loading') { - return ( - <Bullseye> - <EmptyState> - <EmptyStateIcon variant="container" component={Spinner} /> - <Title size="lg" headingLevel="h4"> - Loading Results - </Title> - </EmptyState> - </Bullseye> - ) - } else if (status === 'error') { - return ( - <Bullseye> - <EmptyState> - <EmptyStateIcon variant="container" component={ErrorCircleOIcon} /> - <Title size="lg" headingLevel="h4"> - Unable to connect - </Title> - <EmptyStateBody> - There was an error retrieving data. Check your connection and try again. - </EmptyStateBody> - </EmptyState> - </Bullseye> - ) - } else if (scenarios.length === 0) { - return ( - <Bullseye> - <EmptyState> - <EmptyStateIcon variant="container" component={CubesIcon} /> - <Title size="lg" headingLevel="h4"> - No results - </Title> - <EmptyStateBody> - No results are currently available for this portfolio. Run a scenario to obtain simulation - results. - </EmptyStateBody> - <NewScenario projectId={projectId} portfolioId={portfolioId} /> - </EmptyState> - </Bullseye> - ) - } - - return ( - <Grid hasGutter> - {AVAILABLE_METRICS.map( - (metric) => - selectedMetrics.has(metric) && ( - <GridItem xl={6} lg={12} key={metric}> - <Card> - <CardHeader> - <CardActions> - <PortfolioResultInfo metric={metric} /> - </CardActions> - <CardTitle>{METRIC_NAMES[metric]}</CardTitle> - </CardHeader> - <CardBody> - <Chart - width={650} - height={250} - padding={{ - top: 10, - bottom: 60, - left: 130, - }} - domainPadding={25} - > - <ChartAxis /> - <ChartAxis - dependentAxis - showGrid - label={METRIC_UNITS[metric]} - fixLabelOverlap - /> - <ChartBar - categories={categories} - data={dataPerMetric[metric]} - labelComponent={<ChartTooltip constrainToVisibleArea />} - barWidth={25} - horizontal - /> - <VictoryErrorBar - categories={categories} - data={dataPerMetric[metric]} - errorY={(d) => d.errorY} - labelComponent={<></>} - horizontal - /> - </Chart> - </CardBody> - </Card> - </GridItem> - ) - )} - </Grid> - ) -} - -PortfolioResults.propTypes = { - projectId: PropTypes.number, - portfolioId: PropTypes.number, -} - -export default PortfolioResults diff --git a/opendc-web/opendc-web-ui/src/components/portfolios/ScenarioState.js b/opendc-web/opendc-web-ui/src/components/portfolios/ScenarioState.js deleted file mode 100644 index 99d83f64..00000000 --- a/opendc-web/opendc-web-ui/src/components/portfolios/ScenarioState.js +++ /dev/null @@ -1,62 +0,0 @@ -/* - * 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 { ClockIcon, CheckCircleIcon, ErrorCircleOIcon } from '@patternfly/react-icons' -import { JobState } from '../../shapes' - -function ScenarioState({ state }) { - switch (state) { - case 'PENDING': - case 'CLAIMED': - return ( - <span> - <ClockIcon color="blue" /> Queued - </span> - ) - case 'RUNNING': - return ( - <span> - <ClockIcon color="green" /> Running - </span> - ) - case 'FINISHED': - return ( - <span> - <CheckCircleIcon color="green" /> Finished - </span> - ) - case 'FAILED': - return ( - <span> - <ErrorCircleOIcon color="red" /> Failed - </span> - ) - } - - return 'Unknown' -} - -ScenarioState.propTypes = { - state: JobState.isRequired, -} - -export default ScenarioState diff --git a/opendc-web/opendc-web-ui/src/components/portfolios/ScenarioTable.js b/opendc-web/opendc-web-ui/src/components/portfolios/ScenarioTable.js deleted file mode 100644 index b068d045..00000000 --- a/opendc-web/opendc-web-ui/src/components/portfolios/ScenarioTable.js +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 { Bullseye } from '@patternfly/react-core' -import Link from 'next/link' -import { TableComposable, Thead, Tr, Th, Tbody, Td, ActionsColumn } from '@patternfly/react-table' -import React from 'react' -import { Portfolio, Status } from '../../shapes' -import TableEmptyState from '../util/TableEmptyState' -import ScenarioState from './ScenarioState' -import { useDeleteScenario } from '../../data/project' - -function ScenarioTable({ portfolio, status }) { - const { mutate: deleteScenario } = useDeleteScenario() - const projectId = portfolio?.project?.id - const scenarios = portfolio?.scenarios ?? [] - - const actions = ({ number }) => [ - { - title: 'Delete Scenario', - onClick: () => deleteScenario({ projectId: projectId, number }), - isDisabled: number === 0, - }, - ] - - return ( - <TableComposable aria-label="Scenario List" variant="compact"> - <Thead> - <Tr> - <Th>Name</Th> - <Th>Topology</Th> - <Th>Trace</Th> - <Th>State</Th> - </Tr> - </Thead> - <Tbody> - {scenarios.map((scenario) => ( - <Tr key={scenario.id}> - <Td dataLabel="Name">{scenario.name}</Td> - <Td dataLabel="Topology"> - {scenario.topology ? ( - <Link href={`/projects/${projectId}/topologies/${scenario.topology.number}`}> - {scenario.topology.name} - </Link> - ) : ( - 'Unknown Topology' - )} - </Td> - <Td dataLabel="Workload">{`${scenario.workload.trace.name} (${ - scenario.workload.samplingFraction * 100 - }%)`}</Td> - <Td dataLabel="State"> - <ScenarioState state={scenario.jobs[scenario.jobs.length - 1].state} /> - </Td> - <Td isActionCell> - <ActionsColumn items={actions(scenario)} /> - </Td> - </Tr> - ))} - {scenarios.length === 0 && ( - <Tr> - <Td colSpan={4}> - <Bullseye> - <TableEmptyState - status={status} - loadingTitle="Loading Scenarios" - emptyTitle="No scenarios" - emptyText="You have not created any scenario for this portfolio yet. Click the New Scenario button to create one." - /> - </Bullseye> - </Td> - </Tr> - )} - </Tbody> - </TableComposable> - ) -} - -ScenarioTable.propTypes = { - portfolio: Portfolio, - status: Status.isRequired, -} - -export default ScenarioTable diff --git a/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js b/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js deleted file mode 100644 index 5aaa56ac..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.js +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { ToggleGroup, ToggleGroupItem } from '@patternfly/react-core' -import { filterPanel } from './FilterPanel.module.css' - -export const FILTERS = { SHOW_ALL: 'All Projects', SHOW_OWN: 'My Projects', SHOW_SHARED: 'Shared with me' } - -const FilterPanel = ({ onSelect, activeFilter = 'SHOW_ALL' }) => ( - <ToggleGroup className={`${filterPanel} pf-u-mb-sm`}> - {Object.keys(FILTERS).map((filter) => ( - <ToggleGroupItem - key={filter} - onChange={() => activeFilter === filter || onSelect(filter)} - isSelected={activeFilter === filter} - text={FILTERS[filter]} - /> - ))} - </ToggleGroup> -) - -FilterPanel.propTypes = { - onSelect: PropTypes.func.isRequired, - activeFilter: PropTypes.string, -} - -export default FilterPanel diff --git a/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.module.css b/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.module.css deleted file mode 100644 index 15c36821..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/FilterPanel.module.css +++ /dev/null @@ -1,7 +0,0 @@ -.filterPanel { - display: flex; -} - -.filterPanel > button { - flex: 1 !important; -} diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewPortfolio.js b/opendc-web/opendc-web-ui/src/components/projects/NewPortfolio.js deleted file mode 100644 index aebcc3c9..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/NewPortfolio.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - * 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 { PlusIcon } from '@patternfly/react-icons' -import { Button } from '@patternfly/react-core' -import { useState } from 'react' -import { useNewPortfolio } from '../../data/project' -import NewPortfolioModal from './NewPortfolioModal' - -function NewPortfolio({ projectId }) { - const [isVisible, setVisible] = useState(false) - const { mutate: addPortfolio } = useNewPortfolio() - - const onSubmit = (name, targets) => { - addPortfolio({ projectId, name, targets }) - setVisible(false) - } - - return ( - <> - <Button icon={<PlusIcon />} isSmall onClick={() => setVisible(true)}> - New Portfolio - </Button> - <NewPortfolioModal isOpen={isVisible} onSubmit={onSubmit} onCancel={() => setVisible(false)} /> - </> - ) -} - -NewPortfolio.propTypes = { - projectId: PropTypes.number, -} - -export default NewPortfolio diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewPortfolioModal.js b/opendc-web/opendc-web-ui/src/components/projects/NewPortfolioModal.js deleted file mode 100644 index ba4bc819..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/NewPortfolioModal.js +++ /dev/null @@ -1,161 +0,0 @@ -/* - * 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, { useRef, useState } from 'react' -import { - Form, - FormGroup, - FormSection, - NumberInput, - Select, - SelectGroup, - SelectOption, - SelectVariant, - TextInput, -} from '@patternfly/react-core' -import Modal from '../util/modals/Modal' -import { METRIC_GROUPS, METRIC_NAMES } from '../../util/available-metrics' - -const NewPortfolioModal = ({ isOpen, onSubmit: onSubmitUpstream, onCancel: onUpstreamCancel }) => { - const nameInput = useRef(null) - const [repeats, setRepeats] = useState(1) - const [isSelectOpen, setSelectOpen] = useState(false) - const [selectedMetrics, setSelectedMetrics] = useState([]) - - const [isSubmitted, setSubmitted] = useState(false) - const [errors, setErrors] = useState({}) - - const clearState = () => { - setSubmitted(false) - setErrors({}) - nameInput.current.value = '' - setRepeats(1) - setSelectOpen(false) - setSelectedMetrics([]) - } - - const onSubmit = (event) => { - setSubmitted(true) - - if (event) { - event.preventDefault() - } - - const name = nameInput.current.value - - if (!name) { - setErrors({ name: true }) - return false - } else { - onSubmitUpstream(name, { metrics: selectedMetrics, repeats }) - } - - clearState() - return false - } - const onCancel = () => { - onUpstreamCancel() - clearState() - } - - const onSelect = (event, selection) => { - if (selectedMetrics.includes(selection)) { - setSelectedMetrics((metrics) => metrics.filter((item) => item !== selection)) - } else { - setSelectedMetrics((metrics) => [...metrics, selection]) - } - } - - return ( - <Modal title="New Portfolio" isOpen={isOpen} onSubmit={onSubmit} onCancel={onCancel}> - <Form onSubmit={onSubmit}> - <FormSection> - <FormGroup - label="Name" - fieldId="name" - isRequired - validated={isSubmitted && errors.name ? 'error' : 'default'} - helperTextInvalid="This field cannot be empty" - > - <TextInput - name="name" - id="name" - type="text" - isRequired - ref={nameInput} - placeholder="My Portfolio" - /> - </FormGroup> - </FormSection> - <FormSection title="Targets" titleElement="h4"> - <FormGroup label="Metrics" fieldId="metrics"> - <Select - variant={SelectVariant.typeaheadMulti} - typeAheadAriaLabel="Select a metric" - onToggle={() => setSelectOpen(!isSelectOpen)} - onSelect={onSelect} - onClear={() => setSelectedMetrics([])} - selections={selectedMetrics} - isOpen={isSelectOpen} - placeholderText="Select a metric" - menuAppendTo="parent" - maxHeight="300px" - chipGroupProps={{ numChips: 1 }} - isGrouped - > - {Object.entries(METRIC_GROUPS).map(([group, metrics]) => ( - <SelectGroup label={group} key={group}> - {metrics.map((metric) => ( - <SelectOption key={metric} value={metric}> - {METRIC_NAMES[metric]} - </SelectOption> - ))} - </SelectGroup> - ))} - </Select> - </FormGroup> - <FormGroup label="Repeats per Scenario" fieldId="repeats"> - <NumberInput - id="repeats" - inputName="repeats" - type="number" - value={repeats} - onChange={(e) => setRepeats(Number(e.target.value))} - onPlus={() => setRepeats((r) => r + 1)} - onMinus={() => setRepeats((r) => r - 1)} - min={1} - /> - </FormGroup> - </FormSection> - </Form> - </Modal> - ) -} - -NewPortfolioModal.propTypes = { - isOpen: PropTypes.bool.isRequired, - onSubmit: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired, -} - -export default NewPortfolioModal diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewTopology.js b/opendc-web/opendc-web-ui/src/components/projects/NewTopology.js deleted file mode 100644 index 4c569c56..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/NewTopology.js +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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 { PlusIcon } from '@patternfly/react-icons' -import { Button } from '@patternfly/react-core' -import { useState } from 'react' -import { useNewTopology } from '../../data/topology' -import NewTopologyModal from './NewTopologyModal' - -function NewTopology({ projectId }) { - const [isVisible, setVisible] = useState(false) - const { mutate: addTopology } = useNewTopology() - - const onSubmit = (topology) => { - addTopology(topology) - setVisible(false) - } - return ( - <> - <Button icon={<PlusIcon />} isSmall onClick={() => setVisible(true)}> - New Topology - </Button> - <NewTopologyModal - projectId={projectId} - isOpen={isVisible} - onSubmit={onSubmit} - onCancel={() => setVisible(false)} - /> - </> - ) -} - -NewTopology.propTypes = { - projectId: PropTypes.number, -} - -export default NewTopology diff --git a/opendc-web/opendc-web-ui/src/components/projects/NewTopologyModal.js b/opendc-web/opendc-web-ui/src/components/projects/NewTopologyModal.js deleted file mode 100644 index 780ec034..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/NewTopologyModal.js +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 produce from 'immer' -import PropTypes from 'prop-types' -import React, { useRef, useState } from 'react' -import { Form, FormGroup, FormSelect, FormSelectOption, TextInput } from '@patternfly/react-core' -import { useTopologies } from '../../data/topology' -import Modal from '../util/modals/Modal' - -const NewTopologyModal = ({ projectId, isOpen, onSubmit: onSubmitUpstream, onCancel: onCancelUpstream }) => { - const nameInput = useRef(null) - const [isSubmitted, setSubmitted] = useState(false) - const [originTopology, setOriginTopology] = useState(-1) - const [errors, setErrors] = useState({}) - - const { data: topologies = [] } = useTopologies(projectId, { enabled: isOpen }) - - const clearState = () => { - if (nameInput.current) { - nameInput.current.value = '' - } - setSubmitted(false) - setOriginTopology(-1) - setErrors({}) - } - - const onSubmit = (event) => { - setSubmitted(true) - - if (event) { - event.preventDefault() - } - - const name = nameInput.current.value - - if (!name) { - setErrors({ name: true }) - return false - } else { - const candidate = topologies.find((topology) => topology.id === originTopology) || { rooms: [] } - const topology = produce(candidate, (draft) => { - delete draft.project - draft.projectId = projectId - draft.name = name - }) - onSubmitUpstream(topology) - } - - clearState() - return true - } - - const onCancel = () => { - onCancelUpstream() - clearState() - } - - return ( - <Modal title="New Topology" isOpen={isOpen} onSubmit={onSubmit} onCancel={onCancel}> - <Form onSubmit={onSubmit}> - <FormGroup - label="Name" - fieldId="name" - isRequired - validated={isSubmitted && errors.name ? 'error' : 'default'} - helperTextInvalid="This field cannot be empty" - > - <TextInput id="name" name="name" type="text" isRequired ref={nameInput} /> - </FormGroup> - <FormGroup label="Topology to duplicate" fieldId="origin" isRequired> - <FormSelect - id="origin" - name="origin" - value={originTopology} - onChange={(v) => setOriginTopology(+v)} - > - <FormSelectOption value={-1} key={-1} label="None - start from scratch" /> - {topologies.map((topology) => ( - <FormSelectOption value={topology.id} key={topology.id} label={topology.name} /> - ))} - </FormSelect> - </FormGroup> - </Form> - </Modal> - ) -} - -NewTopologyModal.propTypes = { - projectId: PropTypes.number, - isOpen: PropTypes.bool.isRequired, - onSubmit: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired, -} - -export default NewTopologyModal diff --git a/opendc-web/opendc-web-ui/src/components/projects/PortfolioTable.js b/opendc-web/opendc-web-ui/src/components/projects/PortfolioTable.js deleted file mode 100644 index 0afeaeaf..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/PortfolioTable.js +++ /dev/null @@ -1,99 +0,0 @@ -/* - * 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 { Bullseye } from '@patternfly/react-core' -import PropTypes from 'prop-types' -import Link from 'next/link' -import { TableComposable, Thead, Tbody, Tr, Th, Td, ActionsColumn } from '@patternfly/react-table' -import React from 'react' -import TableEmptyState from '../util/TableEmptyState' -import { usePortfolios, useDeletePortfolio } from '../../data/project' - -function PortfolioTable({ projectId }) { - const { status, data: portfolios = [] } = usePortfolios(projectId) - const { mutate: deletePortfolio } = useDeletePortfolio() - - const actions = (portfolio) => [ - { - title: 'Delete Portfolio', - onClick: () => deletePortfolio({ projectId, number: portfolio.number }), - }, - ] - - return ( - <TableComposable aria-label="Portfolio List" variant="compact"> - <Thead> - <Tr> - <Th>Name</Th> - <Th>Scenarios</Th> - <Th>Metrics</Th> - <Th>Repeats</Th> - </Tr> - </Thead> - <Tbody> - {portfolios.map((portfolio) => ( - <Tr key={portfolio.id}> - <Td dataLabel="Name"> - <Link href={`/projects/${projectId}/portfolios/${portfolio.number}`}>{portfolio.name}</Link> - </Td> - <Td dataLabel="Scenarios"> - {portfolio.scenarios.length === 1 - ? '1 scenario' - : `${portfolio.scenarios.length} scenarios`} - </Td> - <Td dataLabel="Metrics"> - {portfolio.targets.metrics.length === 1 - ? '1 metric' - : `${portfolio.targets.metrics.length} metrics`} - </Td> - <Td dataLabel="Repeats"> - {portfolio.targets.repeats === 1 ? '1 repeat' : `${portfolio.targets.repeats} repeats`} - </Td> - <Td isActionCell> - <ActionsColumn items={actions(portfolio)} /> - </Td> - </Tr> - ))} - {portfolios.length === 0 && ( - <Tr> - <Td colSpan={4}> - <Bullseye> - <TableEmptyState - status={status} - loadingTitle="Loading portfolios" - emptyTitle="No portfolios" - emptyText="You have not created any portfolio for this project yet. Click the New Portfolio button to create one." - /> - </Bullseye> - </Td> - </Tr> - )} - </Tbody> - </TableComposable> - ) -} - -PortfolioTable.propTypes = { - projectId: PropTypes.number, -} - -export default PortfolioTable diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectCollection.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectCollection.js deleted file mode 100644 index a26fed46..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/ProjectCollection.js +++ /dev/null @@ -1,137 +0,0 @@ -import Link from 'next/link' -import { - Gallery, - Bullseye, - EmptyState, - EmptyStateIcon, - Card, - CardTitle, - CardActions, - DropdownItem, - CardHeader, - Dropdown, - KebabToggle, - CardBody, - CardHeaderMain, - TextVariants, - Text, - TextContent, - Tooltip, - Button, - Label, -} from '@patternfly/react-core' -import { PlusIcon, FolderIcon, TrashIcon } from '@patternfly/react-icons' -import PropTypes from 'prop-types' -import React, { useReducer, useMemo } from 'react' -import { Project, Status } from '../../shapes' -import { parseAndFormatDateTime } from '../../util/date-time' -import { AUTH_DESCRIPTION_MAP, AUTH_ICON_MAP, AUTH_NAME_MAP } from '../../util/authorizations' -import TableEmptyState from '../util/TableEmptyState' - -function ProjectCard({ project, onDelete }) { - const [isKebabOpen, toggleKebab] = useReducer((t) => !t, false) - const { id, role, name, updatedAt } = project - const Icon = AUTH_ICON_MAP[role] - - return ( - <Card - isCompact - isRounded - isFlat - className="pf-u-min-height" - style={{ '--pf-u-min-height--MinHeight': '175px' }} - > - <CardHeader className="pf-u-flex-grow-1"> - <CardHeaderMain className="pf-u-align-self-flex-start"> - <FolderIcon /> - </CardHeaderMain> - <CardActions> - <Tooltip content={AUTH_DESCRIPTION_MAP[role]}> - <Label icon={<Icon />}>{AUTH_NAME_MAP[role]}</Label> - </Tooltip> - <Dropdown - isPlain - position="right" - toggle={<KebabToggle className="pf-u-px-0" onToggle={toggleKebab} />} - isOpen={isKebabOpen} - dropdownItems={[ - <DropdownItem - key="trash" - onClick={() => { - onDelete() - toggleKebab() - }} - position="right" - icon={<TrashIcon />} - > - Delete - </DropdownItem>, - ]} - /> - </CardActions> - </CardHeader> - <CardTitle component={Link} className="pf-u-pb-0" href={`/projects/${id}`}> - {name} - </CardTitle> - <CardBody isFilled={false}> - <TextContent> - <Text component={TextVariants.small}>Last modified {parseAndFormatDateTime(updatedAt)}</Text> - </TextContent> - </CardBody> - </Card> - ) -} - -function ProjectCollection({ status, projects, onDelete, onCreate, isFiltering }) { - const sortedProjects = useMemo(() => { - const res = [...projects] - res.sort((a, b) => (new Date(a.updatedAt) < new Date(b.updatedAt) ? 1 : -1)) - return res - }, [projects]) - - if (sortedProjects.length === 0) { - return ( - <TableEmptyState - status={status} - isFiltering={isFiltering} - loadingTitle="Loading Projects" - emptyTitle="No projects" - emptyText="You have not created any projects yet. Create a new project to get started quickly." - emptyAction={ - <Button icon={<PlusIcon />} onClick={onCreate}> - Create Project - </Button> - } - /> - ) - } - - return ( - <Gallery hasGutter aria-label="Available projects"> - {sortedProjects.map((project) => ( - <ProjectCard key={project.id} project={project} onDelete={() => onDelete(project)} /> - ))} - <Card isCompact isFlat isRounded style={{ borderStyle: 'dotted' }}> - <Bullseye> - <EmptyState> - <Button isBlock variant="link" onClick={onCreate}> - <EmptyStateIcon icon={PlusIcon} /> - <br /> - Create Project - </Button> - </EmptyState> - </Bullseye> - </Card> - </Gallery> - ) -} - -ProjectCollection.propTypes = { - status: Status.isRequired, - isFiltering: PropTypes.bool, - projects: PropTypes.arrayOf(Project).isRequired, - onDelete: PropTypes.func, - onCreate: PropTypes.func, -} - -export default ProjectCollection diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectOverview.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectOverview.js deleted file mode 100644 index 3e1656f6..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/ProjectOverview.js +++ /dev/null @@ -1,98 +0,0 @@ -/* - * 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 { - Card, - CardActions, - CardBody, - CardHeader, - CardTitle, - DescriptionList, - DescriptionListDescription, - DescriptionListGroup, - DescriptionListTerm, - Grid, - GridItem, - Skeleton, -} from '@patternfly/react-core' -import NewTopology from './NewTopology' -import TopologyTable from './TopologyTable' -import NewPortfolio from './NewPortfolio' -import PortfolioTable from './PortfolioTable' -import { useProject } from '../../data/project' - -function ProjectOverview({ projectId }) { - const { data: project } = useProject(projectId) - - return ( - <Grid hasGutter> - <GridItem md={2}> - <Card> - <CardTitle>Details</CardTitle> - <CardBody> - <DescriptionList> - <DescriptionListGroup> - <DescriptionListTerm>Name</DescriptionListTerm> - <DescriptionListDescription> - {project?.name ?? <Skeleton screenreaderText="Loading project" />} - </DescriptionListDescription> - </DescriptionListGroup> - </DescriptionList> - </CardBody> - </Card> - </GridItem> - <GridItem md={5}> - <Card> - <CardHeader> - <CardActions> - <NewTopology projectId={projectId} /> - </CardActions> - <CardTitle>Topologies</CardTitle> - </CardHeader> - <CardBody> - <TopologyTable projectId={projectId} /> - </CardBody> - </Card> - </GridItem> - <GridItem md={5}> - <Card> - <CardHeader> - <CardActions> - <NewPortfolio projectId={projectId} /> - </CardActions> - <CardTitle>Portfolios</CardTitle> - </CardHeader> - <CardBody> - <PortfolioTable projectId={projectId} /> - </CardBody> - </Card> - </GridItem> - </Grid> - ) -} - -ProjectOverview.propTypes = { - projectId: PropTypes.number, -} - -export default ProjectOverview diff --git a/opendc-web/opendc-web-ui/src/components/projects/TopologyTable.js b/opendc-web/opendc-web-ui/src/components/projects/TopologyTable.js deleted file mode 100644 index 1c2c4f04..00000000 --- a/opendc-web/opendc-web-ui/src/components/projects/TopologyTable.js +++ /dev/null @@ -1,115 +0,0 @@ -/* - * 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 { Bullseye, AlertGroup, Alert, AlertVariant, AlertActionCloseButton } from '@patternfly/react-core' -import PropTypes from 'prop-types' -import Link from 'next/link' -import { Tr, Th, Thead, Td, ActionsColumn, Tbody, TableComposable } from '@patternfly/react-table' -import React, { useState } from 'react' -import TableEmptyState from '../util/TableEmptyState' -import { parseAndFormatDateTime } from '../../util/date-time' -import { useTopologies, useDeleteTopology } from '../../data/topology' - -function TopologyTable({ projectId }) { - const [error, setError] = useState('') - - const { status, data: topologies = [] } = useTopologies(projectId) - const { mutate: deleteTopology } = useDeleteTopology({ - onError: (error) => setError(error), - }) - - const actions = ({ number }) => [ - { - title: 'Delete Topology', - onClick: () => deleteTopology({ projectId, number }), - isDisabled: number === 0, - }, - ] - - return ( - <> - <AlertGroup isToast> - {error && ( - <Alert - isLiveRegion - variant={AlertVariant.danger} - title={error} - actionClose={ - <AlertActionCloseButton - title={error} - variantLabel="danger alert" - onClose={() => setError(null)} - /> - } - /> - )} - </AlertGroup> - <TableComposable aria-label="Topology List" variant="compact"> - <Thead> - <Tr> - <Th>Name</Th> - <Th>Rooms</Th> - <Th>Last Edited</Th> - </Tr> - </Thead> - <Tbody> - {topologies.map((topology) => ( - <Tr key={topology.id}> - <Td dataLabel="Name"> - <Link href={`/projects/${projectId}/topologies/${topology.number}`}> - {topology.name} - </Link> - </Td> - <Td dataLabel="Rooms"> - {topology.rooms.length === 1 ? '1 room' : `${topology.rooms.length} rooms`} - </Td> - <Td dataLabel="Last Edited">{parseAndFormatDateTime(topology.updatedAt)}</Td> - <Td isActionCell> - <ActionsColumn items={actions(topology)} /> - </Td> - </Tr> - ))} - {topologies.length === 0 && ( - <Tr> - <Td colSpan={3}> - <Bullseye> - <TableEmptyState - status={status} - loadingTitle="Loading topologies" - emptyTitle="No topologies" - emptyText="You have not created any topology for this project yet. Click the New Topology button to create one." - /> - </Bullseye> - </Td> - </Tr> - )} - </Tbody> - </TableComposable> - </> - ) -} - -TopologyTable.propTypes = { - projectId: PropTypes.number, -} - -export default TopologyTable diff --git a/opendc-web/opendc-web-ui/src/components/topologies/RoomTable.js b/opendc-web/opendc-web-ui/src/components/topologies/RoomTable.js deleted file mode 100644 index 7f7b4171..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/RoomTable.js +++ /dev/null @@ -1,74 +0,0 @@ -import { Button, Bullseye } from '@patternfly/react-core' -import PropTypes from 'prop-types' -import React from 'react' -import { useDispatch } from 'react-redux' -import { useTopology } from '../../data/topology' -import { Tr, Th, Thead, TableComposable, Td, ActionsColumn, Tbody } from '@patternfly/react-table' -import { deleteRoom } from '../../redux/actions/topology/room' -import TableEmptyState from '../util/TableEmptyState' - -function RoomTable({ projectId, topologyId, onSelect }) { - const dispatch = useDispatch() - const { status, data: topology } = useTopology(projectId, topologyId) - const onDelete = (room) => dispatch(deleteRoom(room.id)) - const actions = (room) => [ - { - title: 'Delete room', - onClick: () => onDelete(room), - }, - ] - - return ( - <TableComposable aria-label="Room list" variant="compact"> - <Thead> - <Tr> - <Th>Name</Th> - <Th>Tiles</Th> - <Th>Racks</Th> - </Tr> - </Thead> - <Tbody> - {topology?.rooms.map((room) => { - const tileCount = room.tiles.length - const rackCount = room.tiles.filter((tile) => tile.rack).length - return ( - <Tr key={room.id}> - <Td dataLabel="Name"> - <Button variant="link" isInline onClick={() => onSelect(room)}> - {room.name} - </Button> - </Td> - <Td dataLabel="Tiles">{tileCount === 1 ? '1 tile' : `${tileCount} tiles`}</Td> - <Td dataLabel="Racks">{rackCount === 1 ? '1 rack' : `${rackCount} racks`}</Td> - <Td isActionCell> - <ActionsColumn items={actions(room)} /> - </Td> - </Tr> - ) - })} - {topology?.rooms.length === 0 && ( - <Tr> - <Td colSpan={4}> - <Bullseye> - <TableEmptyState - status={status} - loadingTitle="Loading Rooms" - emptyTitle="No rooms" - emptyText="There are currently no rooms in this topology. Open the Floor Plan to create a room" - /> - </Bullseye> - </Td> - </Tr> - )} - </Tbody> - </TableComposable> - ) -} - -RoomTable.propTypes = { - projectId: PropTypes.number, - topologyId: PropTypes.number, - onSelect: PropTypes.func, -} - -export default RoomTable diff --git a/opendc-web/opendc-web-ui/src/components/topologies/TopologyMap.js b/opendc-web/opendc-web-ui/src/components/topologies/TopologyMap.js deleted file mode 100644 index ff583750..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/TopologyMap.js +++ /dev/null @@ -1,69 +0,0 @@ -/* - * 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 React, { useState, useRef } from 'react' -import { - Bullseye, - Drawer, - DrawerContent, - DrawerContentBody, - EmptyState, - EmptyStateIcon, - Spinner, - Title, -} from '@patternfly/react-core' -import MapStage from './map/MapStage' -import Collapse from './map/controls/Collapse' -import { useSelector } from 'react-redux' -import TopologySidebar from './sidebar/TopologySidebar' - -function TopologyMap() { - const topologyIsLoading = useSelector((state) => !state.topology.root) - const interactionLevel = useSelector((state) => state.interactionLevel) - - const [isExpanded, setExpanded] = useState(true) - const panelContent = <TopologySidebar interactionLevel={interactionLevel} onClose={() => setExpanded(false)} /> - - const hotkeysRef = useRef() - - return topologyIsLoading ? ( - <Bullseye> - <EmptyState> - <EmptyStateIcon variant="container" component={Spinner} /> - <Title size="lg" headingLevel="h4"> - Loading Topology - </Title> - </EmptyState> - </Bullseye> - ) : ( - <Drawer isExpanded={isExpanded}> - <DrawerContent panelContent={panelContent}> - <DrawerContentBody style={{ position: 'relative' }}> - <MapStage hotkeysRef={hotkeysRef} /> - <Collapse onClick={() => setExpanded(true)} /> - </DrawerContentBody> - </DrawerContent> - </Drawer> - ) -} - -export default TopologyMap diff --git a/opendc-web/opendc-web-ui/src/components/topologies/TopologyOverview.js b/opendc-web/opendc-web-ui/src/components/topologies/TopologyOverview.js deleted file mode 100644 index f8ee4990..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/TopologyOverview.js +++ /dev/null @@ -1,92 +0,0 @@ -/* - * 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 { - Card, - CardBody, - CardTitle, - DescriptionList, - DescriptionListDescription, - DescriptionListGroup, - DescriptionListTerm, - Grid, - GridItem, - Skeleton, -} from '@patternfly/react-core' -import React from 'react' -import { useTopology } from '../../data/topology' -import { parseAndFormatDateTime } from '../../util/date-time' -import RoomTable from './RoomTable' - -function TopologyOverview({ projectId, topologyNumber, onSelect }) { - const { data: topology } = useTopology(projectId, topologyNumber) - return ( - <Grid hasGutter> - <GridItem md={2}> - <Card> - <CardTitle>Details</CardTitle> - <CardBody> - <DescriptionList> - <DescriptionListGroup> - <DescriptionListTerm>Name</DescriptionListTerm> - <DescriptionListDescription> - {topology?.name ?? <Skeleton screenreaderText="Loading topology" />} - </DescriptionListDescription> - </DescriptionListGroup> - <DescriptionListGroup> - <DescriptionListTerm>Last edited</DescriptionListTerm> - <DescriptionListDescription> - {topology ? ( - parseAndFormatDateTime(topology.updatedAt) - ) : ( - <Skeleton screenreaderText="Loading topology" /> - )} - </DescriptionListDescription> - </DescriptionListGroup> - </DescriptionList> - </CardBody> - </Card> - </GridItem> - <GridItem md={5}> - <Card> - <CardTitle>Rooms</CardTitle> - <CardBody> - <RoomTable - projectId={projectId} - topologyId={topologyNumber} - onSelect={(room) => onSelect('room', room)} - /> - </CardBody> - </Card> - </GridItem> - </Grid> - ) -} - -TopologyOverview.propTypes = { - projectId: PropTypes.number, - topologyNumber: PropTypes.number, - onSelect: PropTypes.func, -} - -export default TopologyOverview diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/GrayContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/GrayContainer.js deleted file mode 100644 index ccf637e5..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/GrayContainer.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 React from 'react' -import { useDispatch } from 'react-redux' -import { goDownOneInteractionLevel } from '../../../redux/actions/interaction-level' -import GrayLayer from './elements/GrayLayer' - -function GrayContainer() { - const dispatch = useDispatch() - const onClick = () => dispatch(goDownOneInteractionLevel()) - return <GrayLayer onClick={onClick} /> -} - -export default GrayContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/MapConstants.js b/opendc-web/opendc-web-ui/src/components/topologies/map/MapConstants.js deleted file mode 100644 index 4c3b2757..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/MapConstants.js +++ /dev/null @@ -1,25 +0,0 @@ -export const MAP_SIZE = 50 -export const TILE_SIZE_IN_PIXELS = 100 -export const TILE_SIZE_IN_METERS = 0.5 -export const MAP_SIZE_IN_PIXELS = MAP_SIZE * TILE_SIZE_IN_PIXELS - -export const OBJECT_MARGIN_IN_PIXELS = TILE_SIZE_IN_PIXELS / 5 -export const TILE_PLUS_MARGIN_IN_PIXELS = TILE_SIZE_IN_PIXELS / 3 -export const OBJECT_SIZE_IN_PIXELS = TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS * 2 - -export const GRID_LINE_WIDTH_IN_PIXELS = 2 -export const WALL_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 16 -export const OBJECT_BORDER_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 16 -export const TILE_PLUS_WIDTH_IN_PIXELS = TILE_SIZE_IN_PIXELS / 10 - -export const RACK_FILL_ICON_WIDTH = OBJECT_SIZE_IN_PIXELS / 3 -export const RACK_FILL_ICON_OPACITY = 0.8 - -export const MAP_MOVE_PIXELS_PER_EVENT = 20 -export const MAP_SCALE_PER_EVENT = 1.1 -export const MAP_MIN_SCALE = 0.5 -export const MAP_MAX_SCALE = 1.5 - -export const MAX_NUM_UNITS_PER_MACHINE = 6 -export const DEFAULT_RACK_SLOT_CAPACITY = 42 -export const DEFAULT_RACK_POWER_CAPACITY = 10000 diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.js b/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.js deleted file mode 100644 index e2b626ec..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.js +++ /dev/null @@ -1,83 +0,0 @@ -import React, { useRef, useState } from 'react' -import PropTypes from 'prop-types' -import { useHotkeys } from 'react-hotkeys-hook' -import { Stage } from 'react-konva' -import { MAP_MAX_SCALE, MAP_MIN_SCALE, MAP_MOVE_PIXELS_PER_EVENT, MAP_SCALE_PER_EVENT } from './MapConstants' -import useResizeObserver from 'use-resize-observer' -import { mapContainer } from './MapStage.module.css' -import MapLayer from './layers/MapLayer' -import RoomHoverLayer from './layers/RoomHoverLayer' -import ObjectHoverLayer from './layers/ObjectHoverLayer' -import ScaleIndicator from './controls/ScaleIndicator' -import Toolbar from './controls/Toolbar' - -function MapStage({ hotkeysRef }) { - const stageRef = useRef(null) - const { width = 500, height = 500 } = useResizeObserver({ ref: stageRef.current?.attrs?.container }) - const [[x, y], setPos] = useState([0, 0]) - const [scale, setScale] = useState(1) - - const clampScale = (target) => Math.min(Math.max(target, MAP_MIN_SCALE), MAP_MAX_SCALE) - const moveWithDelta = (deltaX, deltaY) => setPos(([x, y]) => [x + deltaX, y + deltaY]) - - const onZoom = (e) => { - e.evt.preventDefault() - - const stage = stageRef.current.getStage() - const oldScale = scale - - const pointer = stage.getPointerPosition() - const mousePointTo = { - x: (pointer.x - x) / oldScale, - y: (pointer.y - y) / oldScale, - } - - const newScale = clampScale(e.evt.deltaY > 0 ? oldScale * MAP_SCALE_PER_EVENT : oldScale / MAP_SCALE_PER_EVENT) - - setScale(newScale) - setPos([pointer.x - mousePointTo.x * newScale, pointer.y - mousePointTo.y * newScale]) - } - const onZoomButton = (zoomIn) => - setScale((scale) => clampScale(zoomIn ? scale * MAP_SCALE_PER_EVENT : scale / MAP_SCALE_PER_EVENT)) - const onDragEnd = (e) => setPos([e.target.x(), e.target.y()]) - const onExport = () => { - const download = document.createElement('a') - download.href = stageRef.current.getStage().toDataURL() - download.download = 'opendc-canvas-export-' + Date.now() + '.png' - download.click() - } - - useHotkeys('left, a', () => moveWithDelta(MAP_MOVE_PIXELS_PER_EVENT, 0), { element: hotkeysRef.current }) - useHotkeys('right, d', () => moveWithDelta(-MAP_MOVE_PIXELS_PER_EVENT, 0), { element: hotkeysRef.current }) - useHotkeys('up, w', () => moveWithDelta(0, MAP_MOVE_PIXELS_PER_EVENT), { element: hotkeysRef.current }) - useHotkeys('down, s', () => moveWithDelta(0, -MAP_MOVE_PIXELS_PER_EVENT), { element: hotkeysRef.current }) - - return ( - <> - <Stage - className={mapContainer} - ref={stageRef} - onWheel={onZoom} - onDragEnd={onDragEnd} - draggable - width={width} - height={height} - scale={{ x: scale, y: scale }} - x={x} - y={y} - > - <MapLayer /> - <RoomHoverLayer /> - <ObjectHoverLayer /> - </Stage> - <ScaleIndicator scale={scale} /> - <Toolbar onZoom={onZoomButton} onExport={onExport} /> - </> - ) -} - -MapStage.propTypes = { - hotkeysRef: PropTypes.object.isRequired, -} - -export default MapStage diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.module.css b/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.module.css deleted file mode 100644 index 47c3dde2..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/MapStage.module.css +++ /dev/null @@ -1,29 +0,0 @@ -/*! - * 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. - */ - -.mapContainer { - background-color: var(--pf-global--Color--light-200); - position: relative; - display: flex; - width: 100%; - height: 100%; -} diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/RackContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/RackContainer.js deleted file mode 100644 index 14449a91..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/RackContainer.js +++ /dev/null @@ -1,37 +0,0 @@ -/* - * 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 React from 'react' -import { useSelector } from 'react-redux' -import { Tile } from '../../../shapes' -import RackGroup from './groups/RackGroup' - -function RackContainer({ tile }) { - const interactionLevel = useSelector((state) => state.interactionLevel) - return <RackGroup interactionLevel={interactionLevel} tile={tile} /> -} - -RackContainer.propTypes = { - tile: Tile, -} - -export default RackContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/RackEnergyFillContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/RackEnergyFillContainer.js deleted file mode 100644 index a1ca7426..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/RackEnergyFillContainer.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { useSelector } from 'react-redux' -import RackFillBar from './elements/RackFillBar' - -function RackSpaceFillContainer({ rackId, ...props }) { - const fillFraction = useSelector((state) => { - const rack = state.topology.racks[rackId] - if (!rack) { - return 0 - } - - const { machines, cpus, gpus, memories, storages } = state.topology - let energyConsumptionTotal = 0 - - for (const machineId of rack.machines) { - if (!machineId) { - continue - } - const machine = machines[machineId] - machine.cpus.forEach((id) => (energyConsumptionTotal += cpus[id].energyConsumptionW)) - machine.gpus.forEach((id) => (energyConsumptionTotal += gpus[id].energyConsumptionW)) - machine.memories.forEach((id) => (energyConsumptionTotal += memories[id].energyConsumptionW)) - machine.storages.forEach((id) => (energyConsumptionTotal += storages[id].energyConsumptionW)) - } - - return Math.min(1, energyConsumptionTotal / rack.powerCapacityW) - }) - return <RackFillBar {...props} type="energy" fillFraction={fillFraction} /> -} - -RackSpaceFillContainer.propTypes = { - rackId: PropTypes.string.isRequired, -} - -export default RackSpaceFillContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/RackSpaceFillContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/RackSpaceFillContainer.js deleted file mode 100644 index 2039a9d3..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/RackSpaceFillContainer.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 React from 'react' -import PropTypes from 'prop-types' -import { useSelector } from 'react-redux' -import RackFillBar from './elements/RackFillBar' - -function RackSpaceFillContainer({ rackId, ...props }) { - const rack = useSelector((state) => state.topology.racks[rackId]) - - if (!rack) { - return null - } - - return <RackFillBar {...props} type="space" fillFraction={rack.machines.length / rack.capacity} /> -} - -RackSpaceFillContainer.propTypes = { - rackId: PropTypes.string.isRequired, -} - -export default RackSpaceFillContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/RoomContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/RoomContainer.js deleted file mode 100644 index 76785bea..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/RoomContainer.js +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { goFromBuildingToRoom } from '../../../redux/actions/interaction-level' -import RoomGroup from './groups/RoomGroup' - -function RoomContainer({ roomId, ...props }) { - const interactionLevel = useSelector((state) => state.interactionLevel) - const currentRoomInConstruction = useSelector((state) => state.construction.currentRoomInConstruction) - const room = useSelector((state) => state.topology.rooms[roomId]) - const dispatch = useDispatch() - - if (!room) { - return null - } - - return ( - <RoomGroup - {...props} - interactionLevel={interactionLevel} - currentRoomInConstruction={currentRoomInConstruction} - room={room} - onClick={() => dispatch(goFromBuildingToRoom(roomId))} - /> - ) -} - -RoomContainer.propTypes = { - roomId: PropTypes.string, -} - -export default RoomContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/TileContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/TileContainer.js deleted file mode 100644 index 0788b894..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/TileContainer.js +++ /dev/null @@ -1,50 +0,0 @@ -/* - * 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 React from 'react' -import PropTypes from 'prop-types' -import { useDispatch, useSelector } from 'react-redux' -import { goFromRoomToRack } from '../../../redux/actions/interaction-level' -import TileGroup from './groups/TileGroup' - -function TileContainer({ tileId, ...props }) { - const interactionLevel = useSelector((state) => state.interactionLevel) - const dispatch = useDispatch() - const tile = useSelector((state) => state.topology.tiles[tileId]) - - if (!tile) { - return null - } - - const onClick = (tile) => { - if (tile.rack) { - dispatch(goFromRoomToRack(tile.id)) - } - } - return <TileGroup {...props} onClick={onClick} tile={tile} interactionLevel={interactionLevel} /> -} - -TileContainer.propTypes = { - tileId: PropTypes.string.isRequired, -} - -export default TileContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/TopologyContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/TopologyContainer.js deleted file mode 100644 index cc0d46b3..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/TopologyContainer.js +++ /dev/null @@ -1,34 +0,0 @@ -/* - * 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 React from 'react' -import { useSelector } from 'react-redux' -import TopologyGroup from './groups/TopologyGroup' - -function TopologyContainer() { - const topology = useSelector((state) => state.topology.root) - const interactionLevel = useSelector((state) => state.interactionLevel) - - return <TopologyGroup topology={topology} interactionLevel={interactionLevel} /> -} - -export default TopologyContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/WallContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/WallContainer.js deleted file mode 100644 index 106d8d3d..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/WallContainer.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 React from 'react' -import PropTypes from 'prop-types' -import { useSelector } from 'react-redux' -import WallGroup from './groups/WallGroup' - -function WallContainer({ roomId, ...props }) { - const tiles = useSelector((state) => { - return state.topology.rooms[roomId]?.tiles.map((tileId) => state.topology.tiles[tileId]) ?? [] - }) - return <WallGroup {...props} tiles={tiles} /> -} - -WallContainer.propTypes = { - roomId: PropTypes.string.isRequired, -} - -export default WallContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.js b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.js deleted file mode 100644 index 931ded94..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.js +++ /dev/null @@ -1,42 +0,0 @@ -/* - * 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 { ChevronLeftIcon } from '@patternfly/react-icons' -import { collapseContainer } from './Collapse.module.css' -import { Button } from '@patternfly/react-core' - -function Collapse({ onClick }) { - return ( - <div className={collapseContainer}> - <Button variant="tertiary" onClick={onClick}> - <ChevronLeftIcon /> - </Button> - </div> - ) -} - -Collapse.propTypes = { - onClick: PropTypes.func, -} - -export default Collapse diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.module.css b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.module.css deleted file mode 100644 index 70fd465f..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Collapse.module.css +++ /dev/null @@ -1,55 +0,0 @@ -/*! - * 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. - */ - -.collapseContainer { - position: absolute; - right: var(--pf-global--spacer--xs); - top: 0; - bottom: 10%; - margin: auto 0; - height: 50px; -} - -.collapseContainer > button:global(.pf-m-tertiary) { - height: 100%; - padding: 2px; - - margin-right: var(--pf-global--spacer--xs); - margin-top: var(--pf-global--spacer--xs); - background-color: var(--pf-global--BackgroundColor--100); - border: none; - border-radius: var(--pf-global--BorderRadius--sm); - box-shadow: var(--pf-global--BoxShadow--sm); -} - -.collapseContainer > button:global(.pf-m-tertiary):not(:global(.pf-m-disabled)) { - background-color: var(--pf-global--BackgroundColor--100); -} - -.collapseContainer > button:global(.pf-m-tertiary):after { - display: none; -} - -.collapseContainer > button:global(.pf-m-tertiary):hover { - border: none; - box-shadow: var(--pf-global--BoxShadow--md); -} diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.js b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.js deleted file mode 100644 index 3ec893fb..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.js +++ /dev/null @@ -1,18 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { TILE_SIZE_IN_METERS, TILE_SIZE_IN_PIXELS } from '../MapConstants' -import { scaleIndicator } from './ScaleIndicator.module.css' - -function ScaleIndicator({ scale }) { - return ( - <div className={scaleIndicator} style={{ width: TILE_SIZE_IN_PIXELS * scale }}> - {TILE_SIZE_IN_METERS}m - </div> - ) -} - -ScaleIndicator.propTypes = { - scale: PropTypes.number.isRequired, -} - -export default ScaleIndicator diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.module.css b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.module.css deleted file mode 100644 index f19e0ff2..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/ScaleIndicator.module.css +++ /dev/null @@ -1,10 +0,0 @@ -.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/topologies/map/controls/Toolbar.js b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.js deleted file mode 100644 index 00aaf3e1..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.js +++ /dev/null @@ -1,33 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { control, toolBar } from './Toolbar.module.css' -import { Button } from '@patternfly/react-core' -import { SearchPlusIcon, SearchMinusIcon, CameraIcon } from '@patternfly/react-icons' - -function Toolbar({ onZoom, onExport }) { - return ( - <div className={toolBar}> - <Button variant="tertiary" title="Zoom in" onClick={() => onZoom(true)} className={control}> - <SearchPlusIcon /> - </Button> - <Button variant="tertiary" title="Zoom out" onClick={() => onZoom(false)} className={control}> - <SearchMinusIcon /> - </Button> - <Button - variant="tertiary" - title="Export Canvas to PNG Image" - onClick={() => onExport()} - className={control} - > - <CameraIcon /> - </Button> - </div> - ) -} - -Toolbar.propTypes = { - onZoom: PropTypes.func, - onExport: PropTypes.func, -} - -export default Toolbar diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.module.css b/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.module.css deleted file mode 100644 index 007389da..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/controls/Toolbar.module.css +++ /dev/null @@ -1,27 +0,0 @@ -.toolBar { - position: absolute; - bottom: var(--pf-global--spacer--md); - left: var(--pf-global--spacer--xl); -} - -.control:global(.pf-m-tertiary) { - margin-right: var(--pf-global--spacer--xs); - margin-top: var(--pf-global--spacer--xs); - background-color: var(--pf-global--BackgroundColor--100); - border: none; - border-radius: var(--pf-global--BorderRadius--sm); - box-shadow: var(--pf-global--BoxShadow--sm); -} - -.control:global(.pf-m-tertiary):not(:global(.pf-m-disabled)) { - background-color: var(--pf-global--BackgroundColor--100); -} - -.control:global(.pf-m-tertiary):after { - display: none; -} - -.control:global(.pf-m-tertiary):hover { - border: none; - box-shadow: var(--pf-global--BoxShadow--md); -} diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/Backdrop.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/Backdrop.js deleted file mode 100644 index 93037b51..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/Backdrop.js +++ /dev/null @@ -1,10 +0,0 @@ -import React from 'react' -import { Rect } from 'react-konva' -import { BACKDROP_COLOR } from '../../../../util/colors' -import { MAP_SIZE_IN_PIXELS } from '../MapConstants' - -function Backdrop() { - return <Rect x={0} y={0} width={MAP_SIZE_IN_PIXELS} height={MAP_SIZE_IN_PIXELS} fill={BACKDROP_COLOR} /> -} - -export default Backdrop diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/GrayLayer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/GrayLayer.js deleted file mode 100644 index 08c687f6..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/GrayLayer.js +++ /dev/null @@ -1,24 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Rect } from 'react-konva' -import { GRAYED_OUT_AREA_COLOR } from '../../../../util/colors' -import { MAP_SIZE_IN_PIXELS } from '../MapConstants' - -function GrayLayer({ onClick }) { - return ( - <Rect - x={0} - y={0} - width={MAP_SIZE_IN_PIXELS} - height={MAP_SIZE_IN_PIXELS} - fill={GRAYED_OUT_AREA_COLOR} - onClick={onClick} - /> - ) -} - -GrayLayer.propTypes = { - onClick: PropTypes.func, -} - -export default GrayLayer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/HoverTile.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/HoverTile.js deleted file mode 100644 index 20c2c6d1..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/HoverTile.js +++ /dev/null @@ -1,30 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Rect } from 'react-konva' -import { ROOM_HOVER_INVALID_COLOR, ROOM_HOVER_VALID_COLOR } from '../../../../util/colors' -import { TILE_SIZE_IN_PIXELS } from '../MapConstants' - -function HoverTile({ x, y, isValid, scale = 1, onClick }) { - return ( - <Rect - x={x} - y={y} - scaleX={scale} - scaleY={scale} - width={TILE_SIZE_IN_PIXELS} - height={TILE_SIZE_IN_PIXELS} - fill={isValid ? ROOM_HOVER_VALID_COLOR : ROOM_HOVER_INVALID_COLOR} - onClick={onClick} - /> - ) -} - -HoverTile.propTypes = { - x: PropTypes.number.isRequired, - y: PropTypes.number.isRequired, - isValid: PropTypes.bool.isRequired, - scale: PropTypes.number, - onClick: PropTypes.func.isRequired, -} - -export default HoverTile diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/ImageComponent.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/ImageComponent.js deleted file mode 100644 index fdae53f2..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/ImageComponent.js +++ /dev/null @@ -1,37 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useEffect, useState } from 'react' -import { Image } from 'react-konva' - -const imageCaches = {} - -function ImageComponent({ src, x, y, width, height, opacity }) { - const [image, setImage] = useState(null) - - useEffect(() => { - if (imageCaches[src]) { - setImage(imageCaches[src]) - return - } - - const image = new window.Image() - image.src = src - image.onload = () => { - setImage(image) - imageCaches[src] = image - } - }, [src]) - - // eslint-disable-next-line jsx-a11y/alt-text - 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/topologies/map/elements/RackFillBar.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/RackFillBar.js deleted file mode 100644 index aa284944..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/RackFillBar.js +++ /dev/null @@ -1,68 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Group, Rect } from 'react-konva' -import { - RACK_ENERGY_BAR_BACKGROUND_COLOR, - RACK_ENERGY_BAR_FILL_COLOR, - RACK_SPACE_BAR_BACKGROUND_COLOR, - RACK_SPACE_BAR_FILL_COLOR, -} from '../../../../util/colors' -import { - OBJECT_BORDER_WIDTH_IN_PIXELS, - OBJECT_MARGIN_IN_PIXELS, - RACK_FILL_ICON_OPACITY, - RACK_FILL_ICON_WIDTH, - TILE_SIZE_IN_PIXELS, -} from '../MapConstants' -import ImageComponent from './ImageComponent' - -function RackFillBar({ positionX, positionY, type, fillFraction }) { - const halfOfObjectBorderWidth = OBJECT_BORDER_WIDTH_IN_PIXELS / 2 - const x = - positionX * TILE_SIZE_IN_PIXELS + - OBJECT_MARGIN_IN_PIXELS + - (type === 'space' ? halfOfObjectBorderWidth : 0.5 * (TILE_SIZE_IN_PIXELS - 2 * OBJECT_MARGIN_IN_PIXELS)) - const startY = positionY * TILE_SIZE_IN_PIXELS + OBJECT_MARGIN_IN_PIXELS + halfOfObjectBorderWidth - const width = 0.5 * (TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS * 2) - halfOfObjectBorderWidth - const fullHeight = TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS * 2 - OBJECT_BORDER_WIDTH_IN_PIXELS - - const fractionHeight = fillFraction * fullHeight - const fractionY = - (positionY + 1) * TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS - halfOfObjectBorderWidth - fractionHeight - - return ( - <Group> - <Rect - x={x} - y={startY} - width={width} - height={fullHeight} - fill={type === 'space' ? RACK_SPACE_BAR_BACKGROUND_COLOR : RACK_ENERGY_BAR_BACKGROUND_COLOR} - /> - <Rect - x={x} - y={fractionY} - width={width} - height={fractionHeight} - fill={type === 'space' ? RACK_SPACE_BAR_FILL_COLOR : RACK_ENERGY_BAR_FILL_COLOR} - /> - <ImageComponent - src={'/img/topology/rack-' + type + '-icon.png'} - x={x + width * 0.5 - RACK_FILL_ICON_WIDTH * 0.5} - y={startY + fullHeight * 0.5 - RACK_FILL_ICON_WIDTH * 0.5} - width={RACK_FILL_ICON_WIDTH} - height={RACK_FILL_ICON_WIDTH} - opacity={RACK_FILL_ICON_OPACITY} - /> - </Group> - ) -} - -RackFillBar.propTypes = { - positionX: PropTypes.number.isRequired, - positionY: PropTypes.number.isRequired, - type: PropTypes.string.isRequired, - fillFraction: PropTypes.number.isRequired, -} - -export default RackFillBar diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/RoomTile.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/RoomTile.js deleted file mode 100644 index e7329dc0..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/RoomTile.js +++ /dev/null @@ -1,24 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Rect } from 'react-konva' -import { Tile } from '../../../../shapes' -import { TILE_SIZE_IN_PIXELS } from '../MapConstants' - -function RoomTile({ tile, color }) { - return ( - <Rect - x={tile.positionX * TILE_SIZE_IN_PIXELS} - y={tile.positionY * TILE_SIZE_IN_PIXELS} - width={TILE_SIZE_IN_PIXELS} - height={TILE_SIZE_IN_PIXELS} - fill={color} - /> - ) -} - -RoomTile.propTypes = { - tile: Tile, - color: PropTypes.string, -} - -export default RoomTile diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/TileObject.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/TileObject.js deleted file mode 100644 index 3211f187..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/TileObject.js +++ /dev/null @@ -1,27 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Rect } from 'react-konva' -import { OBJECT_BORDER_COLOR } from '../../../../util/colors' -import { OBJECT_BORDER_WIDTH_IN_PIXELS, OBJECT_MARGIN_IN_PIXELS, TILE_SIZE_IN_PIXELS } from '../MapConstants' - -function TileObject({ positionX, positionY, color }) { - return ( - <Rect - x={positionX * TILE_SIZE_IN_PIXELS + OBJECT_MARGIN_IN_PIXELS} - y={positionY * TILE_SIZE_IN_PIXELS + OBJECT_MARGIN_IN_PIXELS} - width={TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS * 2} - height={TILE_SIZE_IN_PIXELS - OBJECT_MARGIN_IN_PIXELS * 2} - fill={color} - stroke={OBJECT_BORDER_COLOR} - strokeWidth={OBJECT_BORDER_WIDTH_IN_PIXELS} - /> - ) -} - -TileObject.propTypes = { - positionX: PropTypes.number.isRequired, - positionY: PropTypes.number.isRequired, - color: PropTypes.string.isRequired, -} - -export default TileObject diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/TilePlusIcon.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/TilePlusIcon.js deleted file mode 100644 index 186c2b3a..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/TilePlusIcon.js +++ /dev/null @@ -1,44 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Group, Line } from 'react-konva' -import { TILE_PLUS_COLOR } from '../../../../util/colors' -import { TILE_PLUS_MARGIN_IN_PIXELS, TILE_PLUS_WIDTH_IN_PIXELS, TILE_SIZE_IN_PIXELS } from '../MapConstants' - -function TilePlusIcon({ x, y, scale = 1 }) { - const linePoints = [ - [ - x + 0.5 * TILE_SIZE_IN_PIXELS * scale, - y + TILE_PLUS_MARGIN_IN_PIXELS * scale, - x + 0.5 * TILE_SIZE_IN_PIXELS * scale, - y + TILE_SIZE_IN_PIXELS * scale - TILE_PLUS_MARGIN_IN_PIXELS * scale, - ], - [ - x + TILE_PLUS_MARGIN_IN_PIXELS * scale, - y + 0.5 * TILE_SIZE_IN_PIXELS * scale, - x + TILE_SIZE_IN_PIXELS * scale - TILE_PLUS_MARGIN_IN_PIXELS * scale, - y + 0.5 * TILE_SIZE_IN_PIXELS * scale, - ], - ] - return ( - <Group> - {linePoints.map((points, index) => ( - <Line - key={index} - points={points} - lineCap="round" - stroke={TILE_PLUS_COLOR} - strokeWidth={TILE_PLUS_WIDTH_IN_PIXELS * scale} - listening={false} - /> - ))} - </Group> - ) -} - -TilePlusIcon.propTypes = { - x: PropTypes.number, - y: PropTypes.number, - scale: PropTypes.number, -} - -export default TilePlusIcon diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/WallSegment.js b/opendc-web/opendc-web-ui/src/components/topologies/map/elements/WallSegment.js deleted file mode 100644 index 4f18813e..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/elements/WallSegment.js +++ /dev/null @@ -1,32 +0,0 @@ -import React from 'react' -import { Line } from 'react-konva' -import { WallSegment as WallSegmentShape } from '../../../../shapes' -import { WALL_COLOR } from '../../../../util/colors' -import { TILE_SIZE_IN_PIXELS, WALL_WIDTH_IN_PIXELS } from '../MapConstants' - -function WallSegment({ wallSegment }) { - let points - if (wallSegment.isHorizontal) { - points = [ - wallSegment.startPosX * TILE_SIZE_IN_PIXELS, - wallSegment.startPosY * TILE_SIZE_IN_PIXELS, - (wallSegment.startPosX + wallSegment.length) * TILE_SIZE_IN_PIXELS, - wallSegment.startPosY * TILE_SIZE_IN_PIXELS, - ] - } else { - points = [ - wallSegment.startPosX * TILE_SIZE_IN_PIXELS, - wallSegment.startPosY * TILE_SIZE_IN_PIXELS, - wallSegment.startPosX * TILE_SIZE_IN_PIXELS, - (wallSegment.startPosY + wallSegment.length) * TILE_SIZE_IN_PIXELS, - ] - } - - return <Line points={points} lineCap="round" stroke={WALL_COLOR} strokeWidth={WALL_WIDTH_IN_PIXELS} /> -} - -WallSegment.propTypes = { - wallSegment: WallSegmentShape, -} - -export default WallSegment diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/GridGroup.js b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/GridGroup.js deleted file mode 100644 index d66a18de..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/GridGroup.js +++ /dev/null @@ -1,36 +0,0 @@ -import React from 'react' -import { Group, Line } from 'react-konva' -import { GRID_COLOR } from '../../../../util/colors' -import { GRID_LINE_WIDTH_IN_PIXELS, MAP_SIZE, MAP_SIZE_IN_PIXELS, TILE_SIZE_IN_PIXELS } from '../MapConstants' - -const MAP_COORDINATE_ENTRIES = Array.from(new Array(MAP_SIZE), (x, i) => i) -const HORIZONTAL_POINT_PAIRS = MAP_COORDINATE_ENTRIES.map((index) => [ - 0, - index * TILE_SIZE_IN_PIXELS, - MAP_SIZE_IN_PIXELS, - index * TILE_SIZE_IN_PIXELS, -]) -const VERTICAL_POINT_PAIRS = MAP_COORDINATE_ENTRIES.map((index) => [ - index * TILE_SIZE_IN_PIXELS, - 0, - index * TILE_SIZE_IN_PIXELS, - MAP_SIZE_IN_PIXELS, -]) - -function GridGroup() { - return ( - <Group> - {HORIZONTAL_POINT_PAIRS.concat(VERTICAL_POINT_PAIRS).map((points, index) => ( - <Line - key={index} - points={points} - stroke={GRID_COLOR} - strokeWidth={GRID_LINE_WIDTH_IN_PIXELS} - listening={false} - /> - ))} - </Group> - ) -} - -export default GridGroup diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/RackGroup.js b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/RackGroup.js deleted file mode 100644 index ed942661..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/RackGroup.js +++ /dev/null @@ -1,25 +0,0 @@ -import React from 'react' -import { Group } from 'react-konva' -import { Tile } from '../../../../shapes' -import { RACK_BACKGROUND_COLOR } from '../../../../util/colors' -import TileObject from '../elements/TileObject' -import RackSpaceFillContainer from '../RackSpaceFillContainer' -import RackEnergyFillContainer from '../RackEnergyFillContainer' - -function RackGroup({ tile }) { - return ( - <Group> - <TileObject positionX={tile.positionX} positionY={tile.positionY} color={RACK_BACKGROUND_COLOR} /> - <Group> - <RackSpaceFillContainer rackId={tile.rack} positionX={tile.positionX} positionY={tile.positionY} /> - <RackEnergyFillContainer rackId={tile.rack} positionX={tile.positionX} positionY={tile.positionY} /> - </Group> - </Group> - ) -} - -RackGroup.propTypes = { - tile: Tile, -} - -export default RackGroup diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/RoomGroup.js b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/RoomGroup.js deleted file mode 100644 index 3f8b3089..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/RoomGroup.js +++ /dev/null @@ -1,52 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Group } from 'react-konva' -import { InteractionLevel, Room } from '../../../../shapes' -import GrayContainer from '../GrayContainer' -import TileContainer from '../TileContainer' -import WallContainer from '../WallContainer' - -function RoomGroup({ room, interactionLevel, currentRoomInConstruction, onClick }) { - if (currentRoomInConstruction === room.id) { - return ( - <Group onClick={onClick}> - {room.tiles.map((tileId) => ( - <TileContainer key={tileId} tileId={tileId} newTile={true} /> - ))} - </Group> - ) - } - - return ( - <Group onClick={onClick}> - {(() => { - if ( - (interactionLevel.mode === 'RACK' || interactionLevel.mode === 'MACHINE') && - interactionLevel.roomId === room.id - ) { - return [ - room.tiles - .filter((tileId) => tileId !== interactionLevel.tileId) - .map((tileId) => <TileContainer key={tileId} tileId={tileId} />), - <GrayContainer key={-1} />, - room.tiles - .filter((tileId) => tileId === interactionLevel.tileId) - .map((tileId) => <TileContainer key={tileId} tileId={tileId} />), - ] - } else { - return room.tiles.map((tileId) => <TileContainer key={tileId} tileId={tileId} />) - } - })()} - <WallContainer roomId={room.id} /> - </Group> - ) -} - -RoomGroup.propTypes = { - room: Room, - interactionLevel: InteractionLevel, - currentRoomInConstruction: PropTypes.string, - onClick: PropTypes.func, -} - -export default RoomGroup diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/TileGroup.js b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/TileGroup.js deleted file mode 100644 index f2084017..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/TileGroup.js +++ /dev/null @@ -1,36 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Group } from 'react-konva' -import { Tile } from '../../../../shapes' -import { ROOM_DEFAULT_COLOR, ROOM_IN_CONSTRUCTION_COLOR } from '../../../../util/colors' -import RoomTile from '../elements/RoomTile' -import RackContainer from '../RackContainer' - -function TileGroup({ tile, newTile, onClick }) { - let tileObject - if (tile.rack) { - tileObject = <RackContainer tile={tile} /> - } else { - tileObject = null - } - - let color = ROOM_DEFAULT_COLOR - if (newTile) { - color = ROOM_IN_CONSTRUCTION_COLOR - } - - return ( - <Group onClick={() => onClick(tile)}> - <RoomTile tile={tile} color={color} /> - {tileObject} - </Group> - ) -} - -TileGroup.propTypes = { - tile: Tile, - newTile: PropTypes.bool, - onClick: PropTypes.func, -} - -export default TileGroup diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/TopologyGroup.js b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/TopologyGroup.js deleted file mode 100644 index 011dcf34..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/TopologyGroup.js +++ /dev/null @@ -1,44 +0,0 @@ -import React from 'react' -import { Group } from 'react-konva' -import { InteractionLevel, Topology } from '../../../../shapes' -import RoomContainer from '../RoomContainer' -import GrayContainer from '../GrayContainer' - -function TopologyGroup({ topology, interactionLevel }) { - if (!topology) { - return <Group /> - } - - if (interactionLevel.mode === 'BUILDING') { - return ( - <Group> - {topology.rooms.map((roomId) => ( - <RoomContainer key={roomId} roomId={roomId} /> - ))} - </Group> - ) - } - - return ( - <Group> - {topology.rooms - .filter((roomId) => roomId !== interactionLevel.roomId) - .map((roomId) => ( - <RoomContainer key={roomId} roomId={roomId} /> - ))} - {interactionLevel.mode === 'ROOM' ? <GrayContainer /> : null} - {topology.rooms - .filter((roomId) => roomId === interactionLevel.roomId) - .map((roomId) => ( - <RoomContainer key={roomId} roomId={roomId} /> - ))} - </Group> - ) -} - -TopologyGroup.propTypes = { - topology: Topology, - interactionLevel: InteractionLevel, -} - -export default TopologyGroup diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/WallGroup.js b/opendc-web/opendc-web-ui/src/components/topologies/map/groups/WallGroup.js deleted file mode 100644 index 6cbd1cd0..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/groups/WallGroup.js +++ /dev/null @@ -1,22 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Group } from 'react-konva' -import { Tile } from '../../../../shapes' -import { deriveWallLocations } from '../../../../util/tile-calculations' -import WallSegment from '../elements/WallSegment' - -function WallGroup({ tiles }) { - return ( - <Group> - {deriveWallLocations(tiles).map((wallSegment, index) => ( - <WallSegment key={index} wallSegment={wallSegment} /> - ))} - </Group> - ) -} - -WallGroup.propTypes = { - tiles: PropTypes.arrayOf(Tile).isRequired, -} - -export default WallGroup diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/HoverLayerComponent.js b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/HoverLayerComponent.js deleted file mode 100644 index d7e0c56a..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/HoverLayerComponent.js +++ /dev/null @@ -1,55 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useMemo, useState } from 'react' -import { Layer } from 'react-konva/lib/ReactKonva' -import HoverTile from '../elements/HoverTile' -import { TILE_SIZE_IN_PIXELS } from '../MapConstants' -import { useEffectRef } from '../../../../util/effect-ref' - -function HoverLayerComponent({ isEnabled, isValid, onClick, children }) { - const [[mouseWorldX, mouseWorldY], setPos] = useState([0, 0]) - - const layerRef = useEffectRef((layer) => { - if (!layer) { - return - } - - const stage = layer.getStage() - - stage.on('mousemove.hover', () => { - // Transform used to convert mouse coordinates to world coordinates - const transform = stage.getAbsoluteTransform().copy() - transform.invert() - - const { x, y } = transform.point(stage.getPointerPosition()) - setPos([x, y]) - }) - return () => stage.off('mousemove.hover') - }) - - const gridX = Math.floor(mouseWorldX / TILE_SIZE_IN_PIXELS) - const gridY = Math.floor(mouseWorldY / TILE_SIZE_IN_PIXELS) - const valid = useMemo(() => isEnabled && isValid(gridX, gridY), [isEnabled, isValid, gridX, gridY]) - - if (!isEnabled) { - return <Layer /> - } - - const x = gridX * TILE_SIZE_IN_PIXELS - const y = gridY * TILE_SIZE_IN_PIXELS - - return ( - <Layer opacity={0.2} ref={layerRef}> - <HoverTile x={x} y={y} isValid={valid} onClick={() => (valid ? onClick(gridX, gridY) : undefined)} /> - {children ? React.cloneElement(children, { x, y, scale: 1 }) : undefined} - </Layer> - ) -} - -HoverLayerComponent.propTypes = { - isEnabled: PropTypes.bool.isRequired, - isValid: PropTypes.func.isRequired, - onClick: PropTypes.func.isRequired, - children: PropTypes.node, -} - -export default HoverLayerComponent diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/MapLayer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/MapLayer.js deleted file mode 100644 index c902532b..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/MapLayer.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 React from 'react' -import { Group, Layer } from 'react-konva' -import Backdrop from '../elements/Backdrop' -import TopologyContainer from '../TopologyContainer' -import GridGroup from '../groups/GridGroup' - -function MapLayer() { - return ( - <Layer> - <Group> - <Backdrop /> - <TopologyContainer /> - <GridGroup /> - </Group> - </Layer> - ) -} - -export default MapLayer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/ObjectHoverLayer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/ObjectHoverLayer.js deleted file mode 100644 index 5e741a3b..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/ObjectHoverLayer.js +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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 React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { addRackToTile } from '../../../../redux/actions/topology/room' -import { findTileWithPosition } from '../../../../util/tile-calculations' -import HoverLayerComponent from './HoverLayerComponent' -import TilePlusIcon from '../elements/TilePlusIcon' - -export default function ObjectHoverLayer() { - const isEnabled = useSelector((state) => state.construction.inRackConstructionMode) - const isValid = useSelector((state) => (x, y) => { - if (state.interactionLevel.mode !== 'ROOM') { - return false - } - - const currentRoom = state.topology.rooms[state.interactionLevel.roomId] - const tiles = currentRoom.tiles.map((tileId) => state.topology.tiles[tileId]) - const tile = findTileWithPosition(tiles, x, y) - - return !(tile === null || tile.rack) - }) - - const dispatch = useDispatch() - const onClick = (x, y) => dispatch(addRackToTile(x, y)) - return ( - <HoverLayerComponent onClick={onClick} isEnabled={isEnabled} isValid={isValid}> - <TilePlusIcon /> - </HoverLayerComponent> - ) -} diff --git a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/RoomHoverLayer.js b/opendc-web/opendc-web-ui/src/components/topologies/map/layers/RoomHoverLayer.js deleted file mode 100644 index b9cfcaf4..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/map/layers/RoomHoverLayer.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { toggleTileAtLocation } from '../../../../redux/actions/topology/building' -import { - deriveValidNextTilePositions, - findPositionInPositions, - findPositionInRooms, -} from '../../../../util/tile-calculations' -import HoverLayerComponent from './HoverLayerComponent' - -export default function RoomHoverLayer() { - const dispatch = useDispatch() - const onClick = (x, y) => dispatch(toggleTileAtLocation(x, y)) - const isEnabled = useSelector((state) => state.construction.currentRoomInConstruction !== '-1') - const isValid = useSelector((state) => (x, y) => { - const newRoom = { ...state.topology.rooms[state.construction.currentRoomInConstruction] } - const oldRooms = Object.keys(state.topology.rooms) - .map((id) => ({ ...state.topology.rooms[id] })) - .filter( - (room) => - state.topology.root.rooms.indexOf(room.id) !== -1 && - room.id !== state.construction.currentRoomInConstruction - ) - - ;[...oldRooms, newRoom].forEach((room) => { - room.tiles = room.tiles.map((tileId) => state.topology.tiles[tileId]) - }) - if (newRoom.tiles.length === 0) { - return findPositionInRooms(oldRooms, x, y) === -1 - } - - const validNextPositions = deriveValidNextTilePositions(oldRooms, newRoom.tiles) - return findPositionInPositions(validNextPositions, x, y) !== -1 - }) - - return <HoverLayerComponent onClick={onClick} isEnabled={isEnabled} isValid={isValid} /> -} diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/NameComponent.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/NameComponent.js deleted file mode 100644 index ececd07b..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/NameComponent.js +++ /dev/null @@ -1,69 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useRef, useState } from 'react' -import { Button, TextInput } from '@patternfly/react-core' -import { PencilAltIcon, CheckIcon, TimesIcon } from '@patternfly/react-icons' - -function NameComponent({ name, onEdit }) { - const [isEditing, setEditing] = useState(false) - const nameInput = useRef(null) - - const onCancel = () => { - nameInput.current.value = name - setEditing(false) - } - - const onSubmit = (event) => { - if (event) { - event.preventDefault() - } - - const name = nameInput.current.value - if (name) { - onEdit(name) - } - - setEditing(false) - } - - return ( - <form - className={`pf-c-inline-edit ${isEditing ? 'pf-m-inline-editable' : ''} pf-u-display-inline-block`} - onSubmit={onSubmit} - > - <div className="pf-c-inline-edit__group"> - <div className="pf-c-inline-edit__value" id="single-inline-edit-example-label"> - {name} - </div> - <div className="pf-c-inline-edit__action pf-m-enable-editable"> - <Button className="pf-u-py-0" variant="plain" aria-label="Edit" onClick={() => setEditing(true)}> - <PencilAltIcon /> - </Button> - </div> - </div> - <div className="pf-c-inline-edit__group"> - <div className="pf-c-inline-edit__input"> - <TextInput type="text" defaultValue={name} ref={nameInput} aria-label="Editable text input" /> - </div> - <div className="pf-c-inline-edit__group pf-m-action-group pf-m-icon-group"> - <div className="pf-c-inline-edit__action pf-m-valid"> - <Button className="pf-u-py-0" variant="plain" aria-label="Save edits" onClick={onSubmit}> - <CheckIcon /> - </Button> - </div> - <div className="pf-c-inline-edit__action"> - <Button className="pf-u-py-0" variant="plain" aria-label="Cancel edits" onClick={onCancel}> - <TimesIcon /> - </Button> - </div> - </div> - </div> - </form> - ) -} - -NameComponent.propTypes = { - name: PropTypes.string, - onEdit: PropTypes.func, -} - -export default NameComponent diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/TopologySidebar.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/TopologySidebar.js deleted file mode 100644 index 5aaa7834..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/TopologySidebar.js +++ /dev/null @@ -1,83 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { InteractionLevel } from '../../../shapes' -import BuildingSidebar from './building/BuildingSidebar' -import { - Button, - DrawerActions, - DrawerCloseButton, - DrawerHead, - DrawerPanelBody, - DrawerPanelContent, - Flex, - Title, -} from '@patternfly/react-core' -import { AngleLeftIcon } from '@patternfly/react-icons' -import { useDispatch } from 'react-redux' -import { backButton } from './TopologySidebar.module.css' -import RoomSidebar from './room/RoomSidebar' -import RackSidebar from './rack/RackSidebar' -import MachineSidebar from './machine/MachineSidebar' -import { goDownOneInteractionLevel } from '../../../redux/actions/interaction-level' - -const name = { - BUILDING: 'Building', - ROOM: 'Room', - RACK: 'Rack', - MACHINE: 'Machine', -} - -function TopologySidebar({ interactionLevel, onClose }) { - let sidebarContent - - switch (interactionLevel.mode) { - case 'BUILDING': - sidebarContent = <BuildingSidebar /> - break - case 'ROOM': - sidebarContent = <RoomSidebar roomId={interactionLevel.roomId} /> - break - case 'RACK': - sidebarContent = <RackSidebar tileId={interactionLevel.tileId} /> - break - case 'MACHINE': - sidebarContent = <MachineSidebar tileId={interactionLevel.tileId} position={interactionLevel.position} /> - break - default: - sidebarContent = 'Missing Content' - } - - const dispatch = useDispatch() - const onClick = () => dispatch(goDownOneInteractionLevel()) - - return ( - <DrawerPanelContent isResizable defaultSize="450px" minSize="400px"> - <DrawerHead> - <Flex> - <Button - variant="tertiary" - isSmall - className={backButton} - onClick={interactionLevel.mode === 'BUILDING' ? onClose : onClick} - > - <AngleLeftIcon /> - </Button> - <Title className="pf-u-align-self-center" headingLevel="h1"> - {name[interactionLevel.mode]} - </Title> - </Flex> - <DrawerActions> - <DrawerCloseButton onClose={onClose} /> - </DrawerActions> - </DrawerHead> - <DrawerPanelBody>{sidebarContent}</DrawerPanelBody> - </DrawerPanelContent> - ) -} - -TopologySidebar.propTypes = { - interactionLevel: InteractionLevel, - onClose: PropTypes.func, -} - -export default TopologySidebar diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/TopologySidebar.module.css b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/TopologySidebar.module.css deleted file mode 100644 index 3853c625..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/TopologySidebar.module.css +++ /dev/null @@ -1,35 +0,0 @@ -/*! - * 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. - */ - -.backButton:global(.pf-c-button) { - align-self: center; - --pf-c-button--after--BorderColor: var(--pf-global--BorderColor--light-100); - color: var(--pf-global--Color--400); - - --pf-c-button--PaddingRight: var(--pf-global--spacer--sm); - --pf-c-button--PaddingLeft: var(--pf-global--spacer--sm); -} - -.backButton:hover, -.backButton:global(.pf-c-button):focus { - --pf-c-button--after--BorderColor: var(--pf-global--BorderColor--100); -} diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/building/BuildingSidebar.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/building/BuildingSidebar.js deleted file mode 100644 index 5fcd46be..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/building/BuildingSidebar.js +++ /dev/null @@ -1,8 +0,0 @@ -import React from 'react' -import NewRoomConstructionContainer from './NewRoomConstructionContainer' - -function BuildingSidebar() { - return <NewRoomConstructionContainer /> -} - -export default BuildingSidebar diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/building/NewRoomConstructionComponent.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/building/NewRoomConstructionComponent.js deleted file mode 100644 index 9fc85d0c..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/building/NewRoomConstructionComponent.js +++ /dev/null @@ -1,46 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Button, Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem } from '@patternfly/react-core' -import PlusIcon from '@patternfly/react-icons/dist/js/icons/plus-icon' -import CheckIcon from '@patternfly/react-icons/dist/js/icons/check-icon' - -function NewRoomConstructionComponent({ onStart, onFinish, onCancel, currentRoomInConstruction }) { - if (currentRoomInConstruction === '-1') { - return ( - <Button isBlock icon={<PlusIcon />} onClick={onStart}> - Construct a new room - </Button> - ) - } - return ( - <Toolbar - inset={{ - default: 'insetNone', - }} - > - <ToolbarContent> - <ToolbarGroup> - <ToolbarItem> - <Button icon={<CheckIcon />} onClick={onFinish}> - Finalize new room - </Button> - </ToolbarItem> - <ToolbarItem widths={{ default: '100%' }}> - <Button isBlock variant="secondary" onClick={onCancel}> - Cancel - </Button> - </ToolbarItem> - </ToolbarGroup> - </ToolbarContent> - </Toolbar> - ) -} - -NewRoomConstructionComponent.propTypes = { - onStart: PropTypes.func, - onFinish: PropTypes.func, - onCancel: PropTypes.func, - currentRoomInConstruction: PropTypes.string, -} - -export default NewRoomConstructionComponent diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/building/NewRoomConstructionContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/building/NewRoomConstructionContainer.js deleted file mode 100644 index c149b224..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/building/NewRoomConstructionContainer.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { - cancelNewRoomConstruction, - finishNewRoomConstruction, - startNewRoomConstruction, -} from '../../../../redux/actions/topology/building' -import NewRoomConstructionComponent from './NewRoomConstructionComponent' - -function NewRoomConstructionButton() { - const currentRoomInConstruction = useSelector((state) => state.construction.currentRoomInConstruction) - const dispatch = useDispatch() - - return ( - <NewRoomConstructionComponent - onStart={() => dispatch(startNewRoomConstruction())} - onFinish={() => dispatch(finishNewRoomConstruction())} - onCancel={() => dispatch(cancelNewRoomConstruction())} - currentRoomInConstruction={currentRoomInConstruction} - /> - ) -} - -export default NewRoomConstructionButton diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/DeleteMachine.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/DeleteMachine.js deleted file mode 100644 index a4b9457b..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/DeleteMachine.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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, { useState } from 'react' -import { useDispatch } from 'react-redux' -import { Button } from '@patternfly/react-core' -import { TrashIcon } from '@patternfly/react-icons' -import ConfirmationModal from '../../../util/modals/ConfirmationModal' -import { deleteMachine } from '../../../../redux/actions/topology/machine' - -function DeleteMachine({ machineId }) { - const dispatch = useDispatch() - const [isVisible, setVisible] = useState(false) - const callback = (isConfirmed) => { - if (isConfirmed) { - dispatch(deleteMachine(machineId)) - } - setVisible(false) - } - return ( - <> - <Button variant="danger" icon={<TrashIcon />} isBlock onClick={() => setVisible(true)}> - Delete this machine - </Button> - <ConfirmationModal - title="Delete this machine" - message="Are you sure you want to delete this machine?" - isOpen={isVisible} - callback={callback} - /> - </> - ) -} - -DeleteMachine.propTypes = { - machineId: PropTypes.string.isRequired, -} - -export default DeleteMachine diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/MachineSidebar.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/MachineSidebar.js deleted file mode 100644 index 8a4c33dc..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/MachineSidebar.js +++ /dev/null @@ -1,55 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import UnitTabsComponent from './UnitTabsComponent' -import DeleteMachine from './DeleteMachine' -import { - TextContent, - TextList, - TextListItem, - TextListItemVariants, - TextListVariants, - Title, -} from '@patternfly/react-core' -import { useSelector } from 'react-redux' - -function MachineSidebar({ tileId, position }) { - const machine = useSelector(({ topology }) => { - const rack = topology.racks[topology.tiles[tileId].rack] - - for (const machineId of rack.machines) { - const machine = topology.machines[machineId] - if (machine.position === position) { - return machine - } - } - }) - const machineId = machine.id - return ( - <div> - <TextContent> - <Title headingLevel="h2">Details</Title> - <TextList component={TextListVariants.dl}> - <TextListItem component={TextListItemVariants.dt}>Name</TextListItem> - <TextListItem component={TextListItemVariants.dd}> - Machine at position {machine.position} - </TextListItem> - </TextList> - - <Title headingLevel="h2">Actions</Title> - <DeleteMachine machineId={machineId} /> - - <Title headingLevel="h2">Units</Title> - </TextContent> - <div className="pf-u-h-100"> - <UnitTabsComponent machineId={machineId} /> - </div> - </div> - ) -} - -MachineSidebar.propTypes = { - tileId: PropTypes.string.isRequired, - position: PropTypes.number.isRequired, -} - -export default MachineSidebar diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitAddComponent.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitAddComponent.js deleted file mode 100644 index 18cba23a..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitAddComponent.js +++ /dev/null @@ -1,42 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useState } from 'react' -import { Button, InputGroup, Select, SelectOption, SelectVariant } from '@patternfly/react-core' -import PlusIcon from '@patternfly/react-icons/dist/js/icons/plus-icon' - -function UnitAddComponent({ units, onAdd }) { - const [isOpen, setOpen] = useState(false) - const [selected, setSelected] = useState(null) - - return ( - <InputGroup> - <Select - variant={SelectVariant.single} - placeholderText="Select a unit" - aria-label="Select Unit" - onToggle={() => setOpen(!isOpen)} - isOpen={isOpen} - onSelect={(_, selection) => { - setSelected(selection) - setOpen(false) - }} - selections={selected} - > - {units.map((unit) => ( - <SelectOption value={unit.id} key={unit.id}> - {unit.name} - </SelectOption> - ))} - </Select> - <Button icon={<PlusIcon />} variant="control" onClick={() => onAdd(selected)} isDisabled={!selected}> - Add - </Button> - </InputGroup> - ) -} - -UnitAddComponent.propTypes = { - units: PropTypes.array.isRequired, - onAdd: PropTypes.func.isRequired, -} - -export default UnitAddComponent diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitAddContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitAddContainer.js deleted file mode 100644 index a0054ef6..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitAddContainer.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 from 'react' -import { useDispatch, useSelector } from 'react-redux' -import UnitAddComponent from './UnitAddComponent' -import { addUnit } from '../../../../redux/actions/topology/machine' -import UnitType from './UnitType' - -function UnitAddContainer({ machineId, unitType }) { - const units = useSelector((state) => Object.values(state.topology[unitType])) - const dispatch = useDispatch() - - const onAdd = (id) => dispatch(addUnit(machineId, unitType, id)) - - return <UnitAddComponent onAdd={onAdd} units={units} /> -} - -UnitAddContainer.propTypes = { - machineId: PropTypes.string.isRequired, - unitType: UnitType.isRequired, -} - -export default UnitAddContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitListComponent.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitListComponent.js deleted file mode 100644 index 75ab0ad7..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitListComponent.js +++ /dev/null @@ -1,113 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { - Button, - DataList, - DataListAction, - DataListCell, - DataListItem, - DataListItemCells, - DataListItemRow, - DescriptionList, - DescriptionListDescription, - DescriptionListGroup, - DescriptionListTerm, - EmptyState, - EmptyStateBody, - EmptyStateIcon, - Popover, - Title, -} from '@patternfly/react-core' -import { CubesIcon, InfoIcon, TrashIcon } from '@patternfly/react-icons' -import { ProcessingUnit, StorageUnit } from '../../../../shapes' -import UnitType from './UnitType' - -function UnitInfo({ unit, unitType }) { - if (unitType === 'cpus' || unitType === 'gpus') { - return ( - <DescriptionList> - <DescriptionListGroup> - <DescriptionListTerm>Clock Frequency</DescriptionListTerm> - <DescriptionListDescription>{unit.clockRateMhz} MHz</DescriptionListDescription> - </DescriptionListGroup> - <DescriptionListGroup> - <DescriptionListTerm>Number of Cores</DescriptionListTerm> - <DescriptionListDescription>{unit.numberOfCores}</DescriptionListDescription> - </DescriptionListGroup> - <DescriptionListGroup> - <DescriptionListTerm>Energy Consumption</DescriptionListTerm> - <DescriptionListDescription>{unit.energyConsumptionW} W</DescriptionListDescription> - </DescriptionListGroup> - </DescriptionList> - ) - } - - return ( - <DescriptionList> - <DescriptionListGroup> - <DescriptionListTerm>Speed</DescriptionListTerm> - <DescriptionListDescription>{unit.speedMbPerS} Mb/s</DescriptionListDescription> - </DescriptionListGroup> - <DescriptionListGroup> - <DescriptionListTerm>Capacity</DescriptionListTerm> - <DescriptionListDescription>{unit.sizeMb} MB</DescriptionListDescription> - </DescriptionListGroup> - <DescriptionListGroup> - <DescriptionListTerm>Energy Consumption</DescriptionListTerm> - <DescriptionListDescription>{unit.energyConsumptionW} W</DescriptionListDescription> - </DescriptionListGroup> - </DescriptionList> - ) -} - -UnitInfo.propTypes = { - unitType: UnitType.isRequired, - unit: PropTypes.oneOfType([ProcessingUnit, StorageUnit]).isRequired, -} - -function UnitListComponent({ unitType, units, onDelete }) { - if (units.length === 0) { - return ( - <EmptyState> - <EmptyStateIcon icon={CubesIcon} /> - <Title headingLevel="h5" size="lg"> - No units found - </Title> - <EmptyStateBody>You have not configured any units yet. Add some with the menu above!</EmptyStateBody> - </EmptyState> - ) - } - - return ( - <DataList aria-label="Machine Units" isCompact> - {units.map((unit, index) => ( - <DataListItem key={index}> - <DataListItemRow> - <DataListItemCells dataListCells={[<DataListCell key="unit">{unit.name}</DataListCell>]} /> - <DataListAction id="goto" aria-label="Goto Machine" aria-labelledby="goto"> - <Popover - headerContent="Unit Information" - bodyContent={<UnitInfo unitType={unitType} unit={unit} />} - > - <Button isSmall variant="plain" className="pf-u-p-0"> - <InfoIcon /> - </Button> - </Popover> - <Button isSmall variant="plain" className="pf-u-p-0" onClick={() => onDelete(units[index])}> - <TrashIcon /> - </Button> - </DataListAction> - </DataListItemRow> - </DataListItem> - ))} - </DataList> - ) -} - -UnitListComponent.propTypes = { - unitType: UnitType.isRequired, - units: PropTypes.arrayOf(PropTypes.oneOfType([ProcessingUnit, StorageUnit])).isRequired, - onDelete: PropTypes.func, -} - -export default UnitListComponent diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitListContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitListContainer.js deleted file mode 100644 index bcd4bdcc..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitListContainer.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 from 'react' -import { useDispatch, useSelector } from 'react-redux' -import UnitListComponent from './UnitListComponent' -import { deleteUnit } from '../../../../redux/actions/topology/machine' -import UnitType from './UnitType' - -function UnitListContainer({ machineId, unitType }) { - const dispatch = useDispatch() - const units = useSelector((state) => { - const machine = state.topology.machines[machineId] - return machine[unitType].map((id) => state.topology[unitType][id]) - }) - - const onDelete = (unit) => dispatch(deleteUnit(machineId, unitType, unit.id)) - - return <UnitListComponent units={units} unitType={unitType} onDelete={onDelete} /> -} - -UnitListContainer.propTypes = { - machineId: PropTypes.string.isRequired, - unitType: UnitType.isRequired, -} - -export default UnitListContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitTabsComponent.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitTabsComponent.js deleted file mode 100644 index 4032d607..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitTabsComponent.js +++ /dev/null @@ -1,36 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useState } from 'react' -import { Tab, Tabs, TabTitleText } from '@patternfly/react-core' -import UnitAddContainer from './UnitAddContainer' -import UnitListContainer from './UnitListContainer' - -function UnitTabsComponent({ machineId }) { - const [activeTab, setActiveTab] = useState('cpuModel-units') - - return ( - <Tabs activeKey={activeTab} onSelect={(_, tab) => setActiveTab(tab)}> - <Tab eventKey="cpuModel-units" title={<TabTitleText>CPU</TabTitleText>}> - <UnitAddContainer machineId={machineId} unitType="cpus" /> - <UnitListContainer machineId={machineId} unitType="cpus" /> - </Tab> - <Tab eventKey="gpu-units" title={<TabTitleText>GPU</TabTitleText>}> - <UnitAddContainer machineId={machineId} unitType="gpus" /> - <UnitListContainer machineId={machineId} unitType="gpus" /> - </Tab> - <Tab eventKey="memory-units" title={<TabTitleText>Memory</TabTitleText>}> - <UnitAddContainer machineId={machineId} unitType="memories" /> - <UnitListContainer machineId={machineId} unitType="memories" /> - </Tab> - <Tab eventKey="storage-units" title={<TabTitleText>Storage</TabTitleText>}> - <UnitAddContainer machineId={machineId} unitType="storages" /> - <UnitListContainer machineId={machineId} unitType="storages" /> - </Tab> - </Tabs> - ) -} - -UnitTabsComponent.propTypes = { - machineId: PropTypes.string.isRequired, -} - -export default UnitTabsComponent diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitType.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitType.js deleted file mode 100644 index b6d7bf8b..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/machine/UnitType.js +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2022 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' - -export default PropTypes.oneOf(['cpus', 'gpus', 'memories', 'storages']) diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/AddPrefab.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/AddPrefab.js deleted file mode 100644 index 6a0c3ff3..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/AddPrefab.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 from 'react' -import { Button } from '@patternfly/react-core' -import { SaveIcon } from '@patternfly/react-icons' - -function AddPrefab() { - const onClick = () => {} // TODO - return ( - <Button variant="primary" icon={<SaveIcon />} isBlock onClick={onClick} className="pf-u-mb-sm"> - Save this rack to a prefab - </Button> - ) -} - -AddPrefab.propTypes = { - tileId: PropTypes.string.isRequired, -} - -export default AddPrefab diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/DeleteRackContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/DeleteRackContainer.js deleted file mode 100644 index 0583a7a4..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/DeleteRackContainer.js +++ /dev/null @@ -1,60 +0,0 @@ -/* - * 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, { useState } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import TrashIcon from '@patternfly/react-icons/dist/js/icons/trash-icon' -import { Button } from '@patternfly/react-core' -import ConfirmationModal from '../../../util/modals/ConfirmationModal' -import { deleteRack } from '../../../../redux/actions/topology/rack' - -function DeleteRackContainer({ tileId }) { - const dispatch = useDispatch() - const [isVisible, setVisible] = useState(false) - const rackId = useSelector((state) => state.topology.tiles[tileId].rack) - const callback = (isConfirmed) => { - if (isConfirmed) { - dispatch(deleteRack(tileId, rackId)) - } - setVisible(false) - } - return ( - <> - <Button variant="danger" icon={<TrashIcon />} isBlock onClick={() => setVisible(true)}> - Delete this rack - </Button> - <ConfirmationModal - title="Delete this rack" - message="Are you sure you want to delete this rack?" - isOpen={isVisible} - callback={callback} - /> - </> - ) -} - -DeleteRackContainer.propTypes = { - tileId: PropTypes.string.isRequired, -} - -export default DeleteRackContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/MachineComponent.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/MachineComponent.js deleted file mode 100644 index b0a96a9f..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/MachineComponent.js +++ /dev/null @@ -1,40 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Flex, Label } from '@patternfly/react-core' -import { Machine } from '../../../../shapes' - -const UnitIcon = ({ id, type }) => ( - // eslint-disable-next-line @next/next/no-img-element - <img src={'/img/topology/' + id + '-icon.png'} alt={'Machine contains ' + type + ' units'} height={24} width={24} /> -) - -UnitIcon.propTypes = { - id: PropTypes.string, - type: PropTypes.string, -} - -function MachineComponent({ machine, onClick }) { - const hasNoUnits = - machine.cpus.length + machine.gpus.length + machine.memories.length + machine.storages.length === 0 - - return ( - <Flex onClick={() => onClick()}> - {machine.cpus.length > 0 ? <UnitIcon id="cpuModel" type="CPU" /> : undefined} - {machine.gpus.length > 0 ? <UnitIcon id="gpu" type="GPU" /> : undefined} - {machine.memories.length > 0 ? <UnitIcon id="memory" type="memory" /> : undefined} - {machine.storages.length > 0 ? <UnitIcon id="storage" type="storage" /> : undefined} - {hasNoUnits ? ( - <Label variant="outline" color="orange"> - Machine with no units - </Label> - ) : undefined} - </Flex> - ) -} - -MachineComponent.propTypes = { - machine: Machine.isRequired, - onClick: PropTypes.func, -} - -export default MachineComponent diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/MachineListComponent.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/MachineListComponent.js deleted file mode 100644 index 02c97730..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/MachineListComponent.js +++ /dev/null @@ -1,80 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import MachineComponent from './MachineComponent' -import { - Badge, - Button, - DataList, - DataListAction, - DataListCell, - DataListItem, - DataListItemCells, - DataListItemRow, -} from '@patternfly/react-core' -import { AngleRightIcon, PlusIcon } from '@patternfly/react-icons' -import { Machine } from '../../../../shapes' - -function MachineListComponent({ machines = [], onSelect, onAdd }) { - return ( - <DataList aria-label="Rack Units"> - {machines - .map((machine, index) => - machine ? ( - <DataListItem key={index} onClick={() => onSelect(index + 1)}> - <DataListItemRow> - <DataListItemCells - dataListCells={[ - <DataListCell isIcon key="icon"> - <Badge isRead>{index + 1}U</Badge> - </DataListCell>, - <DataListCell key="primary content"> - <MachineComponent onClick={() => onSelect(index + 1)} machine={machine} /> - </DataListCell>, - ]} - /> - <DataListAction id="goto" aria-label="Goto Machine" aria-labelledby="goto"> - <Button isSmall variant="plain" className="pf-u-p-0"> - <AngleRightIcon /> - </Button> - </DataListAction> - </DataListItemRow> - </DataListItem> - ) : ( - <DataListItem key={index}> - <DataListItemRow> - <DataListItemCells - dataListCells={[ - <DataListCell isIcon key="icon"> - <Badge isRead>{index + 1}U</Badge> - </DataListCell>, - <DataListCell key="add" className="text-secondary"> - Empty Slot - </DataListCell>, - ]} - /> - <DataListAction id="add" aria-label="Add Machine" aria-labelledby="add"> - <Button - isSmall - variant="plain" - className="pf-u-p-0" - onClick={() => onAdd(index + 1)} - > - <PlusIcon /> - </Button> - </DataListAction> - </DataListItemRow> - </DataListItem> - ) - ) - .reverse()} - </DataList> - ) -} - -MachineListComponent.propTypes = { - machines: PropTypes.arrayOf(Machine), - onSelect: PropTypes.func.isRequired, - onAdd: PropTypes.func.isRequired, -} - -export default MachineListComponent diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/MachineListContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/MachineListContainer.js deleted file mode 100644 index e1914730..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/MachineListContainer.js +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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, { useMemo } from 'react' -import { useDispatch, useSelector } from 'react-redux' -import MachineListComponent from './MachineListComponent' -import { goFromRackToMachine } from '../../../../redux/actions/interaction-level' -import { addMachine } from '../../../../redux/actions/topology/rack' - -function MachineListContainer({ tileId, ...props }) { - const rack = useSelector((state) => state.topology.racks[state.topology.tiles[tileId].rack]) - const machines = useSelector((state) => rack.machines.map((id) => state.topology.machines[id])) - const machinesNull = useMemo(() => { - const res = Array(rack.capacity).fill(null) - for (const machine of machines) { - res[machine.position - 1] = machine - } - return res - }, [rack, machines]) - const dispatch = useDispatch() - - return ( - <MachineListComponent - {...props} - machines={machinesNull} - onAdd={(index) => dispatch(addMachine(rack.id, index))} - onSelect={(index) => dispatch(goFromRackToMachine(index))} - /> - ) -} - -MachineListContainer.propTypes = { - tileId: PropTypes.string.isRequired, -} - -export default MachineListContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/RackNameContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/RackNameContainer.js deleted file mode 100644 index c3422318..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/RackNameContainer.js +++ /dev/null @@ -1,22 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import NameComponent from '../NameComponent' -import { editRackName } from '../../../../redux/actions/topology/rack' - -const RackNameContainer = ({ tileId }) => { - const { name: rackName, id } = useSelector((state) => state.topology.racks[state.topology.tiles[tileId].rack]) - const dispatch = useDispatch() - const callback = (name) => { - if (name) { - dispatch(editRackName(id, name)) - } - } - return <NameComponent name={rackName} onEdit={callback} /> -} - -RackNameContainer.propTypes = { - tileId: PropTypes.string.isRequired, -} - -export default RackNameContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/RackSidebar.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/RackSidebar.js deleted file mode 100644 index cb7d3b68..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/RackSidebar.js +++ /dev/null @@ -1,58 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { machineListContainer, sidebarContainer } from './RackSidebar.module.css' -import RackNameContainer from './RackNameContainer' -import AddPrefab from './AddPrefab' -import DeleteRackContainer from './DeleteRackContainer' -import MachineListContainer from './MachineListContainer' -import { - Skeleton, - TextContent, - TextList, - TextListItem, - TextListItemVariants, - TextListVariants, - Title, -} from '@patternfly/react-core' -import { useSelector } from 'react-redux' - -function RackSidebar({ tileId }) { - const rack = useSelector((state) => state.topology.racks[state.topology.tiles[tileId].rack]) - - return ( - <div className={sidebarContainer}> - <TextContent> - <Title headingLevel="h2">Details</Title> - <TextList component={TextListVariants.dl}> - <TextListItem - component={TextListItemVariants.dt} - className="pf-u-display-inline-flex pf-u-align-items-center" - > - Name - </TextListItem> - <TextListItem component={TextListItemVariants.dd}> - <RackNameContainer tileId={tileId} /> - </TextListItem> - <TextListItem component={TextListItemVariants.dt}>Capacity</TextListItem> - <TextListItem component={TextListItemVariants.dd}> - {rack?.capacity ?? <Skeleton screenreaderText="Loading rack" />} - </TextListItem> - </TextList> - <Title headingLevel="h2">Actions</Title> - <AddPrefab tileId={tileId} /> - <DeleteRackContainer tileId={tileId} /> - - <Title headingLevel="h2">Slots</Title> - </TextContent> - <div className={machineListContainer}> - <MachineListContainer tileId={tileId} /> - </div> - </div> - ) -} - -RackSidebar.propTypes = { - tileId: PropTypes.string.isRequired, -} - -export default RackSidebar diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/RackSidebar.module.css b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/RackSidebar.module.css deleted file mode 100644 index f4c8829f..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/rack/RackSidebar.module.css +++ /dev/null @@ -1,14 +0,0 @@ -.sidebarContainer { - display: flex; - flex-direction: column; - - height: 100%; -} - -.machineListContainer { - overflow-y: auto; - - flex: 1 0 300px; - - margin-top: 10px; -} diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/DeleteRoomContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/DeleteRoomContainer.js deleted file mode 100644 index 29b8f78a..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/DeleteRoomContainer.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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, { useState } from 'react' -import { useDispatch } from 'react-redux' -import ConfirmationModal from '../../../util/modals/ConfirmationModal' -import { deleteRoom } from '../../../../redux/actions/topology/room' -import TrashIcon from '@patternfly/react-icons/dist/js/icons/trash-icon' -import { Button } from '@patternfly/react-core' - -function DeleteRoomContainer({ roomId }) { - const dispatch = useDispatch() - const [isVisible, setVisible] = useState(false) - const callback = (isConfirmed) => { - if (isConfirmed) { - dispatch(deleteRoom(roomId)) - } - setVisible(false) - } - return ( - <> - <Button variant="danger" icon={<TrashIcon />} isBlock onClick={() => setVisible(true)}> - Delete this room - </Button> - <ConfirmationModal - title="Delete this room" - message="Are you sure you want to delete this room?" - isOpen={isVisible} - callback={callback} - /> - </> - ) -} - -DeleteRoomContainer.propTypes = { - roomId: PropTypes.string.isRequired, -} - -export default DeleteRoomContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/EditRoomContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/EditRoomContainer.js deleted file mode 100644 index 7a278cd6..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/EditRoomContainer.js +++ /dev/null @@ -1,61 +0,0 @@ -/* - * 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 from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { finishRoomEdit, startRoomEdit } from '../../../../redux/actions/topology/building' -import CheckIcon from '@patternfly/react-icons/dist/js/icons/check-icon' -import PencilAltIcon from '@patternfly/react-icons/dist/js/icons/pencil-alt-icon' -import { Button } from '@patternfly/react-core' - -function EditRoomContainer({ roomId }) { - const isEditing = useSelector((state) => state.construction.currentRoomInConstruction !== '-1') - const isInRackConstructionMode = useSelector((state) => state.construction.inRackConstructionMode) - - const dispatch = useDispatch() - const onEdit = () => dispatch(startRoomEdit(roomId)) - const onFinish = () => dispatch(finishRoomEdit()) - - return isEditing ? ( - <Button variant="tertiary" icon={<CheckIcon />} isBlock onClick={onFinish} className="pf-u-mb-sm"> - Finish editing room - </Button> - ) : ( - <Button - variant="tertiary" - icon={<PencilAltIcon />} - isBlock - disabled={isInRackConstructionMode} - onClick={() => (isInRackConstructionMode ? undefined : onEdit())} - className="pf-u-mb-sm" - > - Edit the tiles of this room - </Button> - ) -} - -EditRoomContainer.propTypes = { - roomId: PropTypes.string.isRequired, -} - -export default EditRoomContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/RackConstructionComponent.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/RackConstructionComponent.js deleted file mode 100644 index a384d5d5..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/RackConstructionComponent.js +++ /dev/null @@ -1,35 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import { Button } from '@patternfly/react-core' -import { PlusIcon, TimesIcon } from '@patternfly/react-icons' - -const RackConstructionComponent = ({ onStart, onStop, inRackConstructionMode, isEditingRoom }) => { - if (inRackConstructionMode) { - return ( - <Button isBlock={true} icon={<TimesIcon />} onClick={onStop} className="pf-u-mb-sm"> - Stop rack construction - </Button> - ) - } - - return ( - <Button - icon={<PlusIcon />} - isBlock - isDisabled={isEditingRoom} - onClick={() => (isEditingRoom ? undefined : onStart())} - className="pf-u-mb-sm" - > - Start rack construction - </Button> - ) -} - -RackConstructionComponent.propTypes = { - onStart: PropTypes.func, - onStop: PropTypes.func, - inRackConstructionMode: PropTypes.bool, - isEditingRoom: PropTypes.bool, -} - -export default RackConstructionComponent diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/RackConstructionContainer.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/RackConstructionContainer.js deleted file mode 100644 index e04287a5..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/RackConstructionContainer.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 React from 'react' -import { useDispatch, useSelector } from 'react-redux' -import { startRackConstruction, stopRackConstruction } from '../../../../redux/actions/topology/room' -import RackConstructionComponent from './RackConstructionComponent' - -function RackConstructionContainer(props) { - const isRackConstructionMode = useSelector((state) => state.construction.inRackConstructionMode) - const isEditingRoom = useSelector((state) => state.construction.currentRoomInConstruction !== '-1') - - const dispatch = useDispatch() - const onStart = () => dispatch(startRackConstruction()) - const onStop = () => dispatch(stopRackConstruction()) - return ( - <RackConstructionComponent - {...props} - inRackConstructionMode={isRackConstructionMode} - isEditingRoom={isEditingRoom} - onStart={onStart} - onStop={onStop} - /> - ) -} - -export default RackConstructionContainer diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/RoomName.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/RoomName.js deleted file mode 100644 index 72d45bea..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/RoomName.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 from 'react' -import { useDispatch, useSelector } from 'react-redux' -import NameComponent from '../NameComponent' -import { editRoomName } from '../../../../redux/actions/topology/room' - -function RoomName({ roomId }) { - const { name: roomName, id } = useSelector((state) => state.topology.rooms[roomId]) - const dispatch = useDispatch() - const callback = (name) => { - if (name) { - dispatch(editRoomName(id, name)) - } - } - return <NameComponent name={roomName} onEdit={callback} /> -} - -RoomName.propTypes = { - roomId: PropTypes.string.isRequired, -} - -export default RoomName diff --git a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/RoomSidebar.js b/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/RoomSidebar.js deleted file mode 100644 index 6ad489e0..00000000 --- a/opendc-web/opendc-web-ui/src/components/topologies/sidebar/room/RoomSidebar.js +++ /dev/null @@ -1,43 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import RoomName from './RoomName' -import RackConstructionContainer from './RackConstructionContainer' -import EditRoomContainer from './EditRoomContainer' -import DeleteRoomContainer from './DeleteRoomContainer' -import { - TextContent, - TextList, - TextListItem, - TextListItemVariants, - TextListVariants, - Title, -} from '@patternfly/react-core' - -const RoomSidebar = ({ roomId }) => { - return ( - <TextContent> - <Title headingLevel="h2">Details</Title> - <TextList component={TextListVariants.dl}> - <TextListItem - component={TextListItemVariants.dt} - className="pf-u-display-inline-flex pf-u-align-items-center" - > - Name - </TextListItem> - <TextListItem component={TextListItemVariants.dd}> - <RoomName roomId={roomId} /> - </TextListItem> - </TextList> - <Title headingLevel="h2">Construction</Title> - <RackConstructionContainer /> - <EditRoomContainer roomId={roomId} /> - <DeleteRoomContainer roomId={roomId} /> - </TextContent> - ) -} - -RoomSidebar.propTypes = { - roomId: PropTypes.string.isRequired, -} - -export default RoomSidebar diff --git a/opendc-web/opendc-web-ui/src/components/util/TableEmptyState.js b/opendc-web/opendc-web-ui/src/components/util/TableEmptyState.js deleted file mode 100644 index 9d16ffbb..00000000 --- a/opendc-web/opendc-web-ui/src/components/util/TableEmptyState.js +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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 { Bullseye, EmptyState, EmptyStateBody, EmptyStateIcon, Spinner, Title } from '@patternfly/react-core' -import { SearchIcon, CubesIcon } from '@patternfly/react-icons' -import { Status } from '../../shapes' - -function TableEmptyState({ - status, - isFiltering, - loadingTitle = 'Loading', - emptyTitle = 'No results found', - emptyText = 'No results found of this type.', - emptyAction = '', -}) { - if (status === 'loading') { - return ( - <Bullseye> - <EmptyState variant="xs"> - <EmptyStateIcon variant="container" component={Spinner} /> - <Title headingLevel="h4" size="md"> - {loadingTitle} - </Title> - </EmptyState> - </Bullseye> - ) - } else if (status === 'error') { - return ( - <EmptyState variant="xs"> - <Title headingLevel="h4" size="md"> - Unable to connect - </Title> - <EmptyStateBody> - There was an error retrieving data. Check your connection and try again. - </EmptyStateBody> - </EmptyState> - ) - } else if (status === 'idle') { - return ( - <EmptyState variant="xs"> - <EmptyStateIcon icon={CubesIcon} /> - <Title headingLevel="h4" size="md"> - {emptyTitle} - </Title> - <EmptyStateBody>No results available at this moment.</EmptyStateBody> - </EmptyState> - ) - } else if (isFiltering) { - return ( - <EmptyState variant="xs"> - <EmptyStateIcon icon={SearchIcon} /> - <Title headingLevel="h4" size="md"> - No results found - </Title> - <EmptyStateBody> - No results match this filter criteria. Remove all filters or clear all filters to show results. - </EmptyStateBody> - </EmptyState> - ) - } - - return ( - <EmptyState variant="xs"> - <EmptyStateIcon icon={CubesIcon} /> - <Title headingLevel="h4" size="md"> - {emptyTitle} - </Title> - <EmptyStateBody>{emptyText}</EmptyStateBody> - {emptyAction} - </EmptyState> - ) -} - -TableEmptyState.propTypes = { - status: Status.isRequired, - isFiltering: PropTypes.bool, - loadingTitle: PropTypes.string, - emptyTitle: PropTypes.string, - emptyText: PropTypes.string, - emptyAction: PropTypes.node, -} - -export default TableEmptyState diff --git a/opendc-web/opendc-web-ui/src/components/util/modals/ConfirmationModal.js b/opendc-web/opendc-web-ui/src/components/util/modals/ConfirmationModal.js deleted file mode 100644 index f6e1c98b..00000000 --- a/opendc-web/opendc-web-ui/src/components/util/modals/ConfirmationModal.js +++ /dev/null @@ -1,27 +0,0 @@ -import PropTypes from 'prop-types' -import React from 'react' -import Modal from './Modal' - -function ConfirmationModal({ title, message, isOpen, callback }) { - return ( - <Modal - title={title} - isOpen={isOpen} - onSubmit={() => callback(true)} - onCancel={() => callback(false)} - submitButtonType="danger" - submitButtonText="Confirm" - > - {message} - </Modal> - ) -} - -ConfirmationModal.propTypes = { - title: PropTypes.string.isRequired, - message: PropTypes.string.isRequired, - isOpen: PropTypes.bool.isRequired, - callback: PropTypes.func.isRequired, -} - -export default ConfirmationModal diff --git a/opendc-web/opendc-web-ui/src/components/util/modals/Modal.js b/opendc-web/opendc-web-ui/src/components/util/modals/Modal.js deleted file mode 100644 index d4577062..00000000 --- a/opendc-web/opendc-web-ui/src/components/util/modals/Modal.js +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { Button, Modal as PModal, ModalVariant } from '@patternfly/react-core' - -function Modal({ children, title, isOpen, onSubmit, onCancel, submitButtonType, submitButtonText }) { - const actions = [ - <Button variant={submitButtonType} onClick={onSubmit} key="confirm"> - {submitButtonText} - </Button>, - <Button variant="link" onClick={onCancel} key="cancel"> - Cancel - </Button>, - ] - - return ( - <PModal variant={ModalVariant.small} isOpen={isOpen} onClose={onCancel} title={title} actions={actions}> - {children} - </PModal> - ) -} - -Modal.propTypes = { - title: PropTypes.string.isRequired, - isOpen: PropTypes.bool, - onSubmit: PropTypes.func.isRequired, - onCancel: PropTypes.func.isRequired, - submitButtonType: PropTypes.string, - submitButtonText: PropTypes.string, - children: PropTypes.node, -} - -Modal.defaultProps = { - submitButtonType: 'primary', - submitButtonText: 'Save', - isOpen: false, -} - -export default Modal diff --git a/opendc-web/opendc-web-ui/src/components/util/modals/TextInputModal.js b/opendc-web/opendc-web-ui/src/components/util/modals/TextInputModal.js deleted file mode 100644 index 392a729e..00000000 --- a/opendc-web/opendc-web-ui/src/components/util/modals/TextInputModal.js +++ /dev/null @@ -1,70 +0,0 @@ -import PropTypes from 'prop-types' -import React, { useRef, useState } from 'react' -import Modal from './Modal' -import { Form, FormGroup, TextInput } from '@patternfly/react-core' - -function TextInputModal({ title, label, isOpen, callback, initialValue }) { - const textInput = useRef(null) - const [isSubmitted, setSubmitted] = useState(false) - const [isValid, setValid] = useState(true) - - const resetState = () => { - textInput.current.value = '' - setSubmitted(false) - setValid(false) - } - const onSubmit = (event) => { - const value = textInput.current.value - setSubmitted(true) - - if (event) { - event.preventDefault() - } - - if (!value) { - setValid(false) - return false - } - - callback(value) - resetState() - return true - } - const onCancel = () => { - callback(undefined) - resetState() - } - - return ( - <Modal title={title} isOpen={isOpen} onSubmit={onSubmit} onCancel={onCancel}> - <Form onSubmit={onSubmit}> - <FormGroup - label={label} - fieldId="text-input" - isRequired - validated={isSubmitted && !isValid ? 'error' : 'default'} - helperTextInvalid="This field cannot be empty" - > - <TextInput - id="text-input" - name="text-input" - isRequired - type="text" - ref={textInput} - defaultValue={initialValue} - /> - </FormGroup> - </Form> - </Modal> - ) -} - -TextInputModal.propTypes = { - title: PropTypes.string.isRequired, - label: PropTypes.string.isRequired, - isOpen: PropTypes.bool.isRequired, - callback: PropTypes.func.isRequired, - initialValue: PropTypes.string, -} - -export default TextInputModal diff --git a/opendc-web/opendc-web-ui/src/config.js b/opendc-web/opendc-web-ui/src/config.js deleted file mode 100644 index 52952d69..00000000 --- a/opendc-web/opendc-web-ui/src/config.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2022 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. - */ - -/** - * URL to OpenDC API. - */ -export const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL - -/** - * Authentication configuration. - */ -export const auth = { - domain: process.env.NEXT_PUBLIC_AUTH0_DOMAIN, - clientId: process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID, - audience: process.env.NEXT_PUBLIC_AUTH0_AUDIENCE, - redirectUri: global.window && global.window.location.origin, -} - -/** - * Sentry DSN for web frontend. - */ -export const sentryDsn = process.env.NEXT_PUBLIC_SENTRY_DSN diff --git a/opendc-web/opendc-web-ui/src/data/experiments.js b/opendc-web/opendc-web-ui/src/data/experiments.js deleted file mode 100644 index ca8912a2..00000000 --- a/opendc-web/opendc-web-ui/src/data/experiments.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 { useQuery } from 'react-query' -import { fetchTraces } from '../api/traces' -import { fetchSchedulers } from '../api/schedulers' - -/** - * Configure the query defaults for the experiment endpoints. - */ -export function configureExperimentClient(queryClient, auth) { - queryClient.setQueryDefaults('traces', { queryFn: () => fetchTraces(auth) }) - queryClient.setQueryDefaults('schedulers', { queryFn: () => fetchSchedulers(auth) }) -} - -/** - * Return the available traces to experiment with. - */ -export function useTraces(options) { - return useQuery('traces', options) -} - -/** - * Return the available schedulers to experiment with. - */ -export function useSchedulers(options) { - return useQuery('schedulers', options) -} diff --git a/opendc-web/opendc-web-ui/src/data/project.js b/opendc-web/opendc-web-ui/src/data/project.js deleted file mode 100644 index 60a8fab6..00000000 --- a/opendc-web/opendc-web-ui/src/data/project.js +++ /dev/null @@ -1,166 +0,0 @@ -/* - * 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 { useQuery, useMutation } from 'react-query' -import { addProject, deleteProject, fetchProject, fetchProjects } from '../api/projects' -import { addPortfolio, deletePortfolio, fetchPortfolio, fetchPortfolios } from '../api/portfolios' -import { addScenario, deleteScenario, fetchScenario } from '../api/scenarios' - -/** - * Configure the query defaults for the project endpoints. - */ -export function configureProjectClient(queryClient, auth) { - queryClient.setQueryDefaults('projects', { - queryFn: ({ queryKey }) => (queryKey.length === 1 ? fetchProjects(auth) : fetchProject(auth, queryKey[1])), - }) - - queryClient.setMutationDefaults('addProject', { - mutationFn: (data) => addProject(auth, data), - onSuccess: async (result) => { - queryClient.setQueryData('projects', (old = []) => [...old, result]) - queryClient.setQueryData(['projects', result.id], result) - }, - }) - queryClient.setMutationDefaults('deleteProject', { - mutationFn: (id) => deleteProject(auth, id), - onSuccess: async (result) => { - queryClient.setQueryData('projects', (old = []) => old.filter((project) => project.id !== result.id)) - queryClient.removeQueries(['projects', result.id]) - }, - }) - - queryClient.setQueryDefaults('portfolios', { - queryFn: ({ queryKey }) => - queryKey.length === 2 ? fetchPortfolios(auth, queryKey[1]) : fetchPortfolio(auth, queryKey[1], queryKey[2]), - }) - queryClient.setMutationDefaults('addPortfolio', { - mutationFn: ({ projectId, ...data }) => addPortfolio(auth, projectId, data), - onSuccess: async (result) => { - queryClient.setQueryData(['portfolios', result.project.id], (old = []) => [...old, result]) - queryClient.setQueryData(['portfolios', result.project.id, result.number], result) - }, - }) - queryClient.setMutationDefaults('deletePortfolio', { - mutationFn: ({ projectId, number }) => deletePortfolio(auth, projectId, number), - onSuccess: async (result) => { - queryClient.setQueryData(['portfolios', result.project.id], (old = []) => - old.filter((portfolio) => portfolio.id !== result.id) - ) - queryClient.removeQueries(['portfolios', result.project.id, result.number]) - }, - }) - - queryClient.setQueryDefaults('scenarios', { - queryFn: ({ queryKey }) => fetchScenario(auth, queryKey[1], queryKey[2]), - }) - queryClient.setMutationDefaults('addScenario', { - mutationFn: ({ projectId, portfolioNumber, data }) => addScenario(auth, projectId, portfolioNumber, data), - onSuccess: async (result) => { - // Register updated scenario in cache - queryClient.setQueryData(['scenarios', result.project.id, result.id], result) - queryClient.setQueryData(['portfolios', result.project.id, result.portfolio.number], (old) => ({ - ...old, - scenarios: [...old.scenarios, result], - })) - }, - }) - queryClient.setMutationDefaults('deleteScenario', { - mutationFn: ({ projectId, number }) => deleteScenario(auth, projectId, number), - onSuccess: async (result) => { - queryClient.removeQueries(['scenarios', result.project.id, result.id]) - queryClient.setQueryData(['portfolios', result.project.id, result.portfolio.number], (old) => ({ - ...old, - scenarios: old?.scenarios?.filter((scenario) => scenario.id !== result.id), - })) - }, - }) -} - -/** - * Return the available projects. - */ -export function useProjects(options = {}) { - return useQuery('projects', options) -} - -/** - * Return the project with the specified identifier. - */ -export function useProject(projectId, options = {}) { - return useQuery(['projects', projectId], { enabled: !!projectId, ...options }) -} - -/** - * Create a mutation for a new project. - */ -export function useNewProject() { - return useMutation('addProject') -} - -/** - * Create a mutation for deleting a project. - */ -export function useDeleteProject() { - return useMutation('deleteProject') -} - -/** - * Return the portfolio with the specified identifier. - */ -export function usePortfolio(projectId, portfolioId, options = {}) { - return useQuery(['portfolios', projectId, portfolioId], { enabled: !!(projectId && portfolioId), ...options }) -} - -/** - * Return the portfolios of the specified project. - */ -export function usePortfolios(projectId, options = {}) { - return useQuery(['portfolios', projectId], { enabled: !!projectId, ...options }) -} - -/** - * Create a mutation for a new portfolio. - */ -export function useNewPortfolio() { - return useMutation('addPortfolio') -} - -/** - * Create a mutation for deleting a portfolio. - */ -export function useDeletePortfolio() { - return useMutation('deletePortfolio') -} - -/** - * Create a mutation for a new scenario. - */ -export function useNewScenario() { - return useMutation('addScenario') -} - -/** - * Create a mutation for deleting a scenario. - */ -export function useDeleteScenario() { - return useMutation('deleteScenario') -} diff --git a/opendc-web/opendc-web-ui/src/data/query.js b/opendc-web/opendc-web-ui/src/data/query.js deleted file mode 100644 index 3e5423b9..00000000 --- a/opendc-web/opendc-web-ui/src/data/query.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - * 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 { useMemo } from 'react' -import { QueryClient } from 'react-query' -import { useAuth } from '../auth' -import { configureExperimentClient } from './experiments' -import { configureProjectClient } from './project' -import { configureTopologyClient } from './topology' -import { configureUserClient } from './user' - -let queryClient - -function createQueryClient(auth) { - const client = new QueryClient() - configureProjectClient(client, auth) - configureExperimentClient(client, auth) - configureTopologyClient(client, auth) - configureUserClient(client, auth) - return client -} - -function initializeQueryClient(auth) { - const _queryClient = queryClient ?? createQueryClient(auth) - - // For SSG and SSR always create a new query client - if (typeof window === 'undefined') return _queryClient - // Create the query client once in the client - if (!queryClient) queryClient = _queryClient - - return _queryClient -} - -/** - * Obtain a cached query client. - */ -export function useNewQueryClient() { - const auth = useAuth() - return useMemo(() => initializeQueryClient(auth), []) // eslint-disable-line react-hooks/exhaustive-deps -} diff --git a/opendc-web/opendc-web-ui/src/data/topology.js b/opendc-web/opendc-web-ui/src/data/topology.js deleted file mode 100644 index d5e624d5..00000000 --- a/opendc-web/opendc-web-ui/src/data/topology.js +++ /dev/null @@ -1,88 +0,0 @@ -/* - * 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 { useQuery, useMutation } from 'react-query' -import { addTopology, deleteTopology, fetchTopologies, fetchTopology, updateTopology } from '../api/topologies' - -/** - * Configure the query defaults for the topology endpoints. - */ -export function configureTopologyClient(queryClient, auth) { - queryClient.setQueryDefaults('topologies', { - queryFn: ({ queryKey }) => - queryKey.length === 2 ? fetchTopologies(auth, queryKey[1]) : fetchTopology(auth, queryKey[1], queryKey[2]), - }) - - queryClient.setMutationDefaults('addTopology', { - mutationFn: ({ projectId, ...data }) => addTopology(auth, projectId, data), - onSuccess: (result) => { - queryClient.setQueryData(['topologies', result.project.id], (old = []) => [...old, result]) - queryClient.setQueryData(['topologies', result.project.id, result.number], result) - }, - }) - queryClient.setMutationDefaults('updateTopology', { - mutationFn: (data) => updateTopology(auth, data), - onSuccess: (result) => { - queryClient.setQueryData(['topologies', result.project.id], (old = []) => - old.map((topology) => (topology.id === result.id ? result : topology)) - ) - queryClient.setQueryData(['topologies', result.project.id, result.number], result) - }, - }) - queryClient.setMutationDefaults('deleteTopology', { - mutationFn: ({ projectId, number }) => deleteTopology(auth, projectId, number), - onSuccess: (result) => { - queryClient.setQueryData(['topologies', result.project.id], (old = []) => - old.filter((topology) => topology.id !== result.id) - ) - queryClient.removeQueries(['topologies', result.project.id, result.number]) - }, - }) -} - -/** - * Fetch the topology with the specified identifier for the specified project. - */ -export function useTopology(projectId, topologyId, options = {}) { - return useQuery(['topologies', projectId, topologyId], { enabled: !!(projectId && topologyId), ...options }) -} - -/** - * Fetch all topologies of the specified project. - */ -export function useTopologies(projectId, options = {}) { - return useQuery(['topologies', projectId], { enabled: !!projectId, ...options }) -} - -/** - * Create a mutation for a new topology. - */ -export function useNewTopology() { - return useMutation('addTopology') -} - -/** - * Create a mutation for deleting a topology. - */ -export function useDeleteTopology(options = {}) { - return useMutation('deleteTopology', options) -} diff --git a/opendc-web/opendc-web-ui/src/data/user.js b/opendc-web/opendc-web-ui/src/data/user.js deleted file mode 100644 index 97c0e1e2..00000000 --- a/opendc-web/opendc-web-ui/src/data/user.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2022 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 { useQuery } from 'react-query' -import { fetchUser } from '../api/users' - -/** - * Configure the query defaults for the user client. - */ -export function configureUserClient(queryClient, auth) { - queryClient.setQueryDefaults('user', { - queryFn: () => fetchUser(auth), - }) -} - -/** - * Fetch the user data on the server. - */ -export default function useUser(options = {}) { - return useQuery('user', options) -} diff --git a/opendc-web/opendc-web-ui/src/pages/404.js b/opendc-web/opendc-web-ui/src/pages/404.js deleted file mode 100644 index 0939bc56..00000000 --- a/opendc-web/opendc-web-ui/src/pages/404.js +++ /dev/null @@ -1,38 +0,0 @@ -import React from 'react' -import Head from 'next/head' -import { AppPage } from '../components/AppPage' -import { - Bullseye, - EmptyState, - EmptyStateBody, - EmptyStateIcon, - PageSection, - PageSectionVariants, - Title, -} from '@patternfly/react-core' -import { UnknownIcon } from '@patternfly/react-icons' - -const NotFound = () => { - return ( - <AppPage> - <Head> - <title>Page Not Found - OpenDC</title> - </Head> - <PageSection variant={PageSectionVariants.light}> - <Bullseye> - <EmptyState> - <EmptyStateIcon variant="container" component={UnknownIcon} /> - <Title size="lg" headingLevel="h4"> - 404: That page does not exist - </Title> - <EmptyStateBody> - The requested page is not found. Try refreshing the page if it was recently added. - </EmptyStateBody> - </EmptyState> - </Bullseye> - </PageSection> - </AppPage> - ) -} - -export default NotFound 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 62ce0539..00000000 --- a/opendc-web/opendc-web-ui/src/pages/_app.js +++ /dev/null @@ -1,108 +0,0 @@ -/* - * 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 Head from 'next/head' -import Script from 'next/script' -import { Provider } from 'react-redux' -import { useNewQueryClient } from '../data/query' -import { useStore } from '../redux' -import { AuthProvider, useRequireAuth } from '../auth' -import * as Sentry from '@sentry/react' -import { Integrations } from '@sentry/tracing' -import { QueryClientProvider } from 'react-query' -import { sentryDsn } from '../config' - -import '@patternfly/react-core/dist/styles/base.css' -import '@patternfly/react-styles/css/utilities/Alignment/alignment.css' -import '@patternfly/react-styles/css/utilities/BackgroundColor/BackgroundColor.css' -import '@patternfly/react-styles/css/utilities/BoxShadow/box-shadow.css' -import '@patternfly/react-styles/css/utilities/Display/display.css' -import '@patternfly/react-styles/css/utilities/Flex/flex.css' -import '@patternfly/react-styles/css/utilities/Float/float.css' -import '@patternfly/react-styles/css/utilities/Sizing/sizing.css' -import '@patternfly/react-styles/css/utilities/Spacing/spacing.css' -import '@patternfly/react-styles/css/utilities/Text/text.css' -import '@patternfly/react-styles/css/components/InlineEdit/inline-edit.css' -import '../style/index.css' - -// This setup is necessary to forward the Auth0 context to the Redux context -function Inner({ Component, pageProps }) { - // Force user to be authorized - useRequireAuth() - - const queryClient = useNewQueryClient() - const store = useStore(pageProps.initialReduxState, { queryClient }) - return ( - <QueryClientProvider client={queryClient}> - <Provider store={store}> - <Component {...pageProps} /> - </Provider> - </QueryClientProvider> - ) -} - -Inner.propTypes = { - Component: PropTypes.func, - pageProps: PropTypes.shape({ - initialReduxState: PropTypes.object, - }).isRequired, -} - -// Initialize Sentry if the user has configured a DSN -if (process.browser && sentryDsn) { - Sentry.init({ - environment: process.env.NODE_ENV, - dsn: sentryDsn, - integrations: [new Integrations.BrowserTracing()], - tracesSampleRate: 0.1, - }) -} - -export default function App(props) { - return ( - <> - <Head> - <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> - <meta name="theme-color" content="#00A6D6" /> - </Head> - <Sentry.ErrorBoundary fallback={'An error has occurred'}> - <AuthProvider> - <Inner {...props} /> - </AuthProvider> - </Sentry.ErrorBoundary> - {/* Google Analytics */} - <Script async src="https://www.googletagmanager.com/gtag/js?id=UA-84285092-3" /> - <Script - id="gtag" - dangerouslySetInnerHTML={{ - __html: ` - window.dataLayer = window.dataLayer || []; - function gtag(){dataLayer.push(arguments);} - gtag('js', new Date()); - gtag('config', 'UA-84285092-3'); - `, - }} - /> - </> - ) -} diff --git a/opendc-web/opendc-web-ui/src/pages/_document.js b/opendc-web/opendc-web-ui/src/pages/_document.js deleted file mode 100644 index 9f84b2ab..00000000 --- a/opendc-web/opendc-web-ui/src/pages/_document.js +++ /dev/null @@ -1,76 +0,0 @@ -/* - * 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 lang="en"> - <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" /> - </Head> - <body> - <Main /> - <NextScript /> - </body> - </Html> - ) - } -} - -export default OpenDCDocument diff --git a/opendc-web/opendc-web-ui/src/pages/logout.js b/opendc-web/opendc-web-ui/src/pages/logout.js deleted file mode 100644 index 38d5968e..00000000 --- a/opendc-web/opendc-web-ui/src/pages/logout.js +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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 React from 'react' -import Head from 'next/head' -import { AppPage } from '../components/AppPage' -import { PageSection, PageSectionVariants } from '@patternfly/react-core' - -function Logout() { - return ( - <AppPage> - <Head> - <title>Logged Out - OpenDC</title> - </Head> - <PageSection variant={PageSectionVariants.light}>Logged out successfully</PageSection> - </AppPage> - ) -} - -export default Logout 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 deleted file mode 100644 index 52938bcd..00000000 --- a/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js +++ /dev/null @@ -1,75 +0,0 @@ -/* - * 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 ProjectOverview from '../../../components/projects/ProjectOverview' -import { useProject } from '../../../data/project' -import { AppPage } from '../../../components/AppPage' -import Head from 'next/head' -import Link from 'next/link' -import { - Breadcrumb, - BreadcrumbItem, - PageSection, - PageSectionVariants, - Skeleton, - Text, - TextContent, -} from '@patternfly/react-core' - -function Project() { - const router = useRouter() - const projectId = +router.query['project'] - - const { data: project } = useProject(+projectId) - - const breadcrumb = ( - <Breadcrumb> - <BreadcrumbItem to="/projects" component={Link}> - Projects - </BreadcrumbItem> - <BreadcrumbItem to={`/projects/${projectId}`} component={Link} isActive> - Project details - </BreadcrumbItem> - </Breadcrumb> - ) - - return ( - <AppPage breadcrumb={breadcrumb}> - <Head> - <title>{`${project?.name ?? 'Project'} - OpenDC`}</title> - </Head> - <PageSection variant={PageSectionVariants.light}> - <TextContent> - <Text component="h1"> - {project?.name ?? <Skeleton width="15%" screenreaderText="Loading project" />} - </Text> - </TextContent> - </PageSection> - <PageSection isFilled> - <ProjectOverview projectId={projectId} /> - </PageSection> - </AppPage> - ) -} - -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 deleted file mode 100644 index 5d1e041b..00000000 --- a/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js +++ /dev/null @@ -1,121 +0,0 @@ -/* - * 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 dynamic from 'next/dynamic' -import { useRouter } from 'next/router' -import Head from 'next/head' -import Link from 'next/link' -import React, { useRef } from 'react' -import { - Breadcrumb, - BreadcrumbItem, - Divider, - PageSection, - PageSectionVariants, - Tab, - TabContent, - Tabs, - TabTitleText, - Text, - TextContent, -} from '@patternfly/react-core' -import { AppPage } from '../../../../components/AppPage' -import ContextSelectionSection from '../../../../components/context/ContextSelectionSection' -import PortfolioSelector from '../../../../components/context/PortfolioSelector' -import PortfolioOverview from '../../../../components/portfolios/PortfolioOverview' -import { usePortfolio } from '../../../../data/project' - -const PortfolioResults = dynamic(() => import('../../../../components/portfolios/PortfolioResults'), { ssr: false }) - -/** - * Page that displays the results in a portfolio. - */ -function Portfolio() { - const router = useRouter() - const projectId = +router.query['project'] - const portfolioNumber = +router.query['portfolio'] - - const overviewRef = useRef(null) - const resultsRef = useRef(null) - - const { data: portfolio } = usePortfolio(projectId, portfolioNumber) - - const breadcrumb = ( - <Breadcrumb> - <BreadcrumbItem to="/projects" component={Link}> - Projects - </BreadcrumbItem> - <BreadcrumbItem to={`/projects/${projectId}`} component={Link}> - Project details - </BreadcrumbItem> - <BreadcrumbItem to={`/projects/${projectId}/portfolios/${portfolioNumber}`} component={Link} isActive> - Portfolio - </BreadcrumbItem> - </Breadcrumb> - ) - - const contextSelectors = ( - <ContextSelectionSection> - <PortfolioSelector activePortfolio={portfolio} /> - </ContextSelectionSection> - ) - - return ( - <AppPage breadcrumb={breadcrumb} contextSelectors={contextSelectors}> - <Head> - <title>Portfolio - OpenDC</title> - </Head> - <PageSection variant={PageSectionVariants.light}> - <TextContent> - <Text component="h1">Portfolio</Text> - </TextContent> - </PageSection> - <PageSection type="tabs" variant={PageSectionVariants.light} stickyOnBreakpoint={{ default: 'top' }}> - <Divider component="div" /> - <Tabs defaultActiveKey={0} className="pf-m-page-insets"> - <Tab - eventKey={0} - title={<TabTitleText>Overview</TabTitleText>} - tabContentId="overview" - tabContentRef={overviewRef} - /> - <Tab - eventKey={1} - title={<TabTitleText>Results</TabTitleText>} - tabContentId="results" - tabContentRef={resultsRef} - /> - </Tabs> - </PageSection> - <PageSection isFilled> - <TabContent eventKey={0} id="overview" ref={overviewRef} aria-label="Overview tab"> - <PortfolioOverview projectId={projectId} portfolioId={portfolioNumber} /> - </TabContent> - <TabContent eventKey={1} id="results" ref={resultsRef} aria-label="Results tab" hidden> - <PortfolioResults projectId={projectId} portfolioId={portfolioNumber} /> - </TabContent> - </PageSection> - </AppPage> - ) -} - -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 deleted file mode 100644 index 48359365..00000000 --- a/opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js +++ /dev/null @@ -1,142 +0,0 @@ -/* - * 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 dynamic from 'next/dynamic' -import { useRouter } from 'next/router' -import Head from 'next/head' -import Link from 'next/link' -import ContextSelectionSection from '../../../../components/context/ContextSelectionSection' -import TopologySelector from '../../../../components/context/TopologySelector' -import TopologyOverview from '../../../../components/topologies/TopologyOverview' -import { useDispatch } from 'react-redux' -import React, { useEffect, useState } from 'react' -import { AppPage } from '../../../../components/AppPage' -import { - Breadcrumb, - BreadcrumbItem, - Divider, - PageSection, - PageSectionVariants, - Tab, - TabContent, - Tabs, - TabTitleText, - Text, - TextContent, -} from '@patternfly/react-core' -import { useTopology } from '../../../../data/topology' -import { goToRoom } from '../../../../redux/actions/interaction-level' -import { openTopology } from '../../../../redux/actions/topology' - -const TopologyMap = dynamic(() => import('../../../../components/topologies/TopologyMap'), { ssr: false }) - -/** - * Page that displays a datacenter topology. - */ -function Topology() { - const router = useRouter() - const projectId = +router.query['project'] - const topologyNumber = +router.query['topology'] - - const { data: topology } = useTopology(projectId, topologyNumber) - - const dispatch = useDispatch() - useEffect(() => { - if (topologyNumber) { - dispatch(openTopology(projectId, topologyNumber)) - } - }, [projectId, topologyNumber, dispatch]) - - const [activeTab, setActiveTab] = useState('overview') - - const breadcrumb = ( - <Breadcrumb> - <BreadcrumbItem to="/projects" component={Link}> - Projects - </BreadcrumbItem> - <BreadcrumbItem to={`/projects/${projectId}`} component={Link}> - Project details - </BreadcrumbItem> - <BreadcrumbItem to={`/projects/${projectId}/topologies/${topologyNumber}`} component={Link} isActive> - Topology - </BreadcrumbItem> - </Breadcrumb> - ) - - const contextSelectors = ( - <ContextSelectionSection> - <TopologySelector activeTopology={topology} /> - </ContextSelectionSection> - ) - - return ( - <AppPage breadcrumb={breadcrumb} contextSelectors={contextSelectors}> - <Head> - <title>{`${topology?.name ?? 'Topologies'} - OpenDC`}</title> - </Head> - <PageSection variant={PageSectionVariants.light}> - <TextContent> - <Text component="h1">Topology</Text> - </TextContent> - </PageSection> - <PageSection type="none" variant={PageSectionVariants.light} className="pf-c-page__main-tabs" sticky="top"> - <Divider component="div" /> - <Tabs - activeKey={activeTab} - onSelect={(_, tabIndex) => setActiveTab(tabIndex)} - className="pf-m-page-insets" - > - <Tab eventKey="overview" title={<TabTitleText>Overview</TabTitleText>} tabContentId="overview" /> - <Tab - eventKey="floor-plan" - title={<TabTitleText>Floor Plan</TabTitleText>} - tabContentId="floor-plan" - /> - </Tabs> - </PageSection> - <PageSection padding={activeTab === 'floor-plan' && { default: 'noPadding' }} isFilled> - <TabContent id="overview" aria-label="Overview tab" hidden={activeTab !== 'overview'}> - <TopologyOverview - projectId={projectId} - topologyNumber={topologyNumber} - onSelect={(type, obj) => { - if (type === 'room') { - dispatch(goToRoom(obj.id)) - setActiveTab('floor-plan') - } - }} - /> - </TabContent> - <TabContent - id="floor-plan" - aria-label="Floor Plan tab" - className="pf-u-h-100" - hidden={activeTab !== 'floor-plan'} - > - <TopologyMap /> - </TabContent> - </PageSection> - </AppPage> - ) -} - -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 deleted file mode 100644 index 97ff105c..00000000 --- a/opendc-web/opendc-web-ui/src/pages/projects/index.js +++ /dev/null @@ -1,116 +0,0 @@ -/* - * 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 { PlusIcon } from '@patternfly/react-icons' -import React, { useMemo, useState } from 'react' -import Head from 'next/head' -import ProjectFilterPanel from '../../components/projects/FilterPanel' -import { AppPage } from '../../components/AppPage' -import { - PageSection, - PageSectionVariants, - Title, - Toolbar, - ToolbarItem, - ToolbarContent, - Button, - TextContent, - Text, -} from '@patternfly/react-core' -import ProjectCollection from '../../components/projects/ProjectCollection' -import TextInputModal from '../../components/util/modals/TextInputModal' -import { useProjects, useDeleteProject, useNewProject } from '../../data/project' - -const getVisibleProjects = (projects, filter) => { - switch (filter) { - case 'SHOW_ALL': - return projects - case 'SHOW_OWN': - return projects.filter((project) => project.role === 'OWNER') - case 'SHOW_SHARED': - return projects.filter((project) => project.role !== 'OWNER') - default: - return projects - } -} - -function Projects() { - const { status, data: projects } = useProjects() - const [filter, setFilter] = useState('SHOW_ALL') - const visibleProjects = useMemo(() => getVisibleProjects(projects ?? [], filter), [projects, filter]) - - const { mutate: deleteProject } = useDeleteProject() - const { mutate: addProject } = useNewProject() - - const [isProjectCreationModalVisible, setProjectCreationModalVisible] = useState(false) - const onProjectCreation = (name) => { - if (name) { - addProject({ name }) - } - setProjectCreationModalVisible(false) - } - - return ( - <AppPage> - <Head> - <title>My Projects - OpenDC</title> - </Head> - <PageSection variant={PageSectionVariants.light} isFilled> - <div className="pf-u-mx-auto pf-u-max-width" style={{ '--pf-u-max-width--MaxWidth': '100ch' }}> - <Title className="pf-u-mt-xl-on-md" headingLevel="h1" size="4xl"> - Welcome - </Title> - <TextContent> - <Text component="p">Find all your personal and shared projects</Text> - </TextContent> - <Toolbar inset={{ default: 'insetNone' }}> - <ToolbarContent> - <ToolbarItem> - <ProjectFilterPanel onSelect={setFilter} activeFilter={filter} /> - </ToolbarItem> - <ToolbarItem alignment={{ default: 'alignRight' }}> - <Button icon={<PlusIcon />} onClick={() => setProjectCreationModalVisible(true)}> - Create Project - </Button> - </ToolbarItem> - </ToolbarContent> - </Toolbar> - <ProjectCollection - status={status} - isFiltering={filter !== 'SHOW_ALL'} - projects={visibleProjects} - onDelete={(project) => deleteProject(project.id)} - onCreate={() => setProjectCreationModalVisible(true)} - /> - <TextInputModal - title="New Project" - label="Project name" - isOpen={isProjectCreationModalVisible} - callback={onProjectCreation} - /> - </div> - </PageSection> - </AppPage> - ) -} - -export default Projects diff --git a/opendc-web/opendc-web-ui/src/redux/actions/interaction-level.js b/opendc-web/opendc-web-ui/src/redux/actions/interaction-level.js deleted file mode 100644 index 8381eeef..00000000 --- a/opendc-web/opendc-web-ui/src/redux/actions/interaction-level.js +++ /dev/null @@ -1,57 +0,0 @@ -export const GO_FROM_BUILDING_TO_ROOM = 'GO_FROM_BUILDING_TO_ROOM' -export const GO_FROM_ROOM_TO_RACK = 'GO_FROM_ROOM_TO_RACK' -export const GO_FROM_RACK_TO_MACHINE = 'GO_FROM_RACK_TO_MACHINE' -export const GO_DOWN_ONE_INTERACTION_LEVEL = 'GO_DOWN_ONE_INTERACTION_LEVEL' - -export function goToRoom(roomId) { - return { - type: GO_FROM_BUILDING_TO_ROOM, - roomId, - } -} - -export function goFromBuildingToRoom(roomId) { - return (dispatch, getState) => { - const { interactionLevel } = getState() - if (interactionLevel.mode !== 'BUILDING') { - return - } - - dispatch({ - type: GO_FROM_BUILDING_TO_ROOM, - roomId, - }) - } -} - -export function goFromRoomToRack(tileId) { - return (dispatch, getState) => { - const { interactionLevel } = getState() - if (interactionLevel.mode !== 'ROOM') { - return - } - dispatch({ - type: GO_FROM_ROOM_TO_RACK, - tileId, - }) - } -} - -export function goFromRackToMachine(position) { - return (dispatch, getState) => { - const { interactionLevel } = getState() - if (interactionLevel.mode !== 'RACK') { - return - } - dispatch({ - type: GO_FROM_RACK_TO_MACHINE, - position, - }) - } -} - -export function goDownOneInteractionLevel() { - return { - type: GO_DOWN_ONE_INTERACTION_LEVEL, - } -} diff --git a/opendc-web/opendc-web-ui/src/redux/actions/topology/building.js b/opendc-web/opendc-web-ui/src/redux/actions/topology/building.js deleted file mode 100644 index c12417b9..00000000 --- a/opendc-web/opendc-web-ui/src/redux/actions/topology/building.js +++ /dev/null @@ -1,113 +0,0 @@ -import { v4 as uuid } from 'uuid' -import { addRoom, deleteRoom } from './room' - -export const START_NEW_ROOM_CONSTRUCTION = 'START_NEW_ROOM_CONSTRUCTION' -export const START_NEW_ROOM_CONSTRUCTION_SUCCEEDED = 'START_NEW_ROOM_CONSTRUCTION_SUCCEEDED' -export const FINISH_NEW_ROOM_CONSTRUCTION = 'FINISH_NEW_ROOM_CONSTRUCTION' -export const CANCEL_NEW_ROOM_CONSTRUCTION = 'CANCEL_NEW_ROOM_CONSTRUCTION' -export const CANCEL_NEW_ROOM_CONSTRUCTION_SUCCEEDED = 'CANCEL_NEW_ROOM_CONSTRUCTION_SUCCEEDED' -export const START_ROOM_EDIT = 'START_ROOM_EDIT' -export const FINISH_ROOM_EDIT = 'FINISH_ROOM_EDIT' -export const ADD_TILE = 'ADD_TILE' -export const DELETE_TILE = 'DELETE_TILE' - -export function startNewRoomConstruction() { - return (dispatch, getState) => { - const { topology } = getState() - const topologyId = topology.root.id - const room = { - id: uuid(), - name: 'Room', - topologyId, - tiles: [], - } - - dispatch(addRoom(topologyId, room)) - dispatch(startNewRoomConstructionSucceeded(room.id)) - } -} - -export function startNewRoomConstructionSucceeded(roomId) { - return { - type: START_NEW_ROOM_CONSTRUCTION_SUCCEEDED, - roomId, - } -} - -export function finishNewRoomConstruction() { - return (dispatch, getState) => { - const { topology, construction } = getState() - if (topology.rooms[construction.currentRoomInConstruction].tiles.length === 0) { - dispatch(cancelNewRoomConstruction()) - return - } - - dispatch({ - type: FINISH_NEW_ROOM_CONSTRUCTION, - }) - } -} - -export function cancelNewRoomConstruction() { - return (dispatch, getState) => { - const { construction } = getState() - const roomId = construction.currentRoomInConstruction - dispatch(deleteRoom(roomId)) - dispatch(cancelNewRoomConstructionSucceeded()) - } -} - -export function cancelNewRoomConstructionSucceeded() { - return { - type: CANCEL_NEW_ROOM_CONSTRUCTION_SUCCEEDED, - } -} - -export function startRoomEdit(roomId) { - return { - type: START_ROOM_EDIT, - roomId: roomId, - } -} - -export function finishRoomEdit() { - return { - type: FINISH_ROOM_EDIT, - } -} - -export function toggleTileAtLocation(positionX, positionY) { - return (dispatch, getState) => { - const { topology, construction } = getState() - - const roomId = construction.currentRoomInConstruction - const tileIds = topology.rooms[roomId].tiles - for (const tileId of tileIds) { - if (topology.tiles[tileId].positionX === positionX && topology.tiles[tileId].positionY === positionY) { - dispatch(deleteTile(tileId)) - return - } - } - - dispatch(addTile(roomId, positionX, positionY)) - } -} - -export function addTile(roomId, positionX, positionY) { - return { - type: ADD_TILE, - tile: { - id: uuid(), - roomId, - positionX, - positionY, - }, - } -} - -export function deleteTile(tileId) { - return { - type: DELETE_TILE, - tileId, - } -} diff --git a/opendc-web/opendc-web-ui/src/redux/actions/topology/index.js b/opendc-web/opendc-web-ui/src/redux/actions/topology/index.js deleted file mode 100644 index d48af37a..00000000 --- a/opendc-web/opendc-web-ui/src/redux/actions/topology/index.js +++ /dev/null @@ -1,40 +0,0 @@ -/* - * 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. - */ - -export const OPEN_TOPOLOGY = 'OPEN_TOPOLOGY' -export const STORE_TOPOLOGY = 'STORE_TOPOLOGY' - -export function openTopology(projectId, id) { - return { - type: OPEN_TOPOLOGY, - projectId, - id, - } -} - -export function storeTopology(topology, entities) { - return { - type: STORE_TOPOLOGY, - topology, - entities, - } -} diff --git a/opendc-web/opendc-web-ui/src/redux/actions/topology/machine.js b/opendc-web/opendc-web-ui/src/redux/actions/topology/machine.js deleted file mode 100644 index 93320884..00000000 --- a/opendc-web/opendc-web-ui/src/redux/actions/topology/machine.js +++ /dev/null @@ -1,28 +0,0 @@ -export const DELETE_MACHINE = 'DELETE_MACHINE' -export const ADD_UNIT = 'ADD_UNIT' -export const DELETE_UNIT = 'DELETE_UNIT' - -export function deleteMachine(machineId) { - return { - type: DELETE_MACHINE, - machineId, - } -} - -export function addUnit(machineId, unitType, unitId) { - return { - type: ADD_UNIT, - machineId, - unitType, - unitId, - } -} - -export function deleteUnit(machineId, unitType, unitId) { - return { - type: DELETE_UNIT, - machineId, - unitType, - unitId, - } -} diff --git a/opendc-web/opendc-web-ui/src/redux/actions/topology/rack.js b/opendc-web/opendc-web-ui/src/redux/actions/topology/rack.js deleted file mode 100644 index 1f65952a..00000000 --- a/opendc-web/opendc-web-ui/src/redux/actions/topology/rack.js +++ /dev/null @@ -1,36 +0,0 @@ -import { v4 as uuid } from 'uuid' - -export const EDIT_RACK_NAME = 'EDIT_RACK_NAME' -export const DELETE_RACK = 'DELETE_RACK' -export const ADD_MACHINE = 'ADD_MACHINE' - -export function editRackName(rackId, name) { - return { - type: EDIT_RACK_NAME, - name, - rackId, - } -} - -export function deleteRack(tileId, rackId) { - return { - type: DELETE_RACK, - rackId, - tileId, - } -} - -export function addMachine(rackId, position) { - return { - type: ADD_MACHINE, - machine: { - id: uuid(), - rackId, - position, - cpus: [], - gpus: [], - memories: [], - storages: [], - }, - } -} diff --git a/opendc-web/opendc-web-ui/src/redux/actions/topology/room.js b/opendc-web/opendc-web-ui/src/redux/actions/topology/room.js deleted file mode 100644 index 14cc126c..00000000 --- a/opendc-web/opendc-web-ui/src/redux/actions/topology/room.js +++ /dev/null @@ -1,74 +0,0 @@ -import { v4 as uuid } from 'uuid' -import { - DEFAULT_RACK_SLOT_CAPACITY, - DEFAULT_RACK_POWER_CAPACITY, -} from '../../../components/topologies/map/MapConstants' -import { findTileWithPosition } from '../../../util/tile-calculations' - -export const ADD_ROOM = 'ADD_ROOM' -export const EDIT_ROOM_NAME = 'EDIT_ROOM_NAME' -export const DELETE_ROOM = 'DELETE_ROOM' -export const START_RACK_CONSTRUCTION = 'START_RACK_CONSTRUCTION' -export const STOP_RACK_CONSTRUCTION = 'STOP_RACK_CONSTRUCTION' -export const ADD_RACK_TO_TILE = 'ADD_RACK_TO_TILE' - -export function addRoom(topologyId, room) { - return { - type: ADD_ROOM, - room: { - id: uuid(), - topologyId, - ...room, - }, - } -} - -export function editRoomName(roomId, name) { - return { - type: EDIT_ROOM_NAME, - name, - roomId, - } -} - -export function startRackConstruction() { - return { - type: START_RACK_CONSTRUCTION, - } -} - -export function stopRackConstruction() { - return { - type: STOP_RACK_CONSTRUCTION, - } -} - -export function addRackToTile(positionX, positionY) { - return (dispatch, getState) => { - const { topology, interactionLevel } = getState() - const currentRoom = topology.rooms[interactionLevel.roomId] - const tiles = currentRoom.tiles.map((tileId) => topology.tiles[tileId]) - const tile = findTileWithPosition(tiles, positionX, positionY) - - if (tile !== null) { - dispatch({ - type: ADD_RACK_TO_TILE, - tileId: tile.id, - rack: { - id: uuid(), - name: 'Rack', - capacity: DEFAULT_RACK_SLOT_CAPACITY, - powerCapacityW: DEFAULT_RACK_POWER_CAPACITY, - machines: [], - }, - }) - } - } -} - -export function deleteRoom(roomId) { - return { - type: DELETE_ROOM, - roomId, - } -} diff --git a/opendc-web/opendc-web-ui/src/redux/index.js b/opendc-web/opendc-web-ui/src/redux/index.js deleted file mode 100644 index 53cd9144..00000000 --- a/opendc-web/opendc-web-ui/src/redux/index.js +++ /dev/null @@ -1,59 +0,0 @@ -import { useMemo } from 'react' -import { applyMiddleware, compose, createStore } from 'redux' -import { createLogger } from 'redux-logger' -import createSagaMiddleware from 'redux-saga' -import thunk from 'redux-thunk' -import rootReducer from './reducers' -import rootSaga from './sagas' -import { createReduxEnhancer } from '@sentry/react' -import { sentryDsn } from '../config' - -let store - -function initStore(initialState, ctx) { - const sagaMiddleware = createSagaMiddleware({ context: ctx }) - - const middlewares = [thunk, sagaMiddleware] - - if (process.env.NODE_ENV !== 'production') { - middlewares.push(createLogger()) - } - - let middleware = applyMiddleware(...middlewares) - - if (sentryDsn) { - middleware = compose(middleware, createReduxEnhancer()) - } - - const configuredStore = createStore(rootReducer, initialState, middleware) - sagaMiddleware.run(rootSaga) - store = configuredStore - - return configuredStore -} - -export const initializeStore = (preloadedState, ctx) => { - let _store = store ?? initStore(preloadedState, ctx) - - // After navigating to a page with an initial Redux state, merge that state - // with the current state in the store, and create a new store - if (preloadedState && store) { - _store = initStore({ - ...store.getState(), - ...preloadedState, - }) - // Reset the current store - store = undefined - } - - // For SSG and SSR always create a new store - if (typeof window === 'undefined') return _store - // Create the store once in the client - if (!store) store = _store - - return _store -} - -export function useStore(initialState, ctx) { - return useMemo(() => initializeStore(initialState, ctx), [initialState, ctx]) -} diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/construction-mode.js b/opendc-web/opendc-web-ui/src/redux/reducers/construction-mode.js deleted file mode 100644 index d0aac5ae..00000000 --- a/opendc-web/opendc-web-ui/src/redux/reducers/construction-mode.js +++ /dev/null @@ -1,43 +0,0 @@ -import { combineReducers } from 'redux' -import { GO_DOWN_ONE_INTERACTION_LEVEL } from '../actions/interaction-level' -import { - CANCEL_NEW_ROOM_CONSTRUCTION_SUCCEEDED, - FINISH_NEW_ROOM_CONSTRUCTION, - FINISH_ROOM_EDIT, - START_NEW_ROOM_CONSTRUCTION_SUCCEEDED, - START_ROOM_EDIT, -} from '../actions/topology/building' -import { DELETE_ROOM, START_RACK_CONSTRUCTION, STOP_RACK_CONSTRUCTION } from '../actions/topology/room' - -export function currentRoomInConstruction(state = '-1', action) { - switch (action.type) { - case START_NEW_ROOM_CONSTRUCTION_SUCCEEDED: - return action.roomId - case START_ROOM_EDIT: - return action.roomId - case CANCEL_NEW_ROOM_CONSTRUCTION_SUCCEEDED: - case FINISH_NEW_ROOM_CONSTRUCTION: - case FINISH_ROOM_EDIT: - case DELETE_ROOM: - return '-1' - default: - return state - } -} - -export function inRackConstructionMode(state = false, action) { - switch (action.type) { - case START_RACK_CONSTRUCTION: - return true - case STOP_RACK_CONSTRUCTION: - case GO_DOWN_ONE_INTERACTION_LEVEL: - return false - default: - return state - } -} - -export const construction = combineReducers({ - currentRoomInConstruction, - inRackConstructionMode, -}) diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/index.js b/opendc-web/opendc-web-ui/src/redux/reducers/index.js deleted file mode 100644 index 7ffb1211..00000000 --- a/opendc-web/opendc-web-ui/src/redux/reducers/index.js +++ /dev/null @@ -1,12 +0,0 @@ -import { combineReducers } from 'redux' -import { construction } from './construction-mode' -import { interactionLevel } from './interaction-level' -import topology from './topology' - -const rootReducer = combineReducers({ - topology, - construction, - interactionLevel, -}) - -export default rootReducer diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/interaction-level.js b/opendc-web/opendc-web-ui/src/redux/reducers/interaction-level.js deleted file mode 100644 index b30c68b9..00000000 --- a/opendc-web/opendc-web-ui/src/redux/reducers/interaction-level.js +++ /dev/null @@ -1,68 +0,0 @@ -import { - GO_DOWN_ONE_INTERACTION_LEVEL, - GO_FROM_BUILDING_TO_ROOM, - GO_FROM_RACK_TO_MACHINE, - GO_FROM_ROOM_TO_RACK, -} from '../actions/interaction-level' -import { DELETE_MACHINE } from '../actions/topology/machine' -import { DELETE_RACK } from '../actions/topology/rack' -import { DELETE_ROOM } from '../actions/topology/room' - -export function interactionLevel(state = { mode: 'BUILDING' }, action) { - switch (action.type) { - case GO_FROM_BUILDING_TO_ROOM: - return { - mode: 'ROOM', - roomId: action.roomId, - } - case GO_FROM_ROOM_TO_RACK: - return { - mode: 'RACK', - roomId: state.roomId, - tileId: action.tileId, - } - case GO_FROM_RACK_TO_MACHINE: - return { - mode: 'MACHINE', - roomId: state.roomId, - tileId: state.tileId, - position: action.position, - } - case GO_DOWN_ONE_INTERACTION_LEVEL: - if (state.mode === 'ROOM') { - return { - mode: 'BUILDING', - } - } else if (state.mode === 'RACK') { - return { - mode: 'ROOM', - roomId: state.roomId, - } - } else if (state.mode === 'MACHINE') { - return { - mode: 'RACK', - roomId: state.roomId, - tileId: state.tileId, - } - } else { - return state - } - case DELETE_MACHINE: - return { - mode: 'RACK', - roomId: state.roomId, - tileId: state.tileId, - } - case DELETE_RACK: - return { - mode: 'ROOM', - roomId: state.roomId, - } - case DELETE_ROOM: - return { - mode: 'BUILDING', - } - default: - return state - } -} diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/topology/index.js b/opendc-web/opendc-web-ui/src/redux/reducers/topology/index.js deleted file mode 100644 index 2c849387..00000000 --- a/opendc-web/opendc-web-ui/src/redux/reducers/topology/index.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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 { CPU_UNITS, GPU_UNITS, MEMORY_UNITS, STORAGE_UNITS } from '../../../util/unit-specifications' -import machine from './machine' -import rack from './rack' -import room from './room' -import tile from './tile' -import topology from './topology' - -function objects(state = {}, action) { - return { - cpus: CPU_UNITS, - gpus: GPU_UNITS, - memories: MEMORY_UNITS, - storages: STORAGE_UNITS, - machines: machine(state.machines, action, state), - racks: rack(state.racks, action, state), - tiles: tile(state.tiles, action), - rooms: room(state.rooms, action, state), - root: topology(state.root, action, state), - } -} - -export default objects diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/topology/machine.js b/opendc-web/opendc-web-ui/src/redux/reducers/topology/machine.js deleted file mode 100644 index 1789257b..00000000 --- a/opendc-web/opendc-web-ui/src/redux/reducers/topology/machine.js +++ /dev/null @@ -1,47 +0,0 @@ -import produce from 'immer' -import { STORE_TOPOLOGY } from '../../actions/topology' -import { DELETE_MACHINE, ADD_UNIT, DELETE_UNIT } from '../../actions/topology/machine' -import { ADD_MACHINE, DELETE_RACK } from '../../actions/topology/rack' - -function machine(state = {}, action, { racks }) { - switch (action.type) { - case STORE_TOPOLOGY: - return action.entities.machines || {} - case ADD_MACHINE: - return produce(state, (draft) => { - const { machine } = action - draft[machine.id] = machine - }) - case DELETE_MACHINE: - return produce(state, (draft) => { - const { machineId } = action - delete draft[machineId] - }) - case ADD_UNIT: - return produce(state, (draft) => { - const { machineId, unitType, unitId } = action - draft[machineId][unitType].push(unitId) - }) - case DELETE_UNIT: - return produce(state, (draft) => { - const { machineId, unitType, unitId } = action - const units = draft[machineId][unitType] - const index = units.indexOf(unitId) - units.splice(index, 1) - }) - case DELETE_RACK: - return produce(state, (draft) => { - const { rackId } = action - const rack = racks[rackId] - - for (const id of rack.machines) { - const machine = draft[id] - machine.rackId = undefined - } - }) - default: - return state - } -} - -export default machine diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/topology/rack.js b/opendc-web/opendc-web-ui/src/redux/reducers/topology/rack.js deleted file mode 100644 index ca79348a..00000000 --- a/opendc-web/opendc-web-ui/src/redux/reducers/topology/rack.js +++ /dev/null @@ -1,66 +0,0 @@ -/* - * 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 produce from 'immer' -import { STORE_TOPOLOGY } from '../../actions/topology' -import { DELETE_MACHINE } from '../../actions/topology/machine' -import { DELETE_RACK, EDIT_RACK_NAME, ADD_MACHINE } from '../../actions/topology/rack' -import { ADD_RACK_TO_TILE } from '../../actions/topology/room' - -function rack(state = {}, action, { machines }) { - switch (action.type) { - case STORE_TOPOLOGY: - return action.entities.racks || {} - case ADD_RACK_TO_TILE: - return produce(state, (draft) => { - const { rack } = action - draft[rack.id] = rack - }) - case EDIT_RACK_NAME: - return produce(state, (draft) => { - const { rackId, name } = action - draft[rackId].name = name - }) - case DELETE_RACK: - return produce(state, (draft) => { - const { rackId } = action - delete draft[rackId] - }) - case ADD_MACHINE: - return produce(state, (draft) => { - const { machine } = action - draft[machine.rackId].machines.push(machine.id) - }) - case DELETE_MACHINE: - return produce(state, (draft) => { - const { machineId } = action - const machine = machines[machineId] - const rack = draft[machine.rackId] - const index = rack.machines.indexOf(machineId) - rack.machines.splice(index, 1) - }) - default: - return state - } -} - -export default rack diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/topology/room.js b/opendc-web/opendc-web-ui/src/redux/reducers/topology/room.js deleted file mode 100644 index c05c8bfa..00000000 --- a/opendc-web/opendc-web-ui/src/redux/reducers/topology/room.js +++ /dev/null @@ -1,65 +0,0 @@ -/* - * 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 produce from 'immer' -import { STORE_TOPOLOGY } from '../../actions/topology' -import { ADD_TILE, DELETE_TILE } from '../../actions/topology/building' -import { DELETE_ROOM, EDIT_ROOM_NAME, ADD_ROOM } from '../../actions/topology/room' - -function room(state = {}, action, { tiles }) { - switch (action.type) { - case STORE_TOPOLOGY: - return action.entities.rooms || {} - case ADD_ROOM: - return produce(state, (draft) => { - const { room } = action - draft[room.id] = room - }) - case DELETE_ROOM: - return produce(state, (draft) => { - const { roomId } = action - delete draft[roomId] - }) - case EDIT_ROOM_NAME: - return produce(state, (draft) => { - const { roomId, name } = action - draft[roomId].name = name - }) - case ADD_TILE: - return produce(state, (draft) => { - const { tile } = action - draft[tile.roomId].tiles.push(tile.id) - }) - case DELETE_TILE: - return produce(state, (draft) => { - const { tileId } = action - const tile = tiles[tileId] - const room = draft[tile.roomId] - const index = room.tiles.indexOf(tileId) - room.tiles.splice(index, 1) - }) - default: - return state - } -} - -export default room diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/topology/tile.js b/opendc-web/opendc-web-ui/src/redux/reducers/topology/tile.js deleted file mode 100644 index 24c0e20c..00000000 --- a/opendc-web/opendc-web-ui/src/redux/reducers/topology/tile.js +++ /dev/null @@ -1,58 +0,0 @@ -/* - * 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 produce from 'immer' -import { STORE_TOPOLOGY } from '../../actions/topology' -import { ADD_TILE, DELETE_TILE } from '../../actions/topology/building' -import { DELETE_RACK } from '../../actions/topology/rack' -import { ADD_RACK_TO_TILE } from '../../actions/topology/room' - -function tile(state = {}, action) { - switch (action.type) { - case STORE_TOPOLOGY: - return action.entities.tiles || {} - case ADD_TILE: - return produce(state, (draft) => { - const { tile } = action - draft[tile.id] = tile - }) - case DELETE_TILE: - return produce(state, (draft) => { - const { tileId } = action - delete draft[tileId] - }) - case ADD_RACK_TO_TILE: - return produce(state, (draft) => { - const { rack, tileId } = action - draft[tileId].rack = rack.id - }) - case DELETE_RACK: - return produce(state, (draft) => { - const { tileId } = action - draft[tileId].rack = undefined - }) - default: - return state - } -} - -export default tile diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/topology/topology.js b/opendc-web/opendc-web-ui/src/redux/reducers/topology/topology.js deleted file mode 100644 index dff0a69e..00000000 --- a/opendc-web/opendc-web-ui/src/redux/reducers/topology/topology.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 produce from 'immer' -import { STORE_TOPOLOGY } from '../../actions/topology' -import { ADD_ROOM, DELETE_ROOM } from '../../actions/topology/room' - -function topology(state = undefined, action) { - switch (action.type) { - case STORE_TOPOLOGY: - return action.topology - case ADD_ROOM: - return produce(state, (draft) => { - const { room } = action - draft.rooms.push(room.id) - }) - case DELETE_ROOM: - return produce(state, (draft) => { - const { roomId } = action - const index = draft.rooms.indexOf(roomId) - draft.rooms.splice(index, 1) - }) - default: - return state - } -} - -export default topology diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/index.js b/opendc-web/opendc-web-ui/src/redux/sagas/index.js deleted file mode 100644 index 0fabdb6d..00000000 --- a/opendc-web/opendc-web-ui/src/redux/sagas/index.js +++ /dev/null @@ -1,7 +0,0 @@ -import { fork } from 'redux-saga/effects' -import { watchServer, updateServer } from './topology' - -export default function* rootSaga() { - yield fork(watchServer) - yield fork(updateServer) -} diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/topology.js b/opendc-web/opendc-web-ui/src/redux/sagas/topology.js deleted file mode 100644 index 15147bcf..00000000 --- a/opendc-web/opendc-web-ui/src/redux/sagas/topology.js +++ /dev/null @@ -1,76 +0,0 @@ -import { QueryObserver, MutationObserver } from 'react-query' -import { normalize, denormalize } from 'normalizr' -import { select, put, take, race, getContext, call } from 'redux-saga/effects' -import { eventChannel } from 'redux-saga' -import { Topology } from '../../util/topology-schema' -import { storeTopology, OPEN_TOPOLOGY } from '../actions/topology' - -/** - * Update the topology on the server. - */ -export function* updateServer() { - const queryClient = yield getContext('queryClient') - const mutationObserver = new MutationObserver(queryClient, { mutationKey: 'updateTopology' }) - - while (true) { - yield take( - (action) => - action.type.startsWith('EDIT') || action.type.startsWith('ADD') || action.type.startsWith('DELETE') - ) - const topology = yield select((state) => state.topology) - - if (!topology.root) { - continue - } - - const denormalizedTopology = denormalize(topology.root, Topology, topology) - yield call([mutationObserver, mutationObserver.mutate], denormalizedTopology) - } -} - -/** - * Watch the topology on the server for changes. - */ -export function* watchServer() { - let { projectId, id } = yield take(OPEN_TOPOLOGY) - while (true) { - const channel = yield queryObserver(projectId, id) - - while (true) { - const [action, response] = yield race([take(OPEN_TOPOLOGY), take(channel)]) - - if (action) { - projectId = action.projectId - id = action.id - break - } - - const { isFetched, data } = response - // Only update the topology on the client-side when a new topology was fetched - if (isFetched) { - const { result: topologyId, entities } = normalize(data, Topology) - yield put(storeTopology(entities.topologies[topologyId], entities)) - } - } - } -} - -/** - * Observe changes for the topology with the specified identifier. - */ -function* queryObserver(projectId, id) { - const queryClient = yield getContext('queryClient') - const observer = new QueryObserver(queryClient, { queryKey: ['topologies', projectId, id] }) - - return eventChannel((emitter) => { - const unsubscribe = observer.subscribe((result) => { - emitter(result) - }) - - // Update result to make sure we did not miss any query updates - // between creating the observer and subscribing to it. - observer.updateResult() - - return unsubscribe - }) -} diff --git a/opendc-web/opendc-web-ui/src/shapes.js b/opendc-web/opendc-web-ui/src/shapes.js deleted file mode 100644 index 50b82361..00000000 --- a/opendc-web/opendc-web-ui/src/shapes.js +++ /dev/null @@ -1,187 +0,0 @@ -/* - * 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' - -export const ProjectRole = PropTypes.oneOf(['VIEWER', 'EDITOR', 'OWNER']) - -export const Project = PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - createdAt: PropTypes.string.isRequired, - updatedAt: PropTypes.string.isRequired, - role: ProjectRole, -}) - -export const ProcessingUnit = PropTypes.shape({ - id: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - clockRateMhz: PropTypes.number.isRequired, - numberOfCores: PropTypes.number.isRequired, - energyConsumptionW: PropTypes.number.isRequired, -}) - -export const StorageUnit = PropTypes.shape({ - id: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - speedMbPerS: PropTypes.number.isRequired, - sizeMb: PropTypes.number.isRequired, - energyConsumptionW: PropTypes.number.isRequired, -}) - -export const Machine = PropTypes.shape({ - id: PropTypes.string.isRequired, - position: PropTypes.number.isRequired, - cpus: PropTypes.arrayOf(PropTypes.oneOfType([ProcessingUnit, PropTypes.string])), - gpus: PropTypes.arrayOf(PropTypes.oneOfType([ProcessingUnit, PropTypes.string])), - memories: PropTypes.arrayOf(PropTypes.oneOfType([StorageUnit, PropTypes.string])), - storages: PropTypes.arrayOf(PropTypes.oneOfType([StorageUnit, PropTypes.string])), -}) - -export const Rack = PropTypes.shape({ - id: PropTypes.string.isRequired, - capacity: PropTypes.number.isRequired, - powerCapacityW: PropTypes.number.isRequired, - machines: PropTypes.arrayOf(PropTypes.oneOfType([Machine, PropTypes.string])), -}) - -export const Tile = PropTypes.shape({ - id: PropTypes.string.isRequired, - positionX: PropTypes.number.isRequired, - positionY: PropTypes.number.isRequired, - rack: PropTypes.oneOfType([Rack, PropTypes.string]), -}) - -export const Room = PropTypes.shape({ - id: PropTypes.string.isRequired, - name: PropTypes.string.isRequired, - tiles: PropTypes.arrayOf(PropTypes.oneOfType([Tile, PropTypes.string])), -}) - -export const Topology = PropTypes.shape({ - id: PropTypes.number.isRequired, - number: PropTypes.number.isRequired, - project: Project.isRequired, - name: PropTypes.string.isRequired, - rooms: PropTypes.arrayOf(PropTypes.oneOfType([Room, PropTypes.string])), -}) - -export const Phenomena = PropTypes.shape({ - failures: PropTypes.bool.isRequired, - interference: PropTypes.bool.isRequired, -}) - -export const Scheduler = PropTypes.shape({ - name: PropTypes.string.isRequired, -}) - -export const Trace = PropTypes.shape({ - id: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - type: PropTypes.string.isRequired, -}) - -export const Workload = PropTypes.shape({ - trace: Trace.isRequired, - samplingFraction: PropTypes.number.isRequired, -}) - -export const Targets = PropTypes.shape({ - repeats: PropTypes.number.isRequired, - metrics: PropTypes.arrayOf(PropTypes.string).isRequired, -}) - -export const TopologySummary = PropTypes.shape({ - id: PropTypes.number.isRequired, - number: PropTypes.number.isRequired, - project: Project.isRequired, - name: PropTypes.string.isRequired, -}) - -export const PortfolioSummary = PropTypes.shape({ - id: PropTypes.number.isRequired, - number: PropTypes.number.isRequired, - project: Project.isRequired, - name: PropTypes.string.isRequired, - targets: PropTypes.shape({ - repeats: PropTypes.number.isRequired, - metrics: PropTypes.arrayOf(PropTypes.string).isRequired, - }).isRequired, -}) - -export const ScenarioSummary = PropTypes.shape({ - id: PropTypes.number.isRequired, - number: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - workload: Workload.isRequired, - topology: TopologySummary.isRequired, - phenomena: Phenomena.isRequired, - schedulerName: PropTypes.string.isRequired, - results: PropTypes.object, -}) - -export const JobState = PropTypes.oneOf(['PENDING', 'CLAIMED', 'RUNNING', 'FAILED', 'FINISHED']) - -export const Job = PropTypes.shape({ - id: PropTypes.number.isRequired, - state: JobState.isRequired, - createdAt: PropTypes.string.isRequired, - updatedAt: PropTypes.string.isRequired, - results: PropTypes.object, -}) - -export const Scenario = PropTypes.shape({ - id: PropTypes.number.isRequired, - number: PropTypes.number.isRequired, - project: Project.isRequired, - portfolio: PortfolioSummary.isRequired, - name: PropTypes.string.isRequired, - workload: Workload.isRequired, - topology: TopologySummary.isRequired, - phenomena: Phenomena.isRequired, - schedulerName: PropTypes.string.isRequired, - jobs: PropTypes.arrayOf(Job).isRequired, -}) - -export const Portfolio = PropTypes.shape({ - id: PropTypes.number.isRequired, - number: PropTypes.number.isRequired, - name: PropTypes.string.isRequired, - project: Project.isRequired, - targets: Targets.isRequired, - scenarios: PropTypes.arrayOf(ScenarioSummary).isRequired, -}) - -export const WallSegment = PropTypes.shape({ - startPosX: PropTypes.number.isRequired, - startPosY: PropTypes.number.isRequired, - isHorizontal: PropTypes.bool.isRequired, - length: PropTypes.number.isRequired, -}) - -export const InteractionLevel = PropTypes.shape({ - mode: PropTypes.string.isRequired, - roomId: PropTypes.string, - rackId: PropTypes.string, -}) - -export const Status = PropTypes.oneOf(['idle', 'loading', 'error', 'success']) diff --git a/opendc-web/opendc-web-ui/src/style/index.css b/opendc-web/opendc-web-ui/src/style/index.css deleted file mode 100644 index 7b7103a4..00000000 --- a/opendc-web/opendc-web-ui/src/style/index.css +++ /dev/null @@ -1,28 +0,0 @@ -/*! - * 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. - */ - -body, -#__next { - height: 100%; - - background: #eee; -} diff --git a/opendc-web/opendc-web-ui/src/util/authorizations.js b/opendc-web/opendc-web-ui/src/util/authorizations.js deleted file mode 100644 index 6cb08ba8..00000000 --- a/opendc-web/opendc-web-ui/src/util/authorizations.js +++ /dev/null @@ -1,21 +0,0 @@ -import HomeIcon from '@patternfly/react-icons/dist/js/icons/home-icon' -import EditIcon from '@patternfly/react-icons/dist/js/icons/edit-icon' -import EyeIcon from '@patternfly/react-icons/dist/js/icons/eye-icon' - -export const AUTH_ICON_MAP = { - OWNER: HomeIcon, - EDITOR: EditIcon, - VIEWER: EyeIcon, -} - -export const AUTH_NAME_MAP = { - OWNER: 'Owner', - EDITOR: 'Editor', - VIEWER: 'Viewer', -} - -export const AUTH_DESCRIPTION_MAP = { - OWNER: 'You own this project', - EDITOR: 'You can edit this project', - VIEWER: 'You can view this project', -} diff --git a/opendc-web/opendc-web-ui/src/util/available-metrics.js b/opendc-web/opendc-web-ui/src/util/available-metrics.js deleted file mode 100644 index fda6cd4d..00000000 --- a/opendc-web/opendc-web-ui/src/util/available-metrics.js +++ /dev/null @@ -1,101 +0,0 @@ -export const METRIC_GROUPS = { - 'Host Metrics': [ - 'total_overcommitted_burst', - 'total_power_draw', - 'total_failure_vm_slices', - 'total_granted_burst', - 'total_interfered_burst', - 'total_requested_burst', - 'mean_cpu_usage', - 'mean_cpu_demand', - 'mean_num_deployed_images', - 'max_num_deployed_images', - ], - 'Compute Service Metrics': ['total_vms_submitted', 'total_vms_queued', 'total_vms_finished', 'total_vms_failed'], -} - -export const AVAILABLE_METRICS = [ - 'mean_cpu_usage', - 'mean_cpu_demand', - 'total_requested_burst', - 'total_granted_burst', - 'total_overcommitted_burst', - 'total_interfered_burst', - 'total_power_draw', - 'total_failure_vm_slices', - 'mean_num_deployed_images', - 'max_num_deployed_images', - 'total_vms_submitted', - 'total_vms_queued', - 'total_vms_finished', - 'total_vms_failed', -] - -export const METRIC_NAMES_SHORT = { - total_overcommitted_burst: 'Overcomm. CPU Cycles', - total_granted_burst: 'Granted CPU Cycles', - total_requested_burst: 'Requested CPU Cycles', - total_interfered_burst: 'Interfered CPU Cycles', - total_power_draw: 'Total Power Consumption', - mean_cpu_usage: 'Mean Host CPU Usage', - mean_cpu_demand: 'Mean Host CPU Demand', - mean_num_deployed_images: 'Mean Num. Deployed Images Per Host', - max_num_deployed_images: 'Max. Num. Deployed Images Per Host', - total_failure_vm_slices: 'Total Num. Failed VM Slices', - total_vms_submitted: 'VMs Submitted', - total_vms_queued: 'VMs Queued', - total_vms_finished: 'VMs Finished', - total_vms_failed: 'VMs Failed', -} - -export const METRIC_NAMES = { - total_overcommitted_burst: 'Overcommitted CPU Cycles', - total_granted_burst: 'Granted CPU Cycles', - total_requested_burst: 'Requested CPU Cycles', - total_interfered_burst: 'Interfered CPU Cycles', - total_power_draw: 'Total Power Consumption', - mean_cpu_usage: 'Mean Host CPU Usage', - mean_cpu_demand: 'Mean Host CPU Demand', - mean_num_deployed_images: 'Mean Number of Deployed Images Per Host', - max_num_deployed_images: 'Maximum Number Deployed Images Per Host', - total_failure_vm_slices: 'Failed VM Slices', - total_vms_submitted: 'VMs Submitted', - total_vms_queued: 'VMs Queued', - total_vms_finished: 'VMs Finished', - total_vms_failed: 'VMs Failed', -} - -export const METRIC_UNITS = { - total_overcommitted_burst: 'MFLOP', - total_granted_burst: 'MFLOP', - total_requested_burst: 'MFLOP', - total_interfered_burst: 'MFLOP', - total_power_draw: 'Wh', - mean_cpu_usage: 'MHz', - mean_cpu_demand: 'MHz', - mean_num_deployed_images: 'VMs', - max_num_deployed_images: 'VMs', - total_failure_vm_slices: 'VM Slices', - total_vms_submitted: 'VMs', - total_vms_queued: 'VMs', - total_vms_finished: 'VMs', - total_vms_failed: 'VMs', -} - -export const METRIC_DESCRIPTIONS = { - total_overcommitted_burst: - 'The total CPU clock cycles lost due to overcommitting of resources. This metric is an indicator for resource overload.', - total_requested_burst: 'The total CPU clock cycles that were requested by all virtual machines.', - total_granted_burst: 'The total CPU clock cycles executed by the hosts.', - total_interfered_burst: 'The total CPU clock cycles lost due to resource interference between virtual machines.', - total_power_draw: 'The average power usage in watts.', - mean_cpu_usage: 'The average amount of CPU clock cycles consumed by all virtual machines on a host.', - mean_cpu_demand: 'The average amount of CPU clock cycles requested by all powered on virtual machines on a host.', - mean_num_deployed_images: 'The average number of virtual machines deployed on a host.', - max_num_deployed_images: 'The maximum number of virtual machines deployed at any time.', - total_failure_vm_slices: 'The total amount of CPU clock cycles lost due to failure.', - total_vms_submitted: 'The number of virtual machines scheduled by the compute service.', - total_vms_queued: 'The number of virtual machines still waiting to be scheduled by the compute service.', - total_vms_finished: 'The number of virtual machines that completed.', - total_vms_failed: 'The number of virtual machines that could not be scheduled.', -} diff --git a/opendc-web/opendc-web-ui/src/util/colors.js b/opendc-web/opendc-web-ui/src/util/colors.js deleted file mode 100644 index 34468503..00000000 --- a/opendc-web/opendc-web-ui/src/util/colors.js +++ /dev/null @@ -1,29 +0,0 @@ -export const GRID_COLOR = 'rgba(0, 0, 0, 0.5)' -export const BACKDROP_COLOR = 'rgba(255, 255, 255, 1)' -export const WALL_COLOR = 'rgba(0, 0, 0, 1)' - -export const ROOM_DEFAULT_COLOR = 'rgba(150, 150, 150, 1)' -export const ROOM_IN_CONSTRUCTION_COLOR = 'rgba(51, 153, 255, 1)' -export const ROOM_HOVER_VALID_COLOR = 'rgba(51, 153, 255, 1)' -export const ROOM_HOVER_INVALID_COLOR = 'rgba(255, 102, 0, 1)' -export const ROOM_NAME_COLOR = 'rgba(245, 245, 245, 1)' -export const ROOM_TYPE_COLOR = 'rgba(245, 245, 245, 1)' - -export const TILE_PLUS_COLOR = 'rgba(0, 0, 0, 1)' - -export const OBJECT_BORDER_COLOR = 'rgba(0, 0, 0, 1)' - -export const RACK_BACKGROUND_COLOR = 'rgba(170, 170, 170, 1)' -export const RACK_SPACE_BAR_BACKGROUND_COLOR = 'rgba(222, 235, 247, 0.6)' -export const RACK_SPACE_BAR_FILL_COLOR = 'rgba(91, 155, 213, 0.7)' -export const RACK_ENERGY_BAR_BACKGROUND_COLOR = 'rgba(255, 242, 204, 0.6)' -export const RACK_ENERGY_BAR_FILL_COLOR = 'rgba(244, 215, 0, 0.7)' -export const COOLING_ITEM_BACKGROUND_COLOR = 'rgba(40, 50, 230, 1)' -export const PSU_BACKGROUND_COLOR = 'rgba(230, 50, 60, 1)' - -export const GRAYED_OUT_AREA_COLOR = 'rgba(0, 0, 0, 0.6)' - -export const SIM_LOW_COLOR = 'rgba(197, 224, 180, 1)' -export const SIM_MID_LOW_COLOR = 'rgba(255, 230, 153, 1)' -export const SIM_MID_HIGH_COLOR = 'rgba(248, 203, 173, 1)' -export const SIM_HIGH_COLOR = 'rgba(249, 165, 165, 1)' diff --git a/opendc-web/opendc-web-ui/src/util/date-time.js b/opendc-web/opendc-web-ui/src/util/date-time.js deleted file mode 100644 index 7e2f6623..00000000 --- a/opendc-web/opendc-web-ui/src/util/date-time.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Parses and formats the given date-time string representation. - * - * The format assumed is "YYYY-MM-DDTHH:MM:SS". - * - * @param dateTimeString A string expressing a date and a time, in the above mentioned format. - * @returns {string} A human-friendly string version of that date and time. - */ -export function parseAndFormatDateTime(dateTimeString) { - return formatDateTime(new Date(dateTimeString)) -} - -/** - * Serializes the given date and time value to a human-friendly string. - * - * @param dateTime An object representation of a date and time. - * @returns {string} A human-friendly string version of that date and time. - */ -export function formatDateTime(dateTime) { - let date - const currentDate = new Date() - - date = - addPaddingToTwo(dateTime.getDay()) + - '/' + - addPaddingToTwo(dateTime.getMonth()) + - '/' + - addPaddingToTwo(dateTime.getFullYear()) - - if (dateTime.getFullYear() === currentDate.getFullYear() && dateTime.getMonth() === currentDate.getMonth()) { - if (dateTime.getDate() === currentDate.getDate()) { - date = 'Today' - } else if (dateTime.getDate() === currentDate.getDate() - 1) { - date = 'Yesterday' - } - } - - return date + ', ' + addPaddingToTwo(dateTime.getHours()) + ':' + addPaddingToTwo(dateTime.getMinutes()) -} - -/** - * Formats the given number of seconds/ticks to a formatted time representation. - * - * @param seconds The number of seconds. - * @returns {string} A string representation of that amount of second, in the from of HH:MM:SS. - */ -export function convertSecondsToFormattedTime(seconds) { - if (seconds <= 0) { - return '0s' - } - - let hour = Math.floor(seconds / 3600) - let minute = Math.floor(seconds / 60) % 60 - let second = seconds % 60 - - hour = isNaN(hour) ? 0 : hour - minute = isNaN(minute) ? 0 : minute - second = isNaN(second) ? 0 : second - - if (hour === 0 && minute === 0) { - return second + 's' - } else if (hour === 0) { - return minute + 'm' + addPaddingToTwo(second) + 's' - } else { - return hour + 'h' + addPaddingToTwo(minute) + 'm' + addPaddingToTwo(second) + 's' - } -} - -/** - * Pads the given integer to have at least two digits. - * - * @param integer An integer to be padded. - * @returns {string} A string containing the padded integer. - */ -function addPaddingToTwo(integer) { - if (integer < 10) { - return '0' + integer.toString() - } else { - return integer.toString() - } -} diff --git a/opendc-web/opendc-web-ui/src/util/date-time.test.js b/opendc-web/opendc-web-ui/src/util/date-time.test.js deleted file mode 100644 index 431e39f7..00000000 --- a/opendc-web/opendc-web-ui/src/util/date-time.test.js +++ /dev/null @@ -1,21 +0,0 @@ -import { convertSecondsToFormattedTime } from './date-time' - -describe('tick formatting', () => { - it("returns '0s' for numbers <= 0", () => { - expect(convertSecondsToFormattedTime(-1)).toEqual('0s') - expect(convertSecondsToFormattedTime(0)).toEqual('0s') - }) - it('returns only seconds for values under a minute', () => { - expect(convertSecondsToFormattedTime(1)).toEqual('1s') - expect(convertSecondsToFormattedTime(59)).toEqual('59s') - }) - it('returns seconds and minutes for values under an hour', () => { - expect(convertSecondsToFormattedTime(60)).toEqual('1m00s') - expect(convertSecondsToFormattedTime(61)).toEqual('1m01s') - expect(convertSecondsToFormattedTime(3599)).toEqual('59m59s') - }) - it('returns full time for values over an hour', () => { - expect(convertSecondsToFormattedTime(3600)).toEqual('1h00m00s') - expect(convertSecondsToFormattedTime(3601)).toEqual('1h00m01s') - }) -}) diff --git a/opendc-web/opendc-web-ui/src/util/effect-ref.js b/opendc-web/opendc-web-ui/src/util/effect-ref.js deleted file mode 100644 index 78528585..00000000 --- a/opendc-web/opendc-web-ui/src/util/effect-ref.js +++ /dev/null @@ -1,41 +0,0 @@ -/* - * 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 { useCallback, useRef } from 'react' - -const noop = () => {} - -/** - * A hook that will invoke the specified callback when the reference returned by this function is initialized. - * The callback can return an optional clean-up function. - */ -export function useEffectRef(callback, deps = []) { - const disposeRef = useRef(noop) - return useCallback((element) => { - disposeRef.current() - disposeRef.current = noop - - if (element) { - disposeRef.current = callback(element) || noop - } - }, deps) // eslint-disable-line react-hooks/exhaustive-deps -} diff --git a/opendc-web/opendc-web-ui/src/util/tile-calculations.js b/opendc-web/opendc-web-ui/src/util/tile-calculations.js deleted file mode 100644 index 374ca48c..00000000 --- a/opendc-web/opendc-web-ui/src/util/tile-calculations.js +++ /dev/null @@ -1,255 +0,0 @@ -export function deriveWallLocations(tiles) { - const { verticalWalls, horizontalWalls } = getWallSegments(tiles) - return mergeWallSegments(verticalWalls, horizontalWalls) -} - -function getWallSegments(tiles) { - const verticalWalls = {} - const horizontalWalls = {} - - tiles.forEach((tile) => { - const x = tile.positionX, - y = tile.positionY - - for (let dX = -1; dX <= 1; dX++) { - for (let dY = -1; dY <= 1; dY++) { - if (Math.abs(dX) === Math.abs(dY)) { - continue - } - - let doInsert = true - for (const tile of tiles) { - if (tile.positionX === x + dX && tile.positionY === y + dY) { - doInsert = false - break - } - } - if (!doInsert) { - continue - } - - if (dX === -1) { - if (verticalWalls[x] === undefined) { - verticalWalls[x] = [] - } - if (verticalWalls[x].indexOf(y) === -1) { - verticalWalls[x].push(y) - } - } else if (dX === 1) { - if (verticalWalls[x + 1] === undefined) { - verticalWalls[x + 1] = [] - } - if (verticalWalls[x + 1].indexOf(y) === -1) { - verticalWalls[x + 1].push(y) - } - } else if (dY === -1) { - if (horizontalWalls[y] === undefined) { - horizontalWalls[y] = [] - } - if (horizontalWalls[y].indexOf(x) === -1) { - horizontalWalls[y].push(x) - } - } else if (dY === 1) { - if (horizontalWalls[y + 1] === undefined) { - horizontalWalls[y + 1] = [] - } - if (horizontalWalls[y + 1].indexOf(x) === -1) { - horizontalWalls[y + 1].push(x) - } - } - } - } - }) - - return { verticalWalls, horizontalWalls } -} - -function mergeWallSegments(vertical, horizontal) { - const result = [] - const walls = [vertical, horizontal] - - for (let i = 0; i < 2; i++) { - const wallList = walls[i] - for (let a in wallList) { - a = parseInt(a, 10) - - wallList[a].sort((a, b) => { - return a - b - }) - - let startPos = wallList[a][0] - const isHorizontal = i === 1 - - if (wallList[a].length === 1) { - const startPosX = isHorizontal ? startPos : a - const startPosY = isHorizontal ? a : startPos - result.push({ - startPosX, - startPosY, - isHorizontal, - length: 1, - }) - } else { - let consecutiveCount = 1 - for (let b = 0; b < wallList[a].length - 1; b++) { - if (b + 1 === wallList[a].length - 1) { - if (wallList[a][b + 1] - wallList[a][b] > 1) { - const startPosX = isHorizontal ? startPos : a - const startPosY = isHorizontal ? a : startPos - result.push({ - startPosX, - startPosY, - isHorizontal, - length: consecutiveCount, - }) - consecutiveCount = 0 - startPos = wallList[a][b + 1] - } - const startPosX = isHorizontal ? startPos : a - const startPosY = isHorizontal ? a : startPos - result.push({ - startPosX, - startPosY, - isHorizontal, - length: consecutiveCount + 1, - }) - break - } else if (wallList[a][b + 1] - wallList[a][b] > 1) { - const startPosX = isHorizontal ? startPos : a - const startPosY = isHorizontal ? a : startPos - result.push({ - startPosX, - startPosY, - isHorizontal, - length: consecutiveCount, - }) - startPos = wallList[a][b + 1] - consecutiveCount = 0 - } - consecutiveCount++ - } - } - } - } - - return result -} - -export function deriveValidNextTilePositions(rooms, selectedTiles) { - const result = [], - newPosition = { x: 0, y: 0 } - let isSurroundingTile - - selectedTiles.forEach((tile) => { - const x = tile.positionX - const y = tile.positionY - result.push({ x, y }) - - for (let dX = -1; dX <= 1; dX++) { - for (let dY = -1; dY <= 1; dY++) { - if (Math.abs(dX) === Math.abs(dY)) { - continue - } - - newPosition.x = x + dX - newPosition.y = y + dY - - isSurroundingTile = true - for (let index in selectedTiles) { - if ( - selectedTiles[index].positionX === newPosition.x && - selectedTiles[index].positionY === newPosition.y - ) { - isSurroundingTile = false - break - } - } - - if (isSurroundingTile && findPositionInRooms(rooms, newPosition.x, newPosition.y) === -1) { - result.push({ x: newPosition.x, y: newPosition.y }) - } - } - } - }) - - return result -} - -export function findPositionInPositions(positions, positionX, positionY) { - for (let i = 0; i < positions.length; i++) { - const position = positions[i] - if (positionX === position.x && positionY === position.y) { - return i - } - } - - return -1 -} - -export function findPositionInRooms(rooms, positionX, positionY) { - for (let i = 0; i < rooms.length; i++) { - const room = rooms[i] - if (findPositionInTiles(room.tiles, positionX, positionY) !== -1) { - return i - } - } - - return -1 -} - -function findPositionInTiles(tiles, positionX, positionY) { - let index = -1 - - for (let i = 0; i < tiles.length; i++) { - const tile = tiles[i] - if (positionX === tile.positionX && positionY === tile.positionY) { - index = i - break - } - } - - return index -} - -export function findTileWithPosition(tiles, positionX, positionY) { - for (let i = 0; i < tiles.length; i++) { - if (tiles[i].positionX === positionX && tiles[i].positionY === positionY) { - return tiles[i] - } - } - - return null -} - -export function calculateRoomListBounds(rooms) { - const min = { x: Number.MAX_VALUE, y: Number.MAX_VALUE } - const max = { x: -1, y: -1 } - - rooms.forEach((room) => { - room.tiles.forEach((tile) => { - if (tile.positionX < min.x) { - min.x = tile.positionX - } - if (tile.positionY < min.y) { - min.y = tile.positionY - } - - if (tile.positionX > max.x) { - max.x = tile.positionX - } - if (tile.positionY > max.y) { - max.y = tile.positionY - } - }) - }) - - max.x++ - max.y++ - - const center = { - x: min.x + (max.x - min.x) / 2.0, - y: min.y + (max.y - min.y) / 2.0, - } - - return { min, center, max } -} diff --git a/opendc-web/opendc-web-ui/src/util/topology-schema.js b/opendc-web/opendc-web-ui/src/util/topology-schema.js deleted file mode 100644 index ff672dd6..00000000 --- a/opendc-web/opendc-web-ui/src/util/topology-schema.js +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 { schema } from 'normalizr' - -const Cpu = new schema.Entity('cpus', {}, { idAttribute: 'id' }) -const Gpu = new schema.Entity('gpus', {}, { idAttribute: 'id' }) -const Memory = new schema.Entity('memories', {}, { idAttribute: 'id' }) -const Storage = new schema.Entity('storages', {}, { idAttribute: 'id' }) - -export const Machine = new schema.Entity( - 'machines', - { - cpus: [Cpu], - gpus: [Gpu], - memories: [Memory], - storages: [Storage], - }, - { idAttribute: 'id' } -) - -export const Rack = new schema.Entity('racks', { machines: [Machine] }, { idAttribute: 'id' }) - -export const Tile = new schema.Entity('tiles', { rack: Rack }, { idAttribute: 'id' }) - -export const Room = new schema.Entity('rooms', { tiles: [Tile] }, { idAttribute: 'id' }) - -export const Topology = new schema.Entity('topologies', { rooms: [Room] }, { idAttribute: 'id' }) diff --git a/opendc-web/opendc-web-ui/src/util/unit-specifications.js b/opendc-web/opendc-web-ui/src/util/unit-specifications.js deleted file mode 100644 index 3e3671cd..00000000 --- a/opendc-web/opendc-web-ui/src/util/unit-specifications.js +++ /dev/null @@ -1,102 +0,0 @@ -export const CPU_UNITS = { - 'cpu-1': { - id: 'cpu-1', - name: 'Intel i7 v6 6700k', - clockRateMhz: 4100, - numberOfCores: 4, - energyConsumptionW: 70, - }, - 'cpu-2': { - id: 'cpu-2', - name: 'Intel i5 v6 6700k', - clockRateMhz: 3500, - numberOfCores: 2, - energyConsumptionW: 50, - }, - 'cpu-3': { - id: 'cpu-3', - name: 'Intel® Xeon® E-2224G', - clockRateMhz: 3500, - numberOfCores: 4, - energyConsumptionW: 71, - }, - 'cpu-4': { - id: 'cpu-4', - name: 'Intel® Xeon® E-2244G', - clockRateMhz: 3800, - numberOfCores: 8, - energyConsumptionW: 71, - }, - 'cpu-5': { - id: 'cpu-5', - name: 'Intel® Xeon® E-2246G', - clockRateMhz: 3600, - numberOfCores: 12, - energyConsumptionW: 80, - }, -} - -export const GPU_UNITS = { - 'gpu-1': { - id: 'gpu-1', - name: 'NVIDIA GTX 4 1080', - clockRateMhz: 1200, - numberOfCores: 200, - energyConsumptionW: 250, - }, - 'gpu-2': { - id: 'gpu-2', - name: 'NVIDIA Tesla V100', - clockRateMhz: 1200, - numberOfCores: 5120, - energyConsumptionW: 250, - }, -} - -export const MEMORY_UNITS = { - 'memory-1': { - id: 'memory-1', - name: 'Samsung PC DRAM K4A4G045WD', - speedMbPerS: 16000, - sizeMb: 4000, - energyConsumptionW: 10, - }, - 'memory-2': { - id: 'memory-2', - name: 'Samsung PC DRAM M393A2K43BB1-CRC', - speedMbPerS: 2400, - sizeMb: 16000, - energyConsumptionW: 10, - }, - 'memory-3': { - id: 'memory-3', - name: 'Crucial MTA18ASF4G72PDZ-3G2E1', - speedMbPerS: 3200, - sizeMb: 32000, - energyConsumptionW: 10, - }, - 'memory-4': { - id: 'memory-4', - name: 'Crucial MTA9ASF2G72PZ-3G2E1', - speedMbPerS: 3200, - sizeMb: 16000, - energyConsumptionW: 10, - }, -} - -export const STORAGE_UNITS = { - 'storage-1': { - id: 'storage-1', - name: 'Samsung EVO 2016 SATA III', - speedMbPerS: 6000, - sizeMb: 250000, - energyConsumptionW: 10, - }, - 'storage-2': { - id: 'storage-2', - name: 'Western Digital MTA9ASF2G72PZ-3G2E1', - speedMbPerS: 6000, - sizeMb: 4000000, - energyConsumptionW: 10, - }, -} |
