diff options
| author | mjkwiatkowski <mati.rewa@gmail.com> | 2026-02-16 15:18:21 +0100 |
|---|---|---|
| committer | mjkwiatkowski <mati.rewa@gmail.com> | 2026-02-16 15:18:21 +0100 |
| commit | 2f16cb0f48eca4453e3e894b3d45a3aa09e6dcc0 (patch) | |
| tree | 672d98baa2ac071f2c30de06d613254d0d8cd105 /opendc-web/opendc-web-ui/src/components | |
| parent | 86d35fcec83057e346e4982b5a6908f25342a392 (diff) | |
Diffstat (limited to 'opendc-web/opendc-web-ui/src/components')
100 files changed, 0 insertions, 5486 deletions
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 |
