diff --git a/server/ui/.prettierignore b/server/ui/.prettierignore new file mode 100644 index 00000000..2240fb94 --- /dev/null +++ b/server/ui/.prettierignore @@ -0,0 +1 @@ +src/routeTree.gen.ts \ No newline at end of file diff --git a/server/ui/.prettierrc.json b/server/ui/.prettierrc.json new file mode 100644 index 00000000..1ca87ab7 --- /dev/null +++ b/server/ui/.prettierrc.json @@ -0,0 +1,3 @@ +{ + "singleQuote": false +} diff --git a/server/ui/eslint.config.js b/server/ui/eslint.config.js index 360f4b83..dc9d429c 100644 --- a/server/ui/eslint.config.js +++ b/server/ui/eslint.config.js @@ -1,37 +1,52 @@ -import js from '@eslint/js' -import globals from 'globals' -import reactHooks from 'eslint-plugin-react-hooks' -import reactRefresh from 'eslint-plugin-react-refresh' -import tseslint from 'typescript-eslint' -import react from 'eslint-plugin-react' +import js from "@eslint/js"; +import globals from "globals"; +import reactHooks from "eslint-plugin-react-hooks"; +import reactRefresh from "eslint-plugin-react-refresh"; +import tseslint from "typescript-eslint"; +import react from "eslint-plugin-react"; +import prettierRecommended from "eslint-plugin-prettier/recommended"; export default tseslint.config( - { ignores: ['dist'] }, + { ignores: ["dist"] }, { - extends: [js.configs.recommended, ...tseslint.configs.recommendedTypeChecked, ...tseslint.configs.stylisticTypeChecked], - files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + ...tseslint.configs.recommendedTypeChecked, + ...tseslint.configs.stylisticTypeChecked, + prettierRecommended, + ], + files: ["**/*.{ts,tsx}"], languageOptions: { ecmaVersion: 2020, globals: globals.browser, parserOptions: { - project: ['./tsconfig.node.json', './tsconfig.app.json'], + project: ["./tsconfig.node.json", "./tsconfig.app.json"], tsconfigRootDir: import.meta.dirname, }, }, plugins: { - 'react-hooks': reactHooks, - 'react-refresh': reactRefresh, - react + "react-hooks": reactHooks, + "react-refresh": reactRefresh, + react, }, rules: { ...reactHooks.configs.recommended.rules, - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], ...react.configs.recommended.rules, - ...react.configs['jsx-runtime'].rules, + ...react.configs["jsx-runtime"].rules, + "prettier/prettier": [ + "warn", + { + semi: false, + singleQuote: false, + trailingComma: "es5", + }, + ], }, - settings: { react: { version: '18.3' } }, + settings: { react: { version: "18.3" } }, + ignores: ["src/routeTree.gen.ts"], }, -) +); diff --git a/server/ui/package-lock.json b/server/ui/package-lock.json index 1aa78e85..e8b538c7 100644 --- a/server/ui/package-lock.json +++ b/server/ui/package-lock.json @@ -27,10 +27,13 @@ "@types/react-dom": "^18.3.5", "@vitejs/plugin-react-swc": "^3.5.0", "eslint": "^9.17.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.16", "globals": "^15.14.0", + "prettier": "^3.4.2", "typescript": "~5.6.2", "typescript-eslint": "^8.18.2", "vite": "^6.0.5" @@ -1139,6 +1142,19 @@ "integrity": "sha512-t1UcHbOa4txczTR5UlnG4XcAAdnDSfSlCaOddw/HTqRF59pn2ks2JUu9sfnFRZ8SiAAxKRiYdX5bT7Mf4R24+w==", "license": "MIT" }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.34.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.34.0.tgz", @@ -3246,6 +3262,50 @@ } } }, + "node_modules/eslint-config-prettier": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.0.1.tgz", + "integrity": "sha512-lZBts941cyJyeaooiKxAtzoPHTN+GbQTJFAIdQbRhA4/8whaAraEh47Whw/ZFfrjNSnlAxqfm9i0XVAEkULjCw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "build/bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.3.tgz", + "integrity": "sha512-qJ+y0FfCp/mQYQ/vWQ3s7eUlFEL4PyKfAJxsnYTJ4YT73nsJBWqmEpFryxV9OeUiqmsTsYJ5Y+KDNaeP31wrRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, "node_modules/eslint-plugin-react": { "version": "7.37.4", "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.4.tgz", @@ -3413,6 +3473,13 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -4952,6 +5019,19 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5589,6 +5669,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, "node_modules/tabbable": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", diff --git a/server/ui/package.json b/server/ui/package.json index bca7d8fa..78449c63 100644 --- a/server/ui/package.json +++ b/server/ui/package.json @@ -7,7 +7,9 @@ "dev": "vite --host", "build": "tsc -b && vite build", "lint": "eslint .", - "preview": "vite preview" + "preview": "vite preview", + "format:check": "prettier --check './**/*.{ts,tsx,js,json}'", + "format:fix": "prettier --write './**/*.{ts,tsx,js,json}'" }, "dependencies": { "@patternfly/patternfly": "^6.1.0", @@ -29,10 +31,13 @@ "@types/react-dom": "^18.3.5", "@vitejs/plugin-react-swc": "^3.5.0", "eslint": "^9.17.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-prettier": "^5.2.3", "eslint-plugin-react": "^7.37.4", "eslint-plugin-react-hooks": "^5.0.0", "eslint-plugin-react-refresh": "^0.4.16", "globals": "^15.14.0", + "prettier": "^3.4.2", "typescript": "~5.6.2", "typescript-eslint": "^8.18.2", "vite": "^6.0.5" diff --git a/server/ui/src/app/components/NotificationsContext.tsx b/server/ui/src/app/components/NotificationsContext.tsx index 67333946..2a69440a 100644 --- a/server/ui/src/app/components/NotificationsContext.tsx +++ b/server/ui/src/app/components/NotificationsContext.tsx @@ -1,26 +1,26 @@ import * as React from "react"; -import {AlertProps} from "@patternfly/react-core"; +import { AlertProps } from "@patternfly/react-core"; export interface INotification { - title: string; - variant: AlertProps["variant"]; - message?: React.ReactNode; - hideCloseButton?: boolean; - timeout?: number | boolean; + title: string; + variant: AlertProps["variant"]; + message?: React.ReactNode; + hideCloseButton?: boolean; + timeout?: number | boolean; } export interface INotificationsProvider { - children: React.ReactNode; + children: React.ReactNode; } interface INotificationsContext { - pushNotification: (notification: INotification) => void; - dismissNotification: (key: string) => void; - notifications: INotification[]; + pushNotification: (notification: INotification) => void; + dismissNotification: (key: string) => void; + notifications: INotification[]; } const appContextDefaultValue = {} as INotificationsContext; export const NotificationsContext = React.createContext( - appContextDefaultValue + appContextDefaultValue, ); diff --git a/server/ui/src/app/components/NotificationsProvider.tsx b/server/ui/src/app/components/NotificationsProvider.tsx index 26096cdd..3ce1593a 100644 --- a/server/ui/src/app/components/NotificationsProvider.tsx +++ b/server/ui/src/app/components/NotificationsProvider.tsx @@ -1,42 +1,46 @@ import * as React from "react"; -import {INotification, INotificationsProvider, NotificationsContext} from "./NotificationsContext.tsx"; +import { + INotification, + INotificationsProvider, + NotificationsContext, +} from "./NotificationsContext.tsx"; const notificationDefault: Pick = { - hideCloseButton: false, + hideCloseButton: false, }; export const NotificationsProvider: React.FunctionComponent< - INotificationsProvider -> = ({children}: INotificationsProvider) => { - const [notifications, setNotifications] = React.useState([]); + INotificationsProvider +> = ({ children }: INotificationsProvider) => { + const [notifications, setNotifications] = React.useState([]); - const pushNotification = ( - notification: INotification, - clearNotificationDelay?: number - ) => { - setNotifications([ - ...notifications, - {...notificationDefault, ...notification}, - ]); - setTimeout(() => setNotifications([]), clearNotificationDelay ?? 10000); - }; + const pushNotification = ( + notification: INotification, + clearNotificationDelay?: number, + ) => { + setNotifications([ + ...notifications, + { ...notificationDefault, ...notification }, + ]); + setTimeout(() => setNotifications([]), clearNotificationDelay ?? 10000); + }; - const dismissNotification = (title: string) => { - const remainingNotifications = notifications.filter( - (n) => n.title !== title - ); - setNotifications(remainingNotifications); - }; - - return ( - - {children} - + const dismissNotification = (title: string) => { + const remainingNotifications = notifications.filter( + (n) => n.title !== title, ); + setNotifications(remainingNotifications); + }; + + return ( + + {children} + + ); }; diff --git a/server/ui/src/app/components/PageDrawerContext.tsx b/server/ui/src/app/components/PageDrawerContext.tsx index ee54ef04..3ea961c3 100644 --- a/server/ui/src/app/components/PageDrawerContext.tsx +++ b/server/ui/src/app/components/PageDrawerContext.tsx @@ -1,204 +1,204 @@ import * as React from "react"; import { - Drawer, - DrawerActions, - DrawerCloseButton, - DrawerContent, - DrawerContentBody, - DrawerHead, - DrawerPanelBody, - DrawerPanelContent, - DrawerPanelContentProps, + Drawer, + DrawerActions, + DrawerCloseButton, + DrawerContent, + DrawerContentBody, + DrawerHead, + DrawerPanelBody, + DrawerPanelContent, + DrawerPanelContentProps, } from "@patternfly/react-core"; import pageStyles from "@patternfly/react-styles/css/components/Page/page"; const usePageDrawerState = () => { - const [isDrawerExpanded, setIsDrawerExpanded] = React.useState(false); - const [drawerPanelContent, setDrawerPanelContent] = - React.useState(null); - const [drawerPanelContentProps, setDrawerPanelContentProps] = React.useState< - Partial - >({}); - const [drawerPageKey, setDrawerPageKey] = React.useState(""); - const drawerFocusRef = React.useRef(document.createElement("span")); - return { - isDrawerExpanded, - setIsDrawerExpanded, - drawerPanelContent, - setDrawerPanelContent, - drawerPanelContentProps, - setDrawerPanelContentProps, - drawerPageKey, - setDrawerPageKey, - drawerFocusRef: drawerFocusRef as typeof drawerFocusRef | null, - }; + const [isDrawerExpanded, setIsDrawerExpanded] = React.useState(false); + const [drawerPanelContent, setDrawerPanelContent] = + React.useState(null); + const [drawerPanelContentProps, setDrawerPanelContentProps] = React.useState< + Partial + >({}); + const [drawerPageKey, setDrawerPageKey] = React.useState(""); + const drawerFocusRef = React.useRef(document.createElement("span")); + return { + isDrawerExpanded, + setIsDrawerExpanded, + drawerPanelContent, + setDrawerPanelContent, + drawerPanelContentProps, + setDrawerPanelContentProps, + drawerPageKey, + setDrawerPageKey, + drawerFocusRef: drawerFocusRef as typeof drawerFocusRef | null, + }; }; type PageDrawerState = ReturnType; const noop = () => { - // Define a reusable no-op function + // Define a reusable no-op function }; const PageDrawerContext = React.createContext({ - isDrawerExpanded: false, - setIsDrawerExpanded: noop, - drawerPanelContent: null, - setDrawerPanelContent: noop, - drawerPanelContentProps: {}, - setDrawerPanelContentProps: noop, - drawerPageKey: "", - setDrawerPageKey: noop, - drawerFocusRef: null, + isDrawerExpanded: false, + setIsDrawerExpanded: noop, + drawerPanelContent: null, + setDrawerPanelContent: noop, + drawerPanelContentProps: {}, + setDrawerPanelContentProps: noop, + drawerPageKey: "", + setDrawerPageKey: noop, + drawerFocusRef: null, }); // PageContentWithDrawerProvider should only be rendered as the direct child of a PatternFly Page component. interface IPageContentWithDrawerProviderProps { - children: React.ReactNode; // The entire content of the page. See usage in client/src/app/layout/DefaultLayout. + children: React.ReactNode; // The entire content of the page. See usage in client/src/app/layout/DefaultLayout. } export const PageContentWithDrawerProvider: React.FC< - IPageContentWithDrawerProviderProps -> = ({children}) => { - const pageDrawerState = usePageDrawerState(); - const { - isDrawerExpanded, - drawerFocusRef, - drawerPanelContent, - drawerPanelContentProps, - drawerPageKey, - } = pageDrawerState; - return ( - -
- drawerFocusRef?.current?.focus()} - position="right" - > - - {drawerPanelContent} - - } - > - {children} - - -
-
- ); + IPageContentWithDrawerProviderProps +> = ({ children }) => { + const pageDrawerState = usePageDrawerState(); + const { + isDrawerExpanded, + drawerFocusRef, + drawerPanelContent, + drawerPanelContentProps, + drawerPageKey, + } = pageDrawerState; + return ( + +
+ drawerFocusRef?.current?.focus()} + position="right" + > + + {drawerPanelContent} + + } + > + {children} + + +
+
+ ); }; let numPageDrawerContentInstances = 0; // PageDrawerContent can be rendered anywhere, but must have only one instance rendered at a time. export interface IPageDrawerContentProps { - isExpanded: boolean; - onCloseClick: () => void; // Should be used to update local state such that `isExpanded` becomes false. - header?: React.ReactNode; - children: React.ReactNode; // The content to show in the drawer when `isExpanded` is true. - drawerPanelContentProps?: Partial; // Additional props for the DrawerPanelContent component. - focusKey?: string | number; // A unique key representing the object being described in the drawer. When this changes, the drawer will regain focus. - pageKey: string; // A unique key representing the page where the drawer is used. Causes the drawer to remount when changing pages. + isExpanded: boolean; + onCloseClick: () => void; // Should be used to update local state such that `isExpanded` becomes false. + header?: React.ReactNode; + children: React.ReactNode; // The content to show in the drawer when `isExpanded` is true. + drawerPanelContentProps?: Partial; // Additional props for the DrawerPanelContent component. + focusKey?: string | number; // A unique key representing the object being described in the drawer. When this changes, the drawer will regain focus. + pageKey: string; // A unique key representing the page where the drawer is used. Causes the drawer to remount when changing pages. } export const PageDrawerContent: React.FC = ({ - isExpanded, - onCloseClick, - header = null, - children, - drawerPanelContentProps, - pageKey: localPageKeyProp, - }) => { - const { - setIsDrawerExpanded, - drawerFocusRef, - setDrawerPanelContent, - setDrawerPanelContentProps, - setDrawerPageKey, - } = React.useContext(PageDrawerContext); - - // Warn if we are trying to render more than one PageDrawerContent (they'll fight over the same state). - React.useEffect(() => { - numPageDrawerContentInstances++; - return () => { - numPageDrawerContentInstances--; - }; - }, []); - if (numPageDrawerContentInstances > 1) { - console.warn( - `${numPageDrawerContentInstances} instances of PageDrawerContent are currently rendered! Only one instance of this component should be rendered at a time.` - ); - } - - // Lift the value of isExpanded out to the context, but derive it from local state such as a selected table row. - // This is the ONLY place where `setIsDrawerExpanded` should be called. - // To expand/collapse the drawer, use the `isExpanded` prop when rendering PageDrawerContent. - React.useEffect(() => { - setIsDrawerExpanded(isExpanded); - return () => { - setIsDrawerExpanded(false); - setDrawerPanelContent(null); - }; - }, [isExpanded, setDrawerPanelContent, setIsDrawerExpanded]); - - // Same with pageKey and drawerPanelContentProps, keep them in sync with the local prop on PageDrawerContent. - React.useEffect(() => { - setDrawerPageKey(localPageKeyProp); - return () => { - setDrawerPageKey(""); - }; - }, [localPageKeyProp, setDrawerPageKey]); - - React.useEffect(() => { - setDrawerPanelContentProps(drawerPanelContentProps ?? {}); - }, [drawerPanelContentProps, setDrawerPanelContentProps]); - - // If the drawer is already expanded describing app A, then the user clicks app B, we want to send focus back to the drawer. - - // TODO: This introduces a layout issue bug when clicking in between the columns of a table. - // React.useEffect(() => { - // drawerFocusRef?.current?.focus(); - // }, [drawerFocusRef, focusKey]); - - React.useEffect(() => { - const drawerHead = header ?? children; - const drawerPanelBody = header === null ? null : children; - - setDrawerPanelContent( - <> - + isExpanded, + onCloseClick, + header = null, + children, + drawerPanelContentProps, + pageKey: localPageKeyProp, +}) => { + const { + setIsDrawerExpanded, + drawerFocusRef, + setDrawerPanelContent, + setDrawerPanelContentProps, + setDrawerPageKey, + } = React.useContext(PageDrawerContext); + + // Warn if we are trying to render more than one PageDrawerContent (they'll fight over the same state). + React.useEffect(() => { + numPageDrawerContentInstances++; + return () => { + numPageDrawerContentInstances--; + }; + }, []); + if (numPageDrawerContentInstances > 1) { + console.warn( + `${numPageDrawerContentInstances} instances of PageDrawerContent are currently rendered! Only one instance of this component should be rendered at a time.`, + ); + } + + // Lift the value of isExpanded out to the context, but derive it from local state such as a selected table row. + // This is the ONLY place where `setIsDrawerExpanded` should be called. + // To expand/collapse the drawer, use the `isExpanded` prop when rendering PageDrawerContent. + React.useEffect(() => { + setIsDrawerExpanded(isExpanded); + return () => { + setIsDrawerExpanded(false); + setDrawerPanelContent(null); + }; + }, [isExpanded, setDrawerPanelContent, setIsDrawerExpanded]); + + // Same with pageKey and drawerPanelContentProps, keep them in sync with the local prop on PageDrawerContent. + React.useEffect(() => { + setDrawerPageKey(localPageKeyProp); + return () => { + setDrawerPageKey(""); + }; + }, [localPageKeyProp, setDrawerPageKey]); + + React.useEffect(() => { + setDrawerPanelContentProps(drawerPanelContentProps ?? {}); + }, [drawerPanelContentProps, setDrawerPanelContentProps]); + + // If the drawer is already expanded describing app A, then the user clicks app B, we want to send focus back to the drawer. + + // TODO: This introduces a layout issue bug when clicking in between the columns of a table. + // React.useEffect(() => { + // drawerFocusRef?.current?.focus(); + // }, [drawerFocusRef, focusKey]); + + React.useEffect(() => { + const drawerHead = header ?? children; + const drawerPanelBody = header === null ? null : children; + + setDrawerPanelContent( + <> + {drawerHead} - - - - - {drawerPanelBody} - - ); - }, [ - children, - drawerFocusRef, - header, - isExpanded, - onCloseClick, - setDrawerPanelContent, - ]); - - return null; + + + + + {drawerPanelBody} + , + ); + }, [ + children, + drawerFocusRef, + header, + isExpanded, + onCloseClick, + setDrawerPanelContent, + ]); + + return null; }; diff --git a/server/ui/src/app/layout/about.tsx b/server/ui/src/app/layout/about.tsx index a7426452..1b234c94 100644 --- a/server/ui/src/app/layout/about.tsx +++ b/server/ui/src/app/layout/about.tsx @@ -1,58 +1,57 @@ import React from "react"; -import {AboutModal, Content, ContentVariants} from "@patternfly/react-core"; +import { AboutModal, Content, ContentVariants } from "@patternfly/react-core"; import backgroundImage from "@app/assets/pfbg-icon.svg"; interface IButtonAboutAppProps { - isOpen: boolean; - onClose: () => void; + isOpen: boolean; + onClose: () => void; } const TRANSPARENT_1x1_GIF = - "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw== "; + "data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw== "; export const AboutApp: React.FC = ({ - isOpen, - onClose, - }) => { - return ( - - - - OpenUBL is vendor-neutral, thought-leadering, mostly - informational collection of resources devoted to making Software - Supply Chains easier to create, manage, consume and ultimately… to - trust! - + isOpen, + onClose, +}) => { + return ( + + + + OpenUBL is vendor-neutral, thought-leadering, mostly informational + collection of resources devoted to making Software Supply Chains + easier to create, manage, consume and ultimately… to trust! + - - For more information refer to{" "} - - OpenUBL documentation - - - - - - - Version - 99.0.0 - - - - - ); + + For more information refer to{" "} + + OpenUBL documentation + + + + + + + Version + 99.0.0 + + + + + ); }; diff --git a/server/ui/src/app/layout/header.tsx b/server/ui/src/app/layout/header.tsx index efad934a..a872553e 100644 --- a/server/ui/src/app/layout/header.tsx +++ b/server/ui/src/app/layout/header.tsx @@ -1,247 +1,237 @@ -import React, {useReducer, useState} from "react"; +import React, { useReducer, useState } from "react"; import { - Avatar, - Button, - ButtonVariant, - Divider, - Dropdown, - DropdownItem, - DropdownList, - Masthead, - MastheadBrand, - MastheadContent, - MastheadLogo, - MastheadMain, - MastheadToggle, - MenuToggle, - MenuToggleElement, - PageToggleButton, - Split, - SplitItem, - Title, - ToggleGroup, - ToggleGroupItem, - Toolbar, - ToolbarContent, - ToolbarGroup, - ToolbarItem, + Avatar, + Button, + ButtonVariant, + Divider, + Dropdown, + DropdownItem, + DropdownList, + Masthead, + MastheadBrand, + MastheadContent, + MastheadLogo, + MastheadMain, + MastheadToggle, + MenuToggle, + MenuToggleElement, + PageToggleButton, + Split, + SplitItem, + Title, + ToggleGroup, + ToggleGroupItem, + Toolbar, + ToolbarContent, + ToolbarGroup, + ToolbarItem, } from "@patternfly/react-core"; -import {MoonIcon, SunIcon} from "@patternfly/react-icons/"; +import { MoonIcon, SunIcon } from "@patternfly/react-icons/"; import EllipsisVIcon from "@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon"; import HelpIcon from "@patternfly/react-icons/dist/esm/icons/help-icon"; import BarsIcon from "@patternfly/react-icons/dist/js/icons/bars-icon"; import imgAvatar from "../assets/avatar.svg"; -import {AboutApp} from "./about"; +import { AboutApp } from "./about"; export const HeaderApp: React.FC = () => { - const [isDarkTheme, setIsDarkTheme] = useState( - localStorage.getItem("isDarkTheme") === "true" - ); + const [isDarkTheme, setIsDarkTheme] = useState( + localStorage.getItem("isDarkTheme") === "true", + ); - React.useEffect(() => { - if (isDarkTheme) { - document.documentElement.classList.add("pf-v6-theme-dark"); - localStorage.setItem("isDarkTheme", "true"); - } else { - document.documentElement.classList.remove("pf-v6-theme-dark"); - localStorage.setItem("isDarkTheme", "false"); - } - }, [isDarkTheme]); + React.useEffect(() => { + if (isDarkTheme) { + document.documentElement.classList.add("pf-v6-theme-dark"); + localStorage.setItem("isDarkTheme", "true"); + } else { + document.documentElement.classList.remove("pf-v6-theme-dark"); + localStorage.setItem("isDarkTheme", "false"); + } + }, [isDarkTheme]); - const [isAboutOpen, toggleIsAboutOpen] = useReducer((state) => !state, false); - const [isKebabDropdownOpen, setIsKebabDropdownOpen] = useState(false); - const [isUserDropdownOpen, setIsUserDropdownOpen] = useState(false); + const [isAboutOpen, toggleIsAboutOpen] = useReducer((state) => !state, false); + const [isKebabDropdownOpen, setIsKebabDropdownOpen] = useState(false); + const [isUserDropdownOpen, setIsUserDropdownOpen] = useState(false); - const onKebabDropdownToggle = () => { - setIsKebabDropdownOpen(!isKebabDropdownOpen); - }; + const onKebabDropdownToggle = () => { + setIsKebabDropdownOpen(!isKebabDropdownOpen); + }; - const onKebabDropdownSelect = () => { - setIsKebabDropdownOpen(!isKebabDropdownOpen); - }; + const onKebabDropdownSelect = () => { + setIsKebabDropdownOpen(!isKebabDropdownOpen); + }; - return ( - <> - + return ( + <> + - - - - - - - - - - - - {/**/} - - - - Openubl - - - - - - - - - - {/* toolbar items to always show */} - - - - } - isSelected={!isDarkTheme} - onChange={() => setIsDarkTheme(false)} - /> - } - isSelected={isDarkTheme} - onChange={() => setIsDarkTheme(true)} - /> - - - + + + + + + + + + + + + {/**/} + + + + Openubl + + + + + + + + + + {/* toolbar items to always show */} + + + + } + isSelected={!isDarkTheme} + onChange={() => setIsDarkTheme(false)} + /> + } + isSelected={isDarkTheme} + onChange={() => setIsDarkTheme(true)} + /> + + + - {/* toolbar items to show at desktop sizes */} - - -