diff --git a/frontend/assets/ansible-brand.svg b/frontend/assets/ansible-brand.svg new file mode 100644 index 0000000000..4b0b17ddea --- /dev/null +++ b/frontend/assets/ansible-brand.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/assets/awx-logo.svg b/frontend/assets/awx-logo.svg index 85af5785be..19dcd55505 100644 --- a/frontend/assets/awx-logo.svg +++ b/frontend/assets/awx-logo.svg @@ -1,149 +1,91 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/assets/galaxy-logo-ansibull.png b/frontend/assets/galaxy-logo-ansibull.png new file mode 100644 index 0000000000..0beac2d214 Binary files /dev/null and b/frontend/assets/galaxy-logo-ansibull.png differ diff --git a/frontend/assets/galaxy-logo-icon.png b/frontend/assets/galaxy-logo-icon.png new file mode 100644 index 0000000000..e69de29bb2 diff --git a/frontend/awx/main/AwxMasthead.tsx b/frontend/awx/main/AwxMasthead.tsx index b89d267d68..ff4e0bb00f 100644 --- a/frontend/awx/main/AwxMasthead.tsx +++ b/frontend/awx/main/AwxMasthead.tsx @@ -20,9 +20,11 @@ import { useAwxWebSocketSubscription } from '../common/useAwxWebSocket'; import { getDocsBaseUrl } from '../common/util/getDocsBaseUrl'; import { WorkflowApproval } from '../interfaces/WorkflowApproval'; import { AwxRoute } from './AwxRoutes'; +import { useAwxProductVersionInfo } from './useAwxProductVersionInfo'; export function AwxMasthead() { const { t } = useTranslation(); + const versionInfo = useAwxProductVersionInfo(); const openAnsibleAboutModal = useAnsibleAboutModal(); const config = useAwxConfig(); const pageNavigate = usePageNavigate(); @@ -58,7 +60,13 @@ export function AwxMasthead() { openAnsibleAboutModal({ brandImageSrc: '/assets/awx-logo.svg' })} + onClick={() => + openAnsibleAboutModal({ + brandImageSrc: '/assets/awx-logo.svg', + versionInfo, + userInfo: activeAwxUser?.username, + }) + } data-cy="masthead-about" > {t('About')} diff --git a/frontend/awx/main/useAwxProductVersionInfo.tsx b/frontend/awx/main/useAwxProductVersionInfo.tsx new file mode 100644 index 0000000000..71649c0a2a --- /dev/null +++ b/frontend/awx/main/useAwxProductVersionInfo.tsx @@ -0,0 +1,18 @@ +import { useEffect, useState } from 'react'; +import { awxAPI } from '../common/api/awx-utils'; + +type ProductVersionInfo = Record>; + +export function useAwxProductVersionInfo() { + const [, setError] = useState(); + const [productVersionInfo, setProductVersionInfo] = useState(); + useEffect(() => { + fetch(awxAPI`/ping/`) + .then((response) => response.json()) + .then((data) => setProductVersionInfo(data as ProductVersionInfo)) + .catch((err) => { + setError(err as Error); + }); + }, []); + return productVersionInfo; +} diff --git a/frontend/common/AboutModal.tsx b/frontend/common/AboutModal.tsx index 94177d2f69..252bd23013 100644 --- a/frontend/common/AboutModal.tsx +++ b/frontend/common/AboutModal.tsx @@ -1,16 +1,33 @@ -import { AboutModal, TextContent, TextList, TextListItem } from '@patternfly/react-core'; -import { useEffect, useState } from 'react'; +import { + AboutModal, + DescriptionList, + DescriptionListDescription, + DescriptionListGroup, + DescriptionListTerm, +} from '@patternfly/react-core'; +import { TFunction } from 'i18next'; +import React, { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; +import styled from 'styled-components'; import { usePageDialog } from '../../framework'; export interface AnsibleAboutModalProps { brandImageSrc: string; + versionInfo?: ProductVersionInfo; + userInfo?: string; onClose?: () => void; } -function AnsibleAboutModal(props: AnsibleAboutModalProps) { - const [_dialog, setDialog] = usePageDialog(); +type ProductVersionInfo = Record>; + +const StyledDescriptionListDescription = styled(DescriptionListDescription)` + font-weight: normal; +`; + +export function AnsibleAboutModal(props: AnsibleAboutModalProps) { const { t } = useTranslation(); + const [_dialog, setDialog] = usePageDialog(); + return ( - - - {t('Version')} - {process.env.VERSION} - - + + {Object.entries(props.versionInfo ?? {}) + .filter( + ([product]) => + product !== 'galaxy_ng_commit' && + product !== 'ha' && + product !== 'instances' && + product !== 'instance_groups' && + product !== 'time_zone' && + product !== 'deployment_type' + ) + .map(([product, info]) => ( + + {translateVersion(product, t)} + {typeof info === 'string' ? ( + {info} + ) : ( + Object.entries(info).map(([key, value]) => ( + + { + + {t(value)} + + } + + )) + )} + + ))} + + {t('Username')} + {props.userInfo} + + ); } @@ -49,3 +94,38 @@ export function useAnsibleAboutModal() { }, [props, setDialog]); return setProps; } + +export function translateVersion(name: string | undefined, t: TFunction) { + const VERSION_NAMES: Record = { + server_version: t`Server Version`, + galaxy_ng_version: t`Galaxy NG version`, + galaxy_importer_version: t`Galaxy Importer Version`, + pulp_core_version: t`Pulp Core version`, + pulp_ansible_version: t`Pulp Ansible Version`, + pulp_container_version: t`Pulp Container Version`, + version: t`Version`, + active_node: t`Active Node`, + install_uuid: t`Install UUID`, + instances: t`Instances`, + instance_groups: t`Instance Groups`, + available_versions: t`Available Versions`, + node: t`Node`, + node_type: t`Node type`, + uuid: t`UUID`, + heartbeat: t`Heartbeat`, + capacity: t`Capacity`, + name: t`Name`, + }; + + return VERSION_NAMES[name as string] || name; +} + +export function upstreamServices(name: string | undefined, t: TFunction) { + const SERVICE_NAMES: Record = { + 'Automation Hub': t`Ansible Galaxy`, + AWX: t`AWX`, + 'Event Driven Automation': t`EDA Server`, + }; + + return SERVICE_NAMES[name as string] || name; +} diff --git a/frontend/eda/main/EdaMasthead.tsx b/frontend/eda/main/EdaMasthead.tsx index 503c063a24..e62e5d6f0d 100644 --- a/frontend/eda/main/EdaMasthead.tsx +++ b/frontend/eda/main/EdaMasthead.tsx @@ -13,9 +13,10 @@ import { postRequest } from '../../common/crud/Data'; import { edaAPI } from '../common/eda-utils'; import { useEdaActiveUser } from '../common/useEdaActiveUser'; import { EdaRoute } from './EdaRoutes'; - +import { useEdaProductVersionInfo } from './useEdaProductVersionInfo'; export function EdaMasthead() { const { t } = useTranslation(); + const versionInfo = useEdaProductVersionInfo(); const openAnsibleAboutModal = useAnsibleAboutModal(); const pageNavigate = usePageNavigate(); const { activeEdaUser, refreshActiveEdaUser } = useEdaActiveUser(); @@ -49,7 +50,13 @@ export function EdaMasthead() { openAnsibleAboutModal({ brandImageSrc: '/assets/eda-logo.svg' })} + onClick={() => + openAnsibleAboutModal({ + brandImageSrc: '/assets/eda-icon.svg', + versionInfo, + userInfo: activeEdaUser?.username, + }) + } data-cy="masthead-about" > {t('About')} diff --git a/frontend/eda/main/useEdaProductVersionInfo.tsx b/frontend/eda/main/useEdaProductVersionInfo.tsx new file mode 100644 index 0000000000..fcaef15c3f --- /dev/null +++ b/frontend/eda/main/useEdaProductVersionInfo.tsx @@ -0,0 +1,18 @@ +import { useEffect, useState } from 'react'; +import { edaAPI } from '../common/eda-utils'; + +type ProductVersionInfo = Record>; + +export function useEdaProductVersionInfo() { + const [, setError] = useState(); + const [productVersionInfo, setProductVersionInfo] = useState(); + useEffect(() => { + fetch(edaAPI`/config/`) + .then((response) => response.json()) + .then((data) => setProductVersionInfo(data as ProductVersionInfo)) + .catch((err) => { + setError(err as Error); + }); + }, []); + return productVersionInfo; +} diff --git a/frontend/hub/main/HubMasthead.tsx b/frontend/hub/main/HubMasthead.tsx index 659eeda654..e0d5b999d4 100644 --- a/frontend/hub/main/HubMasthead.tsx +++ b/frontend/hub/main/HubMasthead.tsx @@ -19,9 +19,10 @@ import { useHubActiveUser } from '../common/useHubActiveUser'; import { useHubContext } from '../common/useHubContext'; import { HubItemsResponse } from '../common/useHubView'; import { HubRoute } from './HubRoutes'; - +import { useHubProductVersionInfo } from './useHubProductVersionInfo'; export function HubMasthead() { const { t } = useTranslation(); + const versionInfo = useHubProductVersionInfo(); const openAnsibleAboutModal = useAnsibleAboutModal(); useHubNotifications(); const { activeHubUser, refreshActiveHubUser } = useHubActiveUser(); @@ -56,7 +57,13 @@ export function HubMasthead() { <> openAnsibleAboutModal({ brandImageSrc: '/assets/galaxy-logo.svg' })} + onClick={() => + openAnsibleAboutModal({ + brandImageSrc: '/assets/galaxy-logo-ansibull.png', + versionInfo, + userInfo: activeHubUser?.username, + }) + } data-cy="masthead-about" > {t('About')} @@ -84,20 +91,15 @@ export function HubMasthead() { ); } - export function useHubNotifications() { const { t } = useTranslation(); const getPageUrl = useGetPageUrl(); - const { hasPermission } = useHubContext(); - const canApprove = hasPermission('ansible.modify_ansible_repo_content'); - const { data: result } = useGet>( canApprove ? hubAPI`/v3/plugin/ansible/search/collection-versions/` : undefined, { page_size: 100, repository_label: 'pipeline=staging' } ); - const { setNotificationGroups } = usePageNotifications(); useEffect(() => { setNotificationGroups((groups) => { @@ -109,7 +111,6 @@ export function useHubNotifications() { description: t('Namespace: ') + approval.collection_version?.namespace ?? '', // timestamp: approval.created, variant: 'info', - // TODO to should goto the specific approval page instead of the approvals page to: getPageUrl(HubRoute.Approvals, { query: { status: 'pipeline=staging' } }), })) ?? [], @@ -117,6 +118,5 @@ export function useHubNotifications() { return { ...groups }; }); }, [result, getPageUrl, setNotificationGroups, t]); - return result?.data ?? 0; } diff --git a/frontend/hub/main/useHubProductVersionInfo.tsx b/frontend/hub/main/useHubProductVersionInfo.tsx new file mode 100644 index 0000000000..f53bbf2c94 --- /dev/null +++ b/frontend/hub/main/useHubProductVersionInfo.tsx @@ -0,0 +1,18 @@ +import { useEffect, useState } from 'react'; +import { hubAPI } from '../common/api/formatPath'; + +type ProductVersionInfo = Record>; + +export function useHubProductVersionInfo() { + const [, setError] = useState(); + const [productVersionInfo, setProductVersionInfo] = useState(); + useEffect(() => { + fetch(hubAPI`/`) + .then((response) => response.json()) + .then((data) => setProductVersionInfo(data as ProductVersionInfo)) + .catch((err) => { + setError(err as Error); + }); + }, []); + return productVersionInfo; +} diff --git a/frontend/icons/galaxy-logo-ansibull.png b/frontend/icons/galaxy-logo-ansibull.png new file mode 100644 index 0000000000..0beac2d214 Binary files /dev/null and b/frontend/icons/galaxy-logo-ansibull.png differ