From 9dd75a9a40f7f2aebbc617980c99085f9dc688f8 Mon Sep 17 00:00:00 2001 From: Fabian Mastenbroek Date: Wed, 14 Sep 2022 23:02:46 +0200 Subject: refactor(web/ui): Move project selector into masthead This change moves the project selector into the masthead since it affects the whole application. This follows the PatternFly guidelines. --- .../opendc-web-ui/src/components/AppHeader.js | 46 ++++++++--- .../src/components/AppHeader.module.scss | 35 ++++++++ .../opendc-web-ui/src/components/AppHeaderTools.js | 96 ++++++---------------- .../opendc-web-ui/src/components/AppHeaderUser.js | 81 ++++++++++++++++++ opendc-web/opendc-web-ui/src/components/AppLogo.js | 46 ----------- .../src/components/AppLogo.module.scss | 33 -------- .../src/components/context/ContextSelector.js | 9 +- .../components/context/ContextSelector.module.scss | 2 +- .../src/components/context/ProjectSelector.js | 8 +- 9 files changed, 187 insertions(+), 169 deletions(-) create mode 100644 opendc-web/opendc-web-ui/src/components/AppHeader.module.scss create mode 100644 opendc-web/opendc-web-ui/src/components/AppHeaderUser.js delete mode 100644 opendc-web/opendc-web-ui/src/components/AppLogo.js delete mode 100644 opendc-web/opendc-web-ui/src/components/AppLogo.module.scss (limited to 'opendc-web/opendc-web-ui/src/components') diff --git a/opendc-web/opendc-web-ui/src/components/AppHeader.js b/opendc-web/opendc-web-ui/src/components/AppHeader.js index fd54b3ad..54f3bbf3 100644 --- a/opendc-web/opendc-web-ui/src/components/AppHeader.js +++ b/opendc-web/opendc-web-ui/src/components/AppHeader.js @@ -20,24 +20,44 @@ * SOFTWARE. */ -import { PageHeader } from '@patternfly/react-core' 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 { AppNavigation } from './AppNavigation' -import AppLogo from './AppLogo' +import AppHeaderUser from './AppHeaderUser' +import ProjectSelector from './context/ProjectSelector' -export function AppHeader() { - // eslint-disable-next-line @next/next/no-img-element - const logo = OpenDC +import styles from './AppHeader.module.scss' +export function AppHeader() { return ( - } - topNav={} - /> + + + }> + OpenDC logo + OpenDC + + + + + + + + + + + + + + ) } diff --git a/opendc-web/opendc-web-ui/src/components/AppHeader.module.scss b/opendc-web/opendc-web-ui/src/components/AppHeader.module.scss new file mode 100644 index 00000000..a7a6e325 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/AppHeader.module.scss @@ -0,0 +1,35 @@ +/*! + * 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. + */ + +.logo { + span { + margin-left: 8px; + color: #fff; + align-self: center; + font-weight: 500; + } + + &:hover, + &:focus { + text-decoration: none; + } +} diff --git a/opendc-web/opendc-web-ui/src/components/AppHeaderTools.js b/opendc-web/opendc-web-ui/src/components/AppHeaderTools.js index 3e58b209..499bceef 100644 --- a/opendc-web/opendc-web-ui/src/components/AppHeaderTools.js +++ b/opendc-web/opendc-web-ui/src/components/AppHeaderTools.js @@ -21,29 +21,19 @@ */ import { - Avatar, Button, ButtonVariant, Dropdown, - DropdownGroup, DropdownItem, - DropdownToggle, KebabToggle, - PageHeaderTools, - PageHeaderToolsGroup, - PageHeaderToolsItem, - Skeleton, + ToolbarGroup, + ToolbarItem, } from '@patternfly/react-core' -import { useState } from 'react' -import { useAuth } from '../auth' +import { useReducer } from 'react' import { GithubIcon, HelpIcon } from '@patternfly/react-icons' function AppHeaderTools() { - const { logout, user, isAuthenticated, isLoading } = useAuth() - const username = isAuthenticated || isLoading ? user?.name : 'Anonymous' - const avatar = isAuthenticated || isLoading ? user?.picture : '/img/avatar.svg' - - const [isKebabDropdownOpen, setKebabDropdownOpen] = useState(false) + const [isKebabDropdownOpen, toggleKebabDropdown] = useReducer((t) => !t, false) const kebabDropdownItems = [ , ] - const [isDropdownOpen, setDropdownOpen] = useState(false) - const userDropdownItems = [ - - logout({ returnTo: window.location.origin })} - > - Logout - - , - ] - return ( - - - + + + - - + + - - - - - setKebabDropdownOpen(!isKebabDropdownOpen)} />} - isOpen={isKebabDropdownOpen} - dropdownItems={kebabDropdownItems} - /> - - - setDropdownOpen(!isDropdownOpen)}> - {username ?? ( - - )} - - } - dropdownItems={userDropdownItems} - /> - - - {avatar ? ( - - ) : ( - - )} - + + + + } + isOpen={isKebabDropdownOpen} + dropdownItems={kebabDropdownItems} + /> + + ) } diff --git a/opendc-web/opendc-web-ui/src/components/AppHeaderUser.js b/opendc-web/opendc-web-ui/src/components/AppHeaderUser.js new file mode 100644 index 00000000..809f3ac3 --- /dev/null +++ b/opendc-web/opendc-web-ui/src/components/AppHeaderUser.js @@ -0,0 +1,81 @@ +/* + * 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, +} from '@patternfly/react-core' +import { useReducer } from 'react' +import { useAuth } from '../auth' + +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 [isDropdownOpen, toggleDropdown] = useReducer((t) => !t, false) + const userDropdownItems = [ + + logout({ returnTo: window.location.origin })} + > + Logout + + , + ] + + const avatarComponent = avatar ? ( + + ) : ( + + ) + + return ( + + + {username ?? ( + + )} + + } + dropdownItems={userDropdownItems} + /> + + ) +} diff --git a/opendc-web/opendc-web-ui/src/components/AppLogo.js b/opendc-web/opendc-web-ui/src/components/AppLogo.js deleted file mode 100644 index 92663295..00000000 --- a/opendc-web/opendc-web-ui/src/components/AppLogo.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 PropTypes from 'prop-types' -import Link from 'next/link' -import { appLogo } from './AppLogo.module.scss' - -function AppLogo({ href, children, className, ...props }) { - return ( - <> - - - {children} - OpenDC - - - - ) -} - -AppLogo.propTypes = { - href: PropTypes.string.isRequired, - children: PropTypes.node, - className: PropTypes.string, -} - -export default AppLogo diff --git a/opendc-web/opendc-web-ui/src/components/AppLogo.module.scss b/opendc-web/opendc-web-ui/src/components/AppLogo.module.scss deleted file mode 100644 index 3d228cb6..00000000 --- a/opendc-web/opendc-web-ui/src/components/AppLogo.module.scss +++ /dev/null @@ -1,33 +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. - */ - -.appLogo { - span { - margin-left: 4px; - color: #fff; - } - - &:hover, - &:focus { - text-decoration: none; - } -} diff --git a/opendc-web/opendc-web-ui/src/components/context/ContextSelector.js b/opendc-web/opendc-web-ui/src/components/context/ContextSelector.js index a99b60c0..436c179b 100644 --- a/opendc-web/opendc-web-ui/src/components/context/ContextSelector.js +++ b/opendc-web/opendc-web-ui/src/components/context/ContextSelector.js @@ -23,9 +23,9 @@ import PropTypes from 'prop-types' import { ContextSelector as PFContextSelector, ContextSelectorItem } from '@patternfly/react-core' import { useMemo, useState } from 'react' -import { contextSelector } from './ContextSelector.module.scss' +import styles from './ContextSelector.module.scss' -function ContextSelector({ activeItem, items, onSelect, onToggle, isOpen, label }) { +function ContextSelector({ activeItem, items, onSelect, onToggle, isOpen, label, isFullHeight, type = 'page' }) { const [searchValue, setSearchValue] = useState('') const filteredItems = useMemo( () => items.filter(({ name }) => name.toLowerCase().indexOf(searchValue.toLowerCase()) !== -1) || items, @@ -34,7 +34,7 @@ function ContextSelector({ activeItem, items, onSelect, onToggle, isOpen, label return ( setSearchValue(value)} searchInputValue={searchValue} @@ -47,6 +47,7 @@ function ContextSelector({ activeItem, items, onSelect, onToggle, isOpen, label onSelect(target) onToggle(!isOpen) }} + isFullHeight={isFullHeight} > {filteredItems.map((item) => ( @@ -69,6 +70,8 @@ ContextSelector.propTypes = { onToggle: PropTypes.func.isRequired, isOpen: PropTypes.bool, label: PropTypes.string, + isFullHeight: PropTypes.bool, + type: PropTypes.oneOf(['app', 'page']), } export default ContextSelector diff --git a/opendc-web/opendc-web-ui/src/components/context/ContextSelector.module.scss b/opendc-web/opendc-web-ui/src/components/context/ContextSelector.module.scss index c4b89503..4f86ac64 100644 --- a/opendc-web/opendc-web-ui/src/components/context/ContextSelector.module.scss +++ b/opendc-web/opendc-web-ui/src/components/context/ContextSelector.module.scss @@ -20,7 +20,7 @@ * SOFTWARE. */ -.contextSelector.contextSelector { +.pageSelector.pageSelector { // Ensure this selector has precedence over the default one margin-right: 20px; diff --git a/opendc-web/opendc-web-ui/src/components/context/ProjectSelector.js b/opendc-web/opendc-web-ui/src/components/context/ProjectSelector.js index 7721e04c..5f47c798 100644 --- a/opendc-web/opendc-web-ui/src/components/context/ProjectSelector.js +++ b/opendc-web/opendc-web-ui/src/components/context/ProjectSelector.js @@ -22,14 +22,16 @@ import { useRouter } from 'next/router' import { useState } from 'react' -import { useProjects } from '../../data/project' +import { useProjects, useProject } from '../../data/project' import { Project } from '../../shapes' import ContextSelector from './ContextSelector' -function ProjectSelector({ activeProject }) { +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 ( @@ -40,6 +42,8 @@ function ProjectSelector({ activeProject }) { onSelect={(project) => router.push(`/projects/${project.id}`)} onToggle={setOpen} isOpen={isOpen} + isFullHeight + type="app" /> ) } -- cgit v1.2.3