diff options
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, - }, -} |
