diff --git a/ui/.scripts/generate-version.cjs b/ui/.scripts/generate-version.cjs index d9261b04f2..4fa4bc2e34 100755 --- a/ui/.scripts/generate-version.cjs +++ b/ui/.scripts/generate-version.cjs @@ -1,7 +1,13 @@ #! /usr/bin/env node +var shell = require("shelljs"); const fs = require("fs"); const packageJson = require('../package.json'); +console.info("-------------------------------------------------------"); +console.info("Getting current git SHA"); +const gitSHA = shell.exec("git rev-parse HEAD", { silent: true }).stdout.trim(); +console.info(` SHA: ${gitSHA}`); +console.info("-------------------------------------------------------"); console.info("-------------------------------------------------------"); console.info("Generating version.js"); @@ -14,6 +20,7 @@ const VERSION_OUTPUT_PATH="./ui-app/dist/version.js"; const info = { name: "Apicurio Registry", version: packageJson.version, + digest: gitSHA, builtOn: new Date(), url: "https://www.apicur.io/registry/" }; diff --git a/ui/ui-app/config/version.js b/ui/ui-app/config/version.js index 57566edb83..e3f5fa00b6 100644 --- a/ui/ui-app/config/version.js +++ b/ui/ui-app/config/version.js @@ -2,6 +2,6 @@ var ApicurioInfo = { name: "Apicurio Registry", version: "DEV", digest: "DEV", - builtOn: new Date(), - url: "http://www.apicur.io/" + builtOn: new Date().toString(), + url: "http://www.apicur.io/registry" }; diff --git a/ui/ui-app/public/apicurio_registry_icon_reverse.svg b/ui/ui-app/public/apicurio_registry_icon_reverse.svg new file mode 100644 index 0000000000..8b71dfadbf --- /dev/null +++ b/ui/ui-app/public/apicurio_registry_icon_reverse.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/ui/ui-app/src/app/components/common/IfAuth.tsx b/ui/ui-app/src/app/components/auth/IfAuth.tsx similarity index 97% rename from ui/ui-app/src/app/components/common/IfAuth.tsx rename to ui/ui-app/src/app/components/auth/IfAuth.tsx index cd8b7ae706..4cce23c3fb 100644 --- a/ui/ui-app/src/app/components/common/IfAuth.tsx +++ b/ui/ui-app/src/app/components/auth/IfAuth.tsx @@ -1,5 +1,5 @@ import React, { FunctionComponent } from "react"; -import { Services } from "@services/services"; +import { Services } from "@services/services.ts"; import { AuthService } from "@services/auth"; /** diff --git a/ui/ui-app/src/app/components/auth/index.ts b/ui/ui-app/src/app/components/auth/index.ts index c1d98f3156..19af110ed9 100644 --- a/ui/ui-app/src/app/components/auth/index.ts +++ b/ui/ui-app/src/app/components/auth/index.ts @@ -1 +1,2 @@ export * from "./ApplicationAuth.tsx"; +export * from "./IfAuth.tsx"; diff --git a/ui/ui-app/src/app/components/common/index.ts b/ui/ui-app/src/app/components/common/index.ts index c2b7e6fe04..d3c98f6c36 100644 --- a/ui/ui-app/src/app/components/common/index.ts +++ b/ui/ui-app/src/app/components/common/index.ts @@ -1,6 +1,5 @@ export * from "./ArtifactTypeIcon.tsx"; export * from "./If.tsx"; -export * from "./IfAuth.tsx"; export * from "./IfFeature.tsx"; export * from "./IfNotEmpty.tsx"; export * from "./UrlUpload.tsx"; diff --git a/ui/ui-app/src/app/components/header/AppAboutModal.tsx b/ui/ui-app/src/app/components/header/AppAboutModal.tsx new file mode 100644 index 0000000000..b7ded7b1a1 --- /dev/null +++ b/ui/ui-app/src/app/components/header/AppAboutModal.tsx @@ -0,0 +1,42 @@ +import { FunctionComponent } from "react"; +import { AboutModal, TextContent, TextList, TextListItem } from "@patternfly/react-core"; +import { VersionType } from "@services/version"; +import { Services } from "@services/services.ts"; + + +export type AppAboutModalProps = { + isOpen: boolean; + onClose: () => void; +}; + + +export const AppAboutModal: FunctionComponent = (props: AppAboutModalProps) => { + const version: VersionType = Services.getVersionService().getVersion(); + + return ( + + + + Project + { version.name } + + Version + { version.version } + + Built on + { "" + version.builtOn } + + Digest + { version.digest } + + + + ); +}; diff --git a/ui/ui-app/src/app/components/header/AppHeader.css b/ui/ui-app/src/app/components/header/AppHeader.css deleted file mode 100644 index 16e13d1b04..0000000000 --- a/ui/ui-app/src/app/components/header/AppHeader.css +++ /dev/null @@ -1,8 +0,0 @@ -.header-toolbar { -} - -.app-logo { - display: flex; - width: 350px; - height: 46px; -} diff --git a/ui/ui-app/src/app/components/header/AppHeader.tsx b/ui/ui-app/src/app/components/header/AppHeader.tsx index 92509cf472..069c6edede 100644 --- a/ui/ui-app/src/app/components/header/AppHeader.tsx +++ b/ui/ui-app/src/app/components/header/AppHeader.tsx @@ -1,13 +1,8 @@ -import React, { FunctionComponent } from "react"; -import "./AppHeader.css"; -import { AvatarDropdown, IfAuth } from "@app/components"; -import { - PageHeader, - PageHeaderTools, - PageHeaderToolsGroup, - PageHeaderToolsItem -} from "@patternfly/react-core/deprecated"; +import { FunctionComponent } from "react"; +import { Brand, Masthead, MastheadBrand, MastheadContent, MastheadMain } from "@patternfly/react-core"; import { AppNavigation, useAppNavigation } from "@hooks/useAppNavigation.ts"; +import { Link } from "react-router-dom"; +import { AppHeaderToolbar } from "@app/components"; export type AppHeaderProps = { @@ -18,30 +13,16 @@ export type AppHeaderProps = { export const AppHeader: FunctionComponent = () => { const appNavigation: AppNavigation = useAppNavigation(); - const logoProps = { - href: appNavigation.createLink("/") - }; - - const logo: React.ReactNode = ( -
- Apicurio Registry -
- ); - - const headerActions: React.ReactElement = ( - - - - - - - - - - ); - return ( - + + + }> + + + + + + + ); - }; diff --git a/ui/ui-app/src/app/components/header/AppHeaderToolbar.tsx b/ui/ui-app/src/app/components/header/AppHeaderToolbar.tsx new file mode 100644 index 0000000000..1bbfa8d03e --- /dev/null +++ b/ui/ui-app/src/app/components/header/AppHeaderToolbar.tsx @@ -0,0 +1,38 @@ +import { FunctionComponent, useState } from "react"; +import { Button, Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem } from "@patternfly/react-core"; +import { QuestionCircleIcon } from "@patternfly/react-icons"; +import { AvatarDropdown, IfAuth } from "@app/components"; +import { AppAboutModal } from "@app/components/header/AppAboutModal.tsx"; + + +export type AppHeaderToolbarProps = { + // No properties. +}; + + +export const AppHeaderToolbar: FunctionComponent = () => { + const [isAboutModalOpen, setIsAboutModalOpen] = useState(false); + + return ( + <> + setIsAboutModalOpen(false)} /> + + + + + + + + + + + + + + + + ); + +}; diff --git a/ui/ui-app/src/app/components/header/AvatarDropdown.css b/ui/ui-app/src/app/components/header/AvatarDropdown.css deleted file mode 100644 index 4d5e05969c..0000000000 --- a/ui/ui-app/src/app/components/header/AvatarDropdown.css +++ /dev/null @@ -1,15 +0,0 @@ -#avatar-dropdown { - margin-left: 15px; -} - -#avatar-dropdown .pf-c-dropdown__toggle-icon { - margin-left: 5px; -} - -#avatar-dropdown .avatar-logout-link { - color: rgb(21, 21, 21); -} -#avatar-dropdown .avatar-logout-link:hover { - border-bottom: none; - text-decoration: none; -} diff --git a/ui/ui-app/src/app/components/header/AvatarDropdown.tsx b/ui/ui-app/src/app/components/header/AvatarDropdown.tsx index 4891d1c299..efe9c2502c 100644 --- a/ui/ui-app/src/app/components/header/AvatarDropdown.tsx +++ b/ui/ui-app/src/app/components/header/AvatarDropdown.tsx @@ -1,13 +1,5 @@ import React, { FunctionComponent, useState } from "react"; -import "./AvatarDropdown.css"; -import { - Avatar, - Dropdown, - DropdownItem, - DropdownList, - MenuToggle, - MenuToggleElement -} from "@patternfly/react-core"; +import { Avatar, Dropdown, DropdownItem, DropdownList, MenuToggle, MenuToggleElement } from "@patternfly/react-core"; import { Services } from "@services/services.ts"; @@ -27,23 +19,26 @@ export const AvatarDropdown: FunctionComponent = () => { setIsOpen(!isOpen); }; + const icon = ( + + ); + return ( setIsOpen(isOpen)} - popperProps={{ - position: "right" - }} toggle={(toggleRef: React.Ref) => ( - + { + Services.getUsersService().currentUser().displayName || "User" + } )} shouldFocusToggleOnSelect diff --git a/ui/ui-app/src/app/components/header/index.ts b/ui/ui-app/src/app/components/header/index.ts index d7a03c3ff1..a26a71d70e 100644 --- a/ui/ui-app/src/app/components/header/index.ts +++ b/ui/ui-app/src/app/components/header/index.ts @@ -1,3 +1,4 @@ export * from "./AvatarDropdown"; -export * from "./AppHeader.tsx"; -export * from "./RootPageHeader.tsx"; +export * from "./AppHeader"; +export * from "./AppHeaderToolbar"; +export * from "./RootPageHeader"; diff --git a/ui/ui-app/src/services/index.ts b/ui/ui-app/src/services/index.ts index a10b83788a..40272990e6 100644 --- a/ui/ui-app/src/services/index.ts +++ b/ui/ui-app/src/services/index.ts @@ -8,3 +8,4 @@ export * from "./logger"; export * from "./services"; export * from "./users"; export * from "./url"; +export * from "./version"; diff --git a/ui/ui-app/src/services/services.ts b/ui/ui-app/src/services/services.ts index 53a2e3ec32..b2047f782e 100644 --- a/ui/ui-app/src/services/services.ts +++ b/ui/ui-app/src/services/services.ts @@ -7,6 +7,7 @@ import { DownloaderService } from "./downloader"; import { AuthService } from "./auth"; import { UsersService } from "./users"; import { AlertsService } from "./alerts"; +import { VersionService } from "@services/version"; // TODO convert all of the services into React hooks @@ -25,6 +26,10 @@ export class Services { return Services.all.config; } + public static getVersionService(): VersionService { + return Services.all.version; + } + public static getDownloaderService(): DownloaderService { return Services.all.downloader; } @@ -53,6 +58,7 @@ export class Services { groups: new GroupsService(), users: new UsersService(), config: new ConfigService(), + version: new VersionService(), downloader: new DownloaderService(), admin: new AdminService(), logger: new LoggerService(), diff --git a/ui/ui-app/src/services/version/index.ts b/ui/ui-app/src/services/version/index.ts new file mode 100644 index 0000000000..aa28cc599e --- /dev/null +++ b/ui/ui-app/src/services/version/index.ts @@ -0,0 +1,2 @@ +export * from "./version.service.ts"; +export * from "./version.type.ts"; diff --git a/ui/ui-app/src/services/version/version.service.ts b/ui/ui-app/src/services/version/version.service.ts new file mode 100644 index 0000000000..cef142a268 --- /dev/null +++ b/ui/ui-app/src/services/version/version.service.ts @@ -0,0 +1,50 @@ +import { VersionType } from "@services/version/version.type.ts"; +import { Service } from "@services/baseService.ts"; + +const DEFAULT_VERSION: VersionType = { + name: "Apicurio Registry", + version: "DEV", + digest: "DEV", + builtOn: new Date().toString(), + url: "http://www.apicur.io/" +}; + + +export function getVersion(): VersionType { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + if (ApicurioInfo) { return ApicurioInfo as VersionType; } + + const gw: any = window as any; + if (gw["ApicurioInfo"]) { + return gw["ApicurioInfo"] as VersionType; + } + + return DEFAULT_VERSION; +} + + +/** + * A simple configuration service. Reads information from a global "ApicurioRegistryConfig" variable + * that is typically included via JSONP. + */ +export class VersionService implements Service { + private version: VersionType; + + constructor() { + this.version = getVersion(); + } + + public init(): void { + // Nothing to init (done in c'tor) + } + + public updateConfig(version: VersionType): void { + this.version = version; + } + + public getVersion(): VersionType { + return this.version; + } + +} diff --git a/ui/ui-app/src/services/version/version.type.ts b/ui/ui-app/src/services/version/version.type.ts new file mode 100644 index 0000000000..fe4163c800 --- /dev/null +++ b/ui/ui-app/src/services/version/version.type.ts @@ -0,0 +1,8 @@ + +export interface VersionType { + name: string; + version: string; + digest: string; + builtOn: string; + url: string; +}