From e8e10fb70395904ac219b1d30403a79e9241dd8d Mon Sep 17 00:00:00 2001 From: Cristina Amico Date: Thu, 3 Oct 2024 09:43:47 +0200 Subject: [PATCH] [Fleet] Improve reusable integration policies flow in package policies UI (#193532) Closes https://github.com/elastic/kibana/issues/186357 ## Summary Improve package policies list table support for reusable agent policies. This PR tweaks some parts of this UI: - The agent count is now an aggregation of the total agents on all the shared agent policies. Clicking on this count opens up a new pop up that shows the number of agents for each policy and allows the agent to navigate either to those agents or all of them. The list shows only the top 5 policies by agent count. ![Screenshot 2024-09-30 at 15 45 16](https://github.com/user-attachments/assets/d56b53fd-ce6b-48cf-bc5b-0b5a1f0a7cca) ![Screenshot 2024-09-30 at 14 56 21](https://github.com/user-attachments/assets/831dd34c-d44d-446f-bbb7-0ee146b671e8) - When clicking on "add agent", either from the actions or from the button, we now show the agent policy selector as first step of the flyout, so the user can choose which policy wants to enroll agents to. Currently instead the policy is preselected ![Screenshot 2024-10-01 at 11 06 39](https://github.com/user-attachments/assets/aaba7f0f-21c9-4232-87db-62a4226764df) ### Checklist - [ ] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/packages/kbn-i18n/README.md) - [ ] [Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials - [ ] [Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios --------- Co-authored-by: Elastic Machine Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .../package_policy_agents_cell.test.tsx | 205 +++++++++++---- .../components/package_policy_agents_cell.tsx | 241 ++++++++++++++---- .../detail/policies/package_policies.tsx | 39 ++- .../agent_enrollment_flyout/index.tsx | 6 +- .../agent_enrollment_flyout/types.ts | 2 +- .../public/components/linked_agent_count.tsx | 6 +- .../package_policy_actions_menu.tsx | 5 +- 7 files changed, 388 insertions(+), 116 deletions(-) diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.test.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.test.tsx index c1bba8284548a..6531d2e72e098 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.test.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.test.tsx @@ -7,85 +7,194 @@ import React from 'react'; -import { act } from '@testing-library/react'; +import { act, fireEvent } from '@testing-library/react'; import { createIntegrationsTestRendererMock } from '../../../../../../../../mock'; import type { AgentPolicy } from '../../../../../../types'; +import { useAuthz, useMultipleAgentPolicies } from '../../../../../../hooks'; import { PackagePolicyAgentsCell } from './package_policy_agents_cell'; +jest.mock('../../../../../../hooks', () => ({ + ...jest.requireActual('../../../../../../hooks'), + useAuthz: jest.fn(), + useMultipleAgentPolicies: jest.fn(), + useConfirmForceInstall: jest.fn(), +})); + +const useMultipleAgentPoliciesMock = useMultipleAgentPolicies as jest.MockedFunction< + typeof useMultipleAgentPolicies +>; function renderCell({ - agentCount = 0, - agentPolicy = {} as AgentPolicy, + agentPolicies = [] as AgentPolicy[], onAddAgent = () => {}, hasHelpPopover = false, - canAddAgents = true, }) { const renderer = createIntegrationsTestRendererMock(); return renderer.render( ); } describe('PackagePolicyAgentsCell', () => { - test('it should display add agent if count is 0', async () => { - const utils = renderCell({ agentCount: 0 }); - await act(async () => { - expect(utils.queryByText('Add agent')).toBeInTheDocument(); - }); + beforeEach(() => { + jest.mocked(useAuthz).mockReturnValue({ + fleet: { + addAgents: true, + addFleetServers: true, + }, + } as any); }); - test('it should not display add agent if policy is managed', async () => { - const utils = renderCell({ - agentCount: 0, - agentPolicy: { - is_managed: true, - } as AgentPolicy, + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('when multiple agent policies is disabled', () => { + beforeEach(() => { + useMultipleAgentPoliciesMock.mockReturnValue({ canUseMultipleAgentPolicies: false }); }); - await act(async () => { - expect(utils.queryByText('Add agent')).not.toBeInTheDocument(); + + test('it should display add agent button if count is 0', async () => { + const utils = renderCell({ + agentPolicies: [ + { + name: 'test Policy 1', + } as AgentPolicy, + ], + }); + utils.debug(); + await act(async () => { + expect(utils.queryByText('Add agent')).toBeInTheDocument(); + }); }); - }); - test('it should display only count if count > 0', async () => { - const utils = renderCell({ agentCount: 9999 }); - await act(async () => { - expect(utils.queryByText('Add agent')).not.toBeInTheDocument(); - expect(utils.queryByText('9999')).toBeInTheDocument(); + test('it should display only count if count > 0', async () => { + const utils = renderCell({ + agentPolicies: [ + { + name: 'test Policy 1', + agents: 999, + } as AgentPolicy, + ], + }); + await act(async () => { + expect(utils.queryByText('Add agent')).not.toBeInTheDocument(); + expect(utils.queryByText('999')).toBeInTheDocument(); + }); }); - }); - test('it should display help popover if count is 0 and hasHelpPopover=true', async () => { - const utils = renderCell({ agentCount: 0, hasHelpPopover: true }); - await act(async () => { - expect(utils.queryByText('9999')).not.toBeInTheDocument(); - expect(utils.queryByText('Add agent')).toBeInTheDocument(); - expect( - utils.container.querySelector('[data-test-subj="addAgentHelpPopover"]') - ).toBeInTheDocument(); + test('it should not display help popover if count is > 0 and hasHelpPopover=true', async () => { + const utils = renderCell({ + agentPolicies: [ + { + name: 'test Policy 1', + agents: 999, + } as AgentPolicy, + ], + hasHelpPopover: true, + }); + await act(async () => { + expect(utils.queryByText('999')).toBeInTheDocument(); + expect(utils.queryByText('Add agent')).not.toBeInTheDocument(); + expect( + utils.container.querySelector('[data-test-subj="addAgentHelpPopover"]') + ).not.toBeInTheDocument(); + }); }); - }); - test('it should not display help popover if count is > 0 and hasHelpPopover=true', async () => { - const utils = renderCell({ agentCount: 9999, hasHelpPopover: true }); - await act(async () => { - expect(utils.queryByText('9999')).toBeInTheDocument(); - expect(utils.queryByText('Add agent')).not.toBeInTheDocument(); - expect( - utils.container.querySelector('[data-test-subj="addAgentHelpPopover"]') - ).not.toBeInTheDocument(); + + test('it should display help popover if count = 0 and hasHelpPopover=true', async () => { + const utils = renderCell({ + hasHelpPopover: true, + agentPolicies: [ + { + name: 'test Policy 1', + } as AgentPolicy, + ], + }); + await act(async () => { + expect(utils.queryByText('9999')).not.toBeInTheDocument(); + expect(utils.queryByText('Add agent')).toBeInTheDocument(); + expect( + utils.container.querySelector('[data-test-subj="addAgentHelpPopover"]') + ).toBeInTheDocument(); + }); + }); + + test('it should not display add agent button if policy is managed', async () => { + const utils = renderCell({ + agentPolicies: [ + { + name: 'test Policy 1', + agents: 999, + is_managed: true, + } as AgentPolicy, + ], + }); + utils.debug(); + await act(async () => { + expect(utils.queryByText('Add agent')).not.toBeInTheDocument(); + expect(utils.queryByTestId('LinkedAgentCountLink')).toBeInTheDocument(); + expect(utils.queryByText('999')).toBeInTheDocument(); + }); + }); + + test('Add agent button should be disabled if canAddAgents is false', async () => { + jest.mocked(useAuthz).mockReturnValue({ + fleet: { + addAgents: false, + }, + } as any); + + const utils = renderCell({ + agentPolicies: [ + { + name: 'test Policy 1', + } as AgentPolicy, + ], + }); + await act(async () => { + expect(utils.container.querySelector('[data-test-subj="addAgentButton"]')).toBeDisabled(); + }); }); }); - test('it should be disabled if canAddAgents is false', async () => { - const utils = renderCell({ agentCount: 0, canAddAgents: false }); - await act(async () => { - expect(utils.container.querySelector('[data-test-subj="addAgentButton"]')).toBeDisabled(); + + describe('when multiple agent policies is enabled', () => { + beforeEach(() => { + useMultipleAgentPoliciesMock.mockReturnValue({ canUseMultipleAgentPolicies: true }); + }); + + test('it should display agent count sum and popover if agent count > 0', async () => { + jest.mocked(useAuthz).mockReturnValue({ + fleet: { + addAgents: false, + }, + } as any); + + const utils = renderCell({ + agentPolicies: [ + { + name: 'test Policy 1', + agents: 100, + } as AgentPolicy, + { + name: 'test Policy 2', + agents: 200, + } as AgentPolicy, + ], + }); + await act(async () => { + expect(utils.queryByText('300')).toBeInTheDocument(); + expect(utils.queryByText('Add agent')).not.toBeInTheDocument(); + const button = utils.getByTestId('agentsCountsButton'); + fireEvent.click(button); + expect(utils.queryByTestId('agentCountsPopover')).toBeInTheDocument(); + }); }); }); }); diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.tsx index 1ede5b09cea99..35e6e8634e34a 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/components/package_policy_agents_cell.tsx @@ -5,83 +5,238 @@ * 2.0. */ -import React, { useState } from 'react'; +import React, { Fragment, useState, useMemo } from 'react'; -import { EuiButton } from '@elastic/eui'; +import { + EuiButton, + EuiLink, + EuiPopover, + EuiPopoverTitle, + EuiHorizontalRule, + EuiSpacer, + EuiText, + EuiFlexGroup, + EuiFlexItem, + EuiPopoverFooter, + EuiButtonEmpty, +} from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import { i18n } from '@kbn/i18n'; +import { sortBy } from 'lodash'; import { LinkedAgentCount, AddAgentHelpPopover } from '../../../../../../components'; import type { AgentPolicy } from '../../../../../../types'; +import { policyHasFleetServer } from '../../../../../../services'; +import { useAuthz, useLink, useMultipleAgentPolicies } from '../../../../../../hooks'; +import { + PRIVILEGED_AGENT_KUERY, + UNPRIVILEGED_AGENT_KUERY, + AGENTS_PREFIX, +} from '../../../../../../constants'; const AddAgentButton = ({ onAddAgent, canAddAgents, + withPopover, }: { onAddAgent: () => void; canAddAgents: boolean; -}) => ( - - - -); - -const AddAgentButtonWithPopover = ({ - onAddAgent, - canAddAgents, -}: { - onAddAgent: () => void; - canAddAgents: boolean; + withPopover?: boolean; }) => { const [isHelpOpen, setIsHelpOpen] = useState(true); const onAddAgentCloseHelp = () => { setIsHelpOpen(false); onAddAgent(); }; - const button = ; - return ( + return withPopover ? ( + + + } isOpen={isHelpOpen} closePopover={() => setIsHelpOpen(false)} /> + ) : ( + + + ); }; export const PackagePolicyAgentsCell = ({ - agentPolicy, - agentCount = 0, + agentPolicies, onAddAgent, hasHelpPopover = false, - canAddAgents, }: { - agentPolicy: AgentPolicy; - agentCount?: number; + agentPolicies: AgentPolicy[]; hasHelpPopover?: boolean; onAddAgent: () => void; - canAddAgents: boolean; }) => { - if (agentCount > 0 || agentPolicy.is_managed) { - return ( - - ); + const { canUseMultipleAgentPolicies } = useMultipleAgentPolicies(); + + const agentCount = agentPolicies.reduce((acc, curr) => { + return (acc += curr?.agents || 0); + }, 0); + + const canAddAgents = useAuthz().fleet.addAgents; + const canAddFleetServers = useAuthz().fleet.addFleetServers; + + if (canUseMultipleAgentPolicies && agentCount > 0 && agentPolicies.length > 1) { + return ; } - if (!hasHelpPopover || !canAddAgents) { - return ; + if (!canUseMultipleAgentPolicies || (agentCount > 0 && agentPolicies.length === 1)) { + const agentPolicy = agentPolicies[0]; + const canAddAgentsForPolicy = policyHasFleetServer(agentPolicy) + ? canAddFleetServers + : canAddAgents; + if (agentCount > 0 || agentPolicy.is_managed) + return ( + + ); + else { + ; + } } + return ( + + ); +}; + +export const AgentsCountBreakDown = ({ + agentPolicies, + agentCount, + privilegeMode, +}: { + agentPolicies: AgentPolicy[]; + agentCount: number; + privilegeMode?: 'privileged' | 'unprivileged'; +}) => { + const { getHref } = useLink(); + const authz = useAuthz(); + + const canReadAgents = authz.fleet.readAgents; - return ; + const [isPopoverOpen, setIsPopoverOpen] = useState(false); + const closePopover = () => setIsPopoverOpen(false); + + const getKuery = (agentPolicyId: string) => + `${AGENTS_PREFIX}.policy_id : "${agentPolicyId}"${ + privilegeMode + ? ` and ${ + privilegeMode === 'unprivileged' ? UNPRIVILEGED_AGENT_KUERY : PRIVILEGED_AGENT_KUERY + }` + : '' + }`; + const topFivePolicies = useMemo( + () => sortBy(agentPolicies, 'agents').reverse().slice(0, 5), + [agentPolicies] + ); + + return ( + <> + setIsPopoverOpen(!isPopoverOpen)} + data-test-subj="agentsCountsButton" + > + {agentCount} + + } + > + + {i18n.translate('xpack.fleet.agentsCountsBreakdown.popover.title', { + defaultMessage: 'Agents breakdown', + })} + +
+ + + {i18n.translate('xpack.fleet.agentsCountsBreakdown.popover.heading', { + defaultMessage: 'Top values', + })} + + + + {topFivePolicies.map((agentPolicy) => ( + + + + + {agentPolicy.name} + + + + {agentPolicy?.agents && agentPolicy.agents > 0 ? ( + + {agentPolicy.agents} + + ) : ( + 0 + )} + + + + + ))} + + {agentCount > 0 ? ( + + + {i18n.translate('xpack.fleet.agentsCountsBreakdown.popover.button', { + defaultMessage: 'View all {agentCount, plural, one {# agent} other {# agents}}', + values: { agentCount }, + })} + + + ) : null} +
+
+ + ); }; diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx index b475c4d39d767..300de597f6900 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/policies/package_policies.tsx @@ -20,8 +20,6 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedRelative, FormattedMessage } from '@kbn/i18n-react'; -import { policyHasFleetServer } from '../../../../../../../../common/services'; - import { InstallStatus } from '../../../../../types'; import type { GetAgentPoliciesResponseItem, InMemoryPackagePolicy } from '../../../../../types'; import { @@ -54,6 +52,7 @@ interface PackagePoliciesPanelProps { interface InMemoryPackagePolicyAndAgentPolicy { packagePolicy: InMemoryPackagePolicy; agentPolicies: GetAgentPoliciesResponseItem[]; + rowIndex: number; } const IntegrationDetailsLink = memo<{ @@ -89,6 +88,8 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps const [flyoutOpenForPolicyId, setFlyoutOpenForPolicyId] = useState( agentPolicyIdFromParams ); + const [selectedTableIndex, setSelectedTableIndex] = useState(); + const { getPath, getHref } = useLink(); const getPackageInstallStatus = useGetPackageInstallStatus(); const packageInstallStatus = getPackageInstallStatus(name); @@ -108,19 +109,18 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps const canWriteIntegrationPolicies = useAuthz().integrations.writeIntegrationPolicies; const canReadIntegrationPolicies = useAuthz().integrations.readIntegrationPolicies; - const canAddAgents = useAuthz().fleet.addAgents; - const canAddFleetServers = useAuthz().fleet.addFleetServers; const canReadAgentPolicies = useAuthz().fleet.readAgentPolicies; const packageAndAgentPolicies = useMemo((): Array<{ agentPolicies: GetAgentPoliciesResponseItem[]; packagePolicy: InMemoryPackagePolicy; + rowIndex: number; }> => { if (!data?.items) { return []; } - const newPolicies = data.items.map(({ agentPolicies, packagePolicy }) => { + const newPolicies = data.items.map(({ agentPolicies, packagePolicy }, index) => { const hasUpgrade = isPackagePolicyUpgradable(packagePolicy); return { @@ -129,6 +129,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps ...packagePolicy, hasUpgrade, }, + rowIndex: index, }; }); @@ -283,7 +284,7 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps name: i18n.translate('xpack.fleet.epm.packageDetails.integrationList.agentCount', { defaultMessage: 'Agents', }), - render({ agentPolicies, packagePolicy }: InMemoryPackagePolicyAndAgentPolicy) { + render({ agentPolicies, packagePolicy, rowIndex }: InMemoryPackagePolicyAndAgentPolicy) { if (agentPolicies.length === 0) { return ( @@ -294,16 +295,13 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps ); } - const agentPolicy = agentPolicies[0]; // TODO: handle multiple agent policies - const canAddAgentsForPolicy = policyHasFleetServer(agentPolicy) - ? canAddFleetServers - : canAddAgents; return ( setFlyoutOpenForPolicyId(agentPolicy.id)} - canAddAgents={canAddAgentsForPolicy} + agentPolicies={agentPolicies} + onAddAgent={() => { + setSelectedTableIndex(rowIndex); + setFlyoutOpenForPolicyId(agentPolicies[0].id); + }} hasHelpPopover={showAddAgentHelpForPackagePolicyId === packagePolicy.id} /> ); @@ -340,8 +338,6 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps getHref, canWriteIntegrationPolicies, canShowMultiplePoliciesCell, - canAddFleetServers, - canAddAgents, showAddAgentHelpForPackagePolicyId, refreshPolicies, ] @@ -373,11 +369,13 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps ); } - const selectedPolicies = packageAndAgentPolicies.find(({ agentPolicies: policies }) => - policies.find((policy) => policy.id === flyoutOpenForPolicyId) - ); + + const selectedPolicies = + selectedTableIndex !== undefined ? packageAndAgentPolicies[selectedTableIndex] : undefined; + const agentPolicies = selectedPolicies?.agentPolicies; const packagePolicy = selectedPolicies?.packagePolicy; + const flyoutPolicy = agentPolicies?.length === 1 ? agentPolicies[0] : undefined; return ( @@ -402,7 +400,8 @@ export const PackagePoliciesPage = ({ name, version }: PackagePoliciesPanelProps const { addAgentToPolicyId, ...rest } = parse(search); history.replace({ search: stringify(rest) }); }} - agentPolicy={agentPolicies[0]} + agentPolicy={flyoutPolicy} + selectedAgentPolicies={agentPolicies} isIntegrationFlow={true} installedPackagePolicy={{ name: packagePolicy?.package?.name || '', diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx index 5ccdf37951703..46323eab29b43 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/index.tsx @@ -53,6 +53,7 @@ export * from './steps'; export const AgentEnrollmentFlyout: React.FunctionComponent = ({ onClose, agentPolicy, + selectedAgentPolicies, defaultMode = 'managed', isIntegrationFlow, installedPackagePolicy, @@ -69,12 +70,15 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ const [selectionType, setSelectionType] = useState(); const { - agentPolicies, + agentPolicies: fetchedAgentPolicies, isLoadingInitialAgentPolicies, isLoadingAgentPolicies, refreshAgentPolicies, } = useAgentEnrollmentFlyoutData(); + // Have the option to pass agentPolicies from props, otherwise use the fetched ones + const agentPolicies = selectedAgentPolicies ? selectedAgentPolicies : fetchedAgentPolicies; + const { agentPolicyWithPackagePolicies } = useAgentPolicyWithPackagePolicies(selectedPolicyId); const { diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts index a58feeda65617..0c2e51e294b14 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/types.ts @@ -47,7 +47,6 @@ export interface BaseProps { * The user selected policy to be used. If this value is `undefined` a value must be provided for `agentPolicies`. */ agentPolicy?: AgentPolicy; - isFleetServerPolicySelected?: boolean; isK8s?: K8sMode; @@ -65,6 +64,7 @@ export interface BaseProps { export interface FlyOutProps extends BaseProps { onClose: () => void; defaultMode?: FlyoutMode; + selectedAgentPolicies?: AgentPolicy[]; } export interface InstructionProps extends BaseProps { diff --git a/x-pack/plugins/fleet/public/components/linked_agent_count.tsx b/x-pack/plugins/fleet/public/components/linked_agent_count.tsx index 5b92ac3e96f6e..ff37287ae91ef 100644 --- a/x-pack/plugins/fleet/public/components/linked_agent_count.tsx +++ b/x-pack/plugins/fleet/public/components/linked_agent_count.tsx @@ -43,7 +43,11 @@ export const LinkedAgentCount = memo< }`; return count > 0 ? ( - + {displayValue} ) : ( diff --git a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx index 4da1711b28313..b28cf97b464a9 100644 --- a/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx +++ b/x-pack/plugins/fleet/public/components/package_policy_actions_menu.tsx @@ -37,7 +37,7 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{ const { getHref } = useLink(); const authz = useAuthz(); - const agentPolicy = agentPolicies.length > 0 ? agentPolicies[0] : undefined; // TODO: handle multiple agent policies + const agentPolicy = agentPolicies.length > 0 ? agentPolicies[0] : undefined; const canWriteIntegrationPolicies = authz.integrations.writeIntegrationPolicies; const isFleetServerPolicy = agentPolicy && policyHasFleetServer(agentPolicy); @@ -168,7 +168,8 @@ export const PackagePolicyActionsMenu: React.FunctionComponent<{ {isEnrollmentFlyoutOpen && (