diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/header/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/header/index.tsx new file mode 100644 index 00000000000000..8d9c17f1f53f34 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/header/index.tsx @@ -0,0 +1,9 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export { HeaderLeftContent } from './left_content'; +export { HeaderRightContent } from './right_content'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/header/left_content.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/header/left_content.tsx new file mode 100644 index 00000000000000..768d8f054532ac --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/header/left_content.tsx @@ -0,0 +1,97 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { + EuiFlexGroup, + EuiIconTip, + EuiFlexItem, + EuiButtonEmpty, + EuiTitle, + EuiSpacer, + EuiText, +} from '@elastic/eui'; + +import { useLink } from '../../../../../hooks'; +import type { AgentPolicy } from '../../../../../types'; +import { Loading } from '../../../../../components'; + +interface HeaderLeftContentProps { + isLoading: boolean; + policyId: string; + agentPolicy?: AgentPolicy | null; +} + +export const HeaderLeftContent: React.FunctionComponent = ({ + isLoading, + policyId, + agentPolicy, +}) => { + const { getHref } = useLink(); + + return ( + + + + + + + + {isLoading ? ( + + ) : ( + + + +

+ {(agentPolicy && agentPolicy.name) || ( + + )} +

+
+
+ {agentPolicy?.is_managed && ( + + + + )} +
+ )} +
+ + {agentPolicy && agentPolicy.description ? ( + + + + {agentPolicy.description} + + + ) : null} +
+ ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/header/right_content.stories.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/header/right_content.stories.tsx new file mode 100644 index 00000000000000..d59b00abc311f6 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/header/right_content.stories.tsx @@ -0,0 +1,70 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; + +import { HeaderRightContent as Component } from './right_content'; + +export default { + component: Component, + title: 'Sections/Fleet/AgentPolicyDetails/Header/RightContent', +}; + +export const HeaderRightContent = () => { + return ( +
+ {}} + isAddAgentHelpPopoverOpen={false} + setIsAddAgentHelpPopoverOpen={() => {}} + agentPolicy={ + { + id: 'test123', + revision: 1, + updated_at: new Date().toISOString(), + package_policies: ['test1', 'test2'], + } as any + } + agentStatus={ + { + total: 0, + } as any + } + /> +
+ ); +}; + +export const HeaderRightContentWithManagedPolicy = () => { + return ( +
+ {}} + isAddAgentHelpPopoverOpen={false} + setIsAddAgentHelpPopoverOpen={() => {}} + agentPolicy={ + { + id: 'test123', + revision: 1, + updated_at: new Date().toISOString(), + package_policies: ['test1', 'test2'], + is_managed: true, + } as any + } + agentStatus={ + { + total: 0, + } as any + } + /> +
+ ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/header/right_content.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/header/right_content.tsx new file mode 100644 index 00000000000000..06f999cb596c07 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/header/right_content.tsx @@ -0,0 +1,170 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { FormattedDate, FormattedMessage } from '@kbn/i18n-react'; +import styled from 'styled-components'; +import { useHistory } from 'react-router-dom'; +import { + EuiFlexGroup, + EuiI18nNumber, + EuiFlexItem, + EuiDescriptionList, + EuiDescriptionListTitle, + EuiDescriptionListDescription, + EuiLink, +} from '@elastic/eui'; + +import { useLink } from '../../../../../hooks'; +import type { AgentPolicy, GetAgentStatusResponse } from '../../../../../types'; +import { AgentPolicyActionMenu, LinkedAgentCount } from '../../../components'; +import { AddAgentHelpPopover } from '../../../../../components'; + +export interface HeaderRightContentProps { + isLoading: boolean; + policyId: string; + agentPolicy?: AgentPolicy | null; + agentStatus?: GetAgentStatusResponse['results']; + addAgent: () => void; + onCancelEnrollment?: () => void; + isAddAgentHelpPopoverOpen: boolean; + setIsAddAgentHelpPopoverOpen: (state: boolean) => void; +} + +const Divider = styled.div` + width: 0; + height: 100%; + border-left: ${(props) => props.theme.eui.euiBorderThin}; +`; + +export const HeaderRightContent: React.FunctionComponent = ({ + isLoading, + policyId, + agentPolicy, + agentStatus, + addAgent, + onCancelEnrollment, + isAddAgentHelpPopoverOpen, + setIsAddAgentHelpPopoverOpen, +}) => { + const { getPath } = useLink(); + const history = useHistory(); + + if (!agentPolicy) { + return null; + } + const addAgentLink = ( + + + + ); + + return ( + + {[ + { + label: i18n.translate('xpack.fleet.policyDetails.summary.revision', { + defaultMessage: 'Revision', + }), + content: agentPolicy?.revision ?? 0, + }, + { isDivider: true }, + { + label: i18n.translate('xpack.fleet.policyDetails.summary.integrations', { + defaultMessage: 'Integrations', + }), + content: ( + + ), + }, + { isDivider: true }, + { + label: i18n.translate('xpack.fleet.policyDetails.summary.usedBy', { + defaultMessage: 'Agents', + }), + content: + agentStatus && agentStatus!.total ? ( + + ) : agentPolicy?.is_managed ? ( + + ) : ( + { + setIsAddAgentHelpPopoverOpen(false); + }} + /> + ), + }, + { isDivider: true }, + { + label: i18n.translate('xpack.fleet.policyDetails.summary.lastUpdated', { + defaultMessage: 'Last updated on', + }), + content: + (agentPolicy && ( + + )) || + '', + }, + { isDivider: true }, + { + content: agentPolicy && ( + { + history.push(getPath('policy_details', { policyId: newAgentPolicy.id })); + }} + onCancelEnrollment={onCancelEnrollment} + /> + ), + }, + ].map((item, index) => ( + + {item.isDivider ?? false ? ( + + ) : item.label ? ( + + + {item.label} + + + {item.content} + + + ) : ( + item.content + )} + + ))} + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/index.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/index.ts index 9b40fb39ec9124..2d1a4020bb9a9a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/index.ts +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/components/index.ts @@ -8,3 +8,4 @@ export { PackagePoliciesTable } from './package_policies/package_policies_table'; export { PackagePoliciesView } from './package_policies'; export { SettingsView } from './settings'; +export * from './header'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/index.tsx index b7aca72e8979c7..e684e8f05c8f62 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/details_page/index.tsx @@ -5,27 +5,12 @@ * 2.0. */ -import React, { useMemo, useState } from 'react'; -import { Redirect, useRouteMatch, Switch, Route, useHistory, useLocation } from 'react-router-dom'; +import React, { useMemo, useState, useCallback } from 'react'; +import { Redirect, useRouteMatch, Switch, Route, useLocation } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; -import { FormattedMessage, FormattedDate } from '@kbn/i18n-react'; -import { - EuiButtonEmpty, - EuiDescriptionList, - EuiDescriptionListDescription, - EuiDescriptionListTitle, - EuiFlexGroup, - EuiFlexItem, - EuiI18nNumber, - EuiIconTip, - EuiLink, - EuiPortal, - EuiSpacer, - EuiText, - EuiTitle, -} from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiPortal } from '@elastic/eui'; import type { Props as EuiTabProps } from '@elastic/eui/src/components/tabs/tab'; -import styled from 'styled-components'; import type { AgentPolicy, AgentPolicyDetailsDeployAgentAction } from '../../../types'; import { FLEET_ROUTING_PATHS } from '../../../constants'; @@ -38,25 +23,22 @@ import { useFleetStatus, useIntraAppState, } from '../../../hooks'; -import { Loading, Error, AgentEnrollmentFlyout, AddAgentHelpPopover } from '../../../components'; +import { Loading, Error, AgentEnrollmentFlyout } from '../../../components'; import { WithHeaderLayout } from '../../../layouts'; -import { LinkedAgentCount, AgentPolicyActionMenu } from '../components'; import { useGetAgentStatus, AgentStatusRefreshContext } from './hooks'; -import { PackagePoliciesView, SettingsView } from './components'; - -const Divider = styled.div` - width: 0; - height: 100%; - border-left: ${(props) => props.theme.eui.euiBorderThin}; -`; +import { + PackagePoliciesView, + SettingsView, + HeaderRightContent, + HeaderLeftContent, +} from './components'; export const AgentPolicyDetailsPage: React.FunctionComponent = () => { const { params: { policyId, tabId = '' }, } = useRouteMatch<{ policyId: string; tabId?: string }>(); - const history = useHistory(); - const { getHref, getPath } = useLink(); + const { getHref } = useLink(); const agentPolicyRequest = useGetOneAgentPolicy(policyId); const agentPolicy = agentPolicyRequest.data ? agentPolicyRequest.data.item : null; const { isLoading, error, sendRequest: refreshAgentPolicy } = agentPolicyRequest; @@ -70,6 +52,7 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { const [isAddAgentHelpPopoverOpen, setIsAddAgentHelpPopoverOpen] = useState( openAddAgentHelpPopoverOpenByDefault ); + const agentStatusRequest = useGetAgentStatus(policyId); const { refreshAgentStatus } = agentStatusRequest; const { @@ -80,74 +63,6 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { const { isReady: isFleetReady } = useFleetStatus(); - const headerLeftContent = useMemo( - () => ( - - - - - - - - {isLoading ? ( - - ) : ( - - - -

- {(agentPolicy && agentPolicy.name) || ( - - )} -

-
-
- {agentPolicy?.is_managed && ( - - - - )} -
- )} -
- - {agentPolicy && agentPolicy.description ? ( - - - - {agentPolicy.description} - - - ) : null} -
- ), - [getHref, isLoading, agentPolicy, policyId] - ); - const onCancelEnrollment = useMemo(() => { if (routeState && routeState.onDoneNavigateTo && isFleetReady) { const [appId, options] = routeState.onDoneNavigateTo; @@ -157,118 +72,10 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { return undefined; }, [isFleetReady, navigateToApp, routeState]); - const addAgentLink = ( - { - setIsAddAgentHelpPopoverOpen(false); - setIsEnrollmentFlyoutOpen(true); - }} - > - - - ); - const headerRightContent = useMemo( - () => - agentPolicy ? ( - - {[ - { - label: i18n.translate('xpack.fleet.policyDetails.summary.revision', { - defaultMessage: 'Revision', - }), - content: agentPolicy?.revision ?? 0, - }, - { isDivider: true }, - { - label: i18n.translate('xpack.fleet.policyDetails.summary.integrations', { - defaultMessage: 'Integrations', - }), - content: ( - - ), - }, - { isDivider: true }, - { - label: i18n.translate('xpack.fleet.policyDetails.summary.usedBy', { - defaultMessage: 'Agents', - }), - content: - agentStatus && agentStatus!.total ? ( - - ) : ( - { - setIsAddAgentHelpPopoverOpen(false); - }} - /> - ), - }, - { isDivider: true }, - { - label: i18n.translate('xpack.fleet.policyDetails.summary.lastUpdated', { - defaultMessage: 'Last updated on', - }), - content: - (agentPolicy && ( - - )) || - '', - }, - { isDivider: true }, - { - content: agentPolicy && ( - { - history.push(getPath('policy_details', { policyId: newAgentPolicy.id })); - }} - onCancelEnrollment={onCancelEnrollment} - /> - ), - }, - ].map((item, index) => ( - - {item.isDivider ?? false ? ( - - ) : item.label ? ( - - - {item.label} - - - {item.content} - - - ) : ( - item.content - )} - - ))} - - ) : undefined, - /* eslint-disable-next-line react-hooks/exhaustive-deps */ - [agentPolicy, policyId, agentStatus, isAddAgentHelpPopoverOpen] - ); + const addAgent = useCallback(() => { + setIsAddAgentHelpPopoverOpen(false); + setIsEnrollmentFlyoutOpen(true); + }, []); const headerTabs = useMemo(() => { return [ @@ -355,6 +162,21 @@ export const AgentPolicyDetailsPage: React.FunctionComponent = () => { onCancelEnrollment, policyId, ]); + const headerLeftContent = ( + + ); + const headerRightContent = ( + + ); return (