From 9551f4b8c765a2d6c8858d0fc0c5cad4799afd8e Mon Sep 17 00:00:00 2001 From: Angela Chuang <6295984+angorayc@users.noreply.github.com> Date: Tue, 10 Dec 2024 17:36:17 +0000 Subject: [PATCH] Onboarding integration flow fleet (#203443) ## Summary Summarize your PR. If it involves visual changes include a screenshot or gif. ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [ ] 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 - [ ] If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker) - [ ] This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations. - [ ] [Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed - [ ] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) ### Identify risks Does this PR introduce any risks? For example, consider risks like hard to test bugs, performance regression, potential of data loss. Describe the risk, its severity, and mitigation for each identified risk. Invite stakeholders and evaluate how to proceed before merging. - [ ] [See some risk examples](https://github.com/elastic/kibana/blob/main/RISK_MATRIX.mdx) - [ ] ... --- .../advanced_tab.tsx | 3 + .../fleet_server_instructions/index.tsx | 21 ++- .../quick_start_tab.tsx | 7 +- .../steps/confirm_fleet_server_connection.tsx | 8 +- .../embedded_integration_steps_layout.tsx | 37 +++++ .../embedded_integration_flow/index.tsx | 149 +++++++++++++++++ .../onboarding_steps/add_fleet_server.tsx | 43 +++++ .../onboarding_steps/agent_enrollment.tsx | 122 ++++++++++++++ .../check_fleet_server_required.tsx | 82 ++++++++++ .../confirm_incoming_data.tsx | 150 ++++++++++++++++++ .../create_package_policy.tsx | 24 +++ .../onboarding_steps/index.ts | 60 +++++++ .../embedded_integration_flow/types.ts | 56 +++++++ .../create_package_policy_page/index.tsx | 31 ++-- .../components/bottom_bar.tsx | 100 ++++++------ .../confirm_incoming_data_with_preview.tsx | 5 +- .../confirming_incoming_data_standalone.tsx | 5 +- .../components/multi_page_steps_layout.tsx | 1 + .../components/page_steps/confirm_data.tsx | 23 +-- .../hooks/use_get_agent_policy_or_default.tsx | 3 +- .../multi_page_layout/index.tsx | 49 +----- .../single_page_layout/components/layout.tsx | 7 +- .../single_page_layout/hooks/form.tsx | 22 +-- .../single_page_layout/index.tsx | 46 ++++-- .../create_package_policy_page/types.ts | 15 +- .../components/enrollment_recommendation.tsx | 5 +- .../fleet_server_requirement_page.tsx | 13 +- .../hooks/use_fleet_integration_context.tsx | 5 +- .../hooks/use_integrations_state.tsx | 4 +- .../sections/epm/screens/detail/index.tsx | 44 ++--- .../confirm_agent_enrollment.tsx | 13 ++ .../agent_enrollment_flyout/instructions.tsx | 42 +++-- .../agent_enrollment_confirmation_step.tsx | 3 + .../steps/compute_steps.tsx | 11 +- .../agent_enrollment_flyout/types.ts | 14 +- .../fleet/public/hooks/use_fleet_status.tsx | 2 +- x-pack/plugins/fleet/public/index.ts | 1 - .../public/types/intra_app_route_state.ts | 7 + .../integration_card_grid_tabs.tsx | 53 ++++--- .../integrations/use_integration_card_list.ts | 1 + 40 files changed, 1072 insertions(+), 215 deletions(-) create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/embedded_integration_steps_layout.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/index.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/add_fleet_server.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/agent_enrollment.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/check_fleet_server_required.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/confirm_incoming_data.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/create_package_policy.tsx create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/index.ts create mode 100644 x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/types.ts diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx index 2eacab9bbaa49..5e88e69682d79 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/advanced_tab.tsx @@ -22,11 +22,13 @@ import { interface AdvancedTabProps { onClose: () => void; selectedPolicyId?: string; + handleContinueAddingAgentClick?: () => void; } export const AdvancedTab: React.FunctionComponent = ({ selectedPolicyId, onClose, + handleContinueAddingAgentClick, }) => { const { isSelectFleetServerPolicyLoading, @@ -81,6 +83,7 @@ export const AdvancedTab: React.FunctionComponent = ({ getConfirmFleetServerConnectionStep({ hasRecentlyEnrolledFleetServers, disabled: !Boolean(serviceToken), + handleContinueAddingAgentClick, }), ]; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/index.tsx index b46d4f2060669..e8acf2597df6b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/index.tsx @@ -39,20 +39,33 @@ interface Props { onClose: () => void; } -const useFleetServerTabs = (onClose: () => void) => { +export const useFleetServerTabs = ( + onClose: () => void, + handleContinueAddingAgentClick?: () => void +) => { const [currentTab, setCurrentTab] = useState('quickStart'); const quickStartTab = { id: 'quickStart', label: 'Quick Start', - content: , + content: ( + + ), 'data-test-subj': 'fleetServerFlyoutTab-quickStart', }; const advancedTab = { id: 'advanced', label: 'Advanced', - content: , + content: ( + + ), 'data-test-subj': 'fleetServerFlyoutTab-advanced', }; @@ -62,7 +75,7 @@ const useFleetServerTabs = (onClose: () => void) => { return { tabs: [quickStartTab, advancedTab], currentTab, setCurrentTab, currentTabContent }; }; -const Header: React.FunctionComponent<{ +export const Header: React.FunctionComponent<{ isFlyout?: boolean; currentTab: string; tabs: Array<{ id: string; label: string; content: React.ReactNode }>; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx index 959b1d08d7863..c3334ce28cc2a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/quick_start_tab.tsx @@ -19,9 +19,13 @@ import { useLatestFleetServers } from './hooks/use_latest_fleet_servers'; interface Props { onClose: () => void; + handleContinueAddingAgentClick?: () => void; } -export const QuickStartTab: React.FunctionComponent = ({ onClose }) => { +export const QuickStartTab: React.FunctionComponent = ({ + onClose, + handleContinueAddingAgentClick, +}) => { const { fleetServerHost, setFleetServerHost, @@ -62,6 +66,7 @@ export const QuickStartTab: React.FunctionComponent = ({ onClose }) => { getConfirmFleetServerConnectionStep({ hasRecentlyEnrolledFleetServers, disabled: status !== 'success', + handleContinueAddingAgentClick, }), ]; diff --git a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/confirm_fleet_server_connection.tsx b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/confirm_fleet_server_connection.tsx index de6ff3f95fbf9..ae0b36738a77b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/confirm_fleet_server_connection.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/components/fleet_server_instructions/steps/confirm_fleet_server_connection.tsx @@ -18,9 +18,11 @@ import { useFleetStatus, useFlyoutContext } from '../../../hooks'; export function getConfirmFleetServerConnectionStep({ disabled, hasRecentlyEnrolledFleetServers, + handleContinueAddingAgentClick, }: { disabled: boolean; hasRecentlyEnrolledFleetServers: boolean; + handleContinueAddingAgentClick?: () => void; }): EuiStepProps { return { title: hasRecentlyEnrolledFleetServers @@ -34,6 +36,7 @@ export function getConfirmFleetServerConnectionStep({ children: !disabled && ( ), }; @@ -41,7 +44,8 @@ export function getConfirmFleetServerConnectionStep({ const ConfirmFleetServerConnectionStepContent: React.FunctionComponent<{ hasRecentlyEnrolledFleetServers: boolean; -}> = ({ hasRecentlyEnrolledFleetServers }) => { + handleContinueAddingAgentClick?: () => void; +}> = ({ handleContinueAddingAgentClick, hasRecentlyEnrolledFleetServers }) => { const flyoutContext = useFlyoutContext(); const fleetStatus = useFleetStatus(); @@ -64,7 +68,7 @@ const ConfirmFleetServerConnectionStepContent: React.FunctionComponent<{ = (props) => { + const { steps, currentStep, error } = props; + + if (error) { + return ( + + } + error={error} + /> + ); + } + + const StepComponent = steps[currentStep].component; + + return ; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/index.tsx new file mode 100644 index 0000000000000..5cc12169f2369 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/index.tsx @@ -0,0 +1,149 @@ +/* + * 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, { useCallback, useEffect, useMemo, useState } from 'react'; +import { i18n } from '@kbn/i18n'; + +import { splitPkgKey } from '../../../../../../../common/services'; + +import { + useGetPackageInfoByKeyQuery, + useFleetServerHostsForPolicy, + useStartServices, +} from '../../../../hooks'; + +import { useIntegrationsStateContext } from '../../../../../integrations/hooks'; + +import type { AgentPolicy } from '../../../../types'; +import { useGetAgentPolicyOrDefault } from '../multi_page_layout/hooks'; +import { Loading } from '../../../../components'; + +import { onboardingManagedSteps } from './onboarding_steps'; + +import { EmbeddedIntegrationStepsLayout } from './embedded_integration_steps_layout'; +import type { EmbeddedIntegrationFlowProps } from './types'; + +export const EmbeddedIntegrationFlow: React.FC = ({ + prerelease, + integrationName: integration, + onStepNext, + onCancel, + handleViewAssets, + from, +}) => { + const { notifications } = useStartServices(); + + const { pkgkey: pkgKeyContext } = useIntegrationsStateContext(); + const pkgkey = pkgKeyContext || ''; + const { pkgName, pkgVersion } = splitPkgKey(pkgkey); + const [currentStep, setCurrentStep] = useState(0); + const [isManaged, setIsManaged] = useState(true); + const [enrolledAgentIds, setEnrolledAgentIds] = useState([]); + const [selectedAgentPolicies, setSelectedAgentPolicies] = useState(); + const toggleIsManaged = (newIsManaged: boolean) => { + setIsManaged(newIsManaged); + setCurrentStep(0); + }; + + const agentPolicyId = useMemo(() => selectedAgentPolicies?.[0]?.id, [selectedAgentPolicies]); + const { + data: packageInfoData, + error: packageInfoError, + isLoading: isPackageInfoLoading, + } = useGetPackageInfoByKeyQuery(pkgName, pkgVersion, { prerelease, full: true }); + + const { + agentPolicy, + enrollmentAPIKey, + error: agentPolicyError, + isLoading: isAgentPolicyLoading, + } = useGetAgentPolicyOrDefault(agentPolicyId); + + const packageInfo = useMemo(() => packageInfoData?.item, [packageInfoData]); + + const integrationInfo = useMemo(() => { + if (!integration) return; + return packageInfo?.policy_templates?.find( + (policyTemplate) => policyTemplate.name === integration + ); + }, [packageInfo?.policy_templates, integration]); + + const { fleetServerHost, fleetProxy, downloadSource } = useFleetServerHostsForPolicy(agentPolicy); + + const stepsNext = useCallback( + (props?: { selectedAgentPolicies?: AgentPolicy[]; toStep?: number }) => { + if (currentStep === onboardingManagedSteps.length - 1) { + return; + } + + setCurrentStep(props?.toStep ?? currentStep + 1); + onStepNext?.(props?.toStep ?? currentStep + 1); + + // selected agent policy is set after integration is configured + if (props?.selectedAgentPolicies) { + setSelectedAgentPolicies(props?.selectedAgentPolicies); + } + }, + [currentStep, onStepNext] + ); + + const stepsBack = () => { + if (currentStep === 0) { + return; + } + + setCurrentStep(currentStep - 1); + }; + + useEffect(() => { + if (!isPackageInfoLoading && packageInfoError) { + notifications.toasts.addError(packageInfoError, { + title: i18n.translate('xpack.fleet.createPackagePolicy.errorLoadingPackageTitle', { + defaultMessage: 'Error loading package information', + }), + toastMessage: packageInfoError.message, + }); + } + }, [isPackageInfoLoading, notifications.toasts, packageInfoError]); + + useEffect(() => { + if (!isAgentPolicyLoading && agentPolicyError) { + notifications.toasts.addError(agentPolicyError, { + title: i18n.translate('xpack.fleet.createPackagePolicy.errorLoadingAgentPolicyTitle', { + defaultMessage: 'Error loading agent policy information', + }), + toastMessage: agentPolicyError.message, + }); + } + }, [isAgentPolicyLoading, notifications.toasts, agentPolicyError]); + + return !isPackageInfoLoading && packageInfo ? ( + + ) : ( + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/add_fleet_server.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/add_fleet_server.tsx new file mode 100644 index 0000000000000..c62f04e5efd8e --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/add_fleet_server.tsx @@ -0,0 +1,43 @@ +/* + * 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 { EuiSpacer } from '@elastic/eui'; + +import { useCheckPermissions } from '../../../../../hooks'; + +import { useFleetServerTabs } from '../../../../../components'; +import { Header } from '../../../../../components/fleet_server_instructions'; +import { FleetServerMissingESPrivileges } from '../../../../agents/components'; +import type { EmbeddedIntegrationStepsLayoutProps } from '../types'; + +export const AddFleetServerStepFromOnboardingHub: React.FC = ({ + onCancel, + onNext, +}) => { + const { tabs, currentTab, setCurrentTab, currentTabContent } = useFleetServerTabs( + onCancel, + onNext + ); + + const { permissionsError, isPermissionsLoading } = useCheckPermissions(); + + return ( + <> +
setCurrentTab(id)} /> + {!isPermissionsLoading && permissionsError === 'MISSING_FLEET_SERVER_SETUP_PRIVILEGES' ? ( + + ) : ( + <> + + {currentTabContent} + + )} + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/agent_enrollment.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/agent_enrollment.tsx new file mode 100644 index 0000000000000..4536d13fdea7a --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/agent_enrollment.tsx @@ -0,0 +1,122 @@ +/* + * 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 { useCallback, useEffect, useState } from 'react'; + +import React from 'react'; + +import type { + FlyoutMode, + SelectionType, +} from '../../../../../../../components/agent_enrollment_flyout/types'; +import { useAgentEnrollmentFlyoutData, useFleetServerHostsForPolicy } from '../../../../../hooks'; +import { + useAgentPolicyWithPackagePolicies, + useCloudSecurityIntegration, + useIsK8sPolicy, +} from '../../../../../../../components/agent_enrollment_flyout/hooks'; +import { usePollingAgentCount } from '../../../../../../../components/agent_enrollment_flyout/confirm_agent_enrollment'; +import type { PackagePolicy } from '../../../../../types'; +import { FLEET_SERVER_PACKAGE } from '../../../../../constants'; +import { Instructions, Loading } from '../../../../../components'; +import type { EmbeddedIntegrationStepsLayoutProps } from '../types'; + +export const AgentEnrollmentFromOnboardingHub = ({ + agentPolicy, + selectedAgentPolicies, + isManaged, + from, + onNext, + setEnrolledAgentIds, + steps, +}: EmbeddedIntegrationStepsLayoutProps) => { + const [selectedPolicyId, setSelectedPolicyId] = useState(agentPolicy?.id); + const [isFleetServerPolicySelected, setIsFleetServerPolicySelected] = useState(false); + const [selectedApiKeyId, setSelectedAPIKeyId] = useState(); + const [mode, setMode] = useState(isManaged ? 'managed' : 'standalone'); + const [selectionType, setSelectionType] = useState(); + + const { + 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 { fleetServerHost, fleetProxy, downloadSource, downloadSourceProxy } = + useFleetServerHostsForPolicy(agentPolicyWithPackagePolicies); + + const selectedPolicy = agentPolicyWithPackagePolicies + ? agentPolicyWithPackagePolicies + : undefined; + + const { enrolledAgentIds } = usePollingAgentCount(selectedPolicyId || '', { + noLowerTimeLimit: true, + pollImmediately: true, + }); + + const onClickViewIncomingData = useCallback(() => { + setEnrolledAgentIds(enrolledAgentIds); + onNext({ toStep: steps.length - 1 }); + }, [enrolledAgentIds, onNext, setEnrolledAgentIds, steps.length]); + + const handleAddFleetServer = useCallback(() => { + setEnrolledAgentIds(enrolledAgentIds); + onNext(); + }, [enrolledAgentIds, onNext, setEnrolledAgentIds]); + + useEffect(() => { + if (selectedPolicy) { + if ( + (selectedPolicy.package_policies as PackagePolicy[]).some( + (packagePolicy) => packagePolicy.package?.name === FLEET_SERVER_PACKAGE + ) + ) { + setIsFleetServerPolicySelected(true); + } else { + setIsFleetServerPolicySelected(false); + } + } + }, [selectedPolicy, isFleetServerPolicySelected]); + + const { isK8s } = useIsK8sPolicy(selectedPolicy ?? undefined); + const { cloudSecurityIntegration } = useCloudSecurityIntegration(selectedPolicy ?? undefined); + + return isLoadingInitialAgentPolicies || isLoadingAgentPolicies ? ( + + ) : ( + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/check_fleet_server_required.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/check_fleet_server_required.tsx new file mode 100644 index 0000000000000..427608f36049f --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/check_fleet_server_required.tsx @@ -0,0 +1,82 @@ +/* + * 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, { useEffect, useState } from 'react'; + +import { + useFleetServerStandalone, + useFleetStatus, + useGetEnrollmentSettings, +} from '../../../../../hooks'; + +import { shouldShowFleetServerEnrollment } from '../../../../../components'; +import type { EmbeddedIntegrationStepsLayoutProps } from '../types'; +import { useAgentPolicyWithPackagePolicies } from '../../../../../../../components/agent_enrollment_flyout/hooks'; +import { FleetServerRequirementPage } from '../../../../agents/agent_requirements_page'; +import type { PackagePolicy } from '../../../../../types'; +import { FLEET_SERVER_PACKAGE } from '../../../../../constants'; + +export const CheckFleetServerRequiredFromOnboardingHub: React.FC< + EmbeddedIntegrationStepsLayoutProps +> = ({ agentPolicy, isManaged, setIsManaged, onNext, currentStep }) => { + const [isFleetServerPolicySelected, setIsFleetServerPolicySelected] = useState(false); + + const fleetStatus = useFleetStatus(); + const { isFleetServerStandalone } = useFleetServerStandalone(); + const { data: enrollmentSettings } = useGetEnrollmentSettings(); + + const showFleetServerEnrollment = shouldShowFleetServerEnrollment({ + isFleetServerStandalone, + isFleetServerPolicySelected, + enrollmentSettings, + fleetStatusMissingRequirements: fleetStatus.missingRequirements, + }); + + const { agentPolicyWithPackagePolicies } = useAgentPolicyWithPackagePolicies(agentPolicy?.id); + + const selectedPolicy = agentPolicyWithPackagePolicies + ? agentPolicyWithPackagePolicies + : undefined; + + useEffect(() => { + if (selectedPolicy) { + if ( + (selectedPolicy.package_policies as PackagePolicy[]).some( + (packagePolicy) => packagePolicy.package?.name === FLEET_SERVER_PACKAGE + ) + ) { + setIsFleetServerPolicySelected(true); + } else { + setIsFleetServerPolicySelected(false); + } + } + }, [selectedPolicy, isFleetServerPolicySelected]); + + useEffect(() => { + // If the user is standalone, skip the Fleet Server enrollment step + // If the user is managed and the Fleet Server enrollment is done, skip the Fleet Server enrollment step + if ((!isManaged && showFleetServerEnrollment) || (isManaged && !showFleetServerEnrollment)) { + onNext({ toStep: currentStep + 2 }); + } + }, [currentStep, isManaged, onNext, showFleetServerEnrollment]); + + if (isManaged && showFleetServerEnrollment) { + return ( + { + setIsManaged(false); + onNext({ toStep: currentStep + 2 }); + }} + handleAddFleetServer={() => { + onNext(); + }} + /> + ); + } + + return null; +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/confirm_incoming_data.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/confirm_incoming_data.tsx new file mode 100644 index 0000000000000..6f630eb51d65a --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/confirm_incoming_data.tsx @@ -0,0 +1,150 @@ +/* + * 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, { useCallback, useState } from 'react'; + +import { EuiButton, EuiButtonEmpty, EuiFlexGroup, EuiFlexItem } from '@elastic/eui'; + +import { FormattedMessage } from '@kbn/i18n-react'; + +import { DASHBOARD_APP_ID, createDashboardEditUrl } from '@kbn/dashboard-plugin/public'; + +import { useIntraAppState, useStartServices } from '../../../../../hooks'; + +import { + ConfirmIncomingDataWithPreview, + ConfirmIncomingDataStandalone, +} from '../../multi_page_layout/components'; +import type { EmbeddedIntegrationStepsLayoutProps } from '../types'; +import { FLEET_KUBERNETES_PACKAGE } from '../../../../../../../../common'; +import type { AgentPolicyDetailsDeployAgentAction } from '../../../../../types'; + +export const FinalBottomBar: React.FC<{ + pkgkey: string; + handleAddAnotherIntegration: () => void; + handleViewAssets: () => void; + viewKubernetesMetricsDashboards: () => void; +}> = ({ + pkgkey, + handleAddAnotherIntegration, + handleViewAssets, + viewKubernetesMetricsDashboards, +}) => { + const isK8s = pkgkey.includes(FLEET_KUBERNETES_PACKAGE); + return ( + + + + + + + + + {!isK8s && ( + + + + + + )} + {isK8s && ( + + + + + + )} + + ); +}; + +export const ConfirmDataStepFromOnboardingHub: React.FC = ( + props +) => { + const { enrolledAgentIds, packageInfo, isManaged, onCancel, handleViewAssets } = props; + const core = useStartServices(); + + const [agentDataConfirmed, setAgentDataConfirmed] = useState(false); + const { + docLinks: { + links: { + fleet: { troubleshooting: troubleshootLink }, + }, + }, + application: { navigateToApp }, + } = core; + const routeState = useIntraAppState(); + + const handleAddAnotherIntegration = () => { + // Close the modal + onCancel(); + }; + + const viewKubernetesMetricsDashboards = useCallback(() => { + // Open the Kubernetes metrics dashboards in security solution + // href={getAbsolutePath( + // '/app/dashboards#/view/kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c' + // )} + if (routeState && routeState.onDoneNavigateTo) { + const [appId] = routeState.onDoneNavigateTo; + return () => { + navigateToApp(appId === 'securitySolutionUI' ? appId : DASHBOARD_APP_ID, { + path: createDashboardEditUrl(`kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c`), + }); + }; + } + }, [navigateToApp, routeState]); + + if (!isManaged) { + return ( + + + + ); + } + + return ( + + {!!agentDataConfirmed && ( + + )} + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/create_package_policy.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/create_package_policy.tsx new file mode 100644 index 0000000000000..a7b37f4e1a35d --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/create_package_policy.tsx @@ -0,0 +1,24 @@ +/* + * 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 type { EmbeddedIntegrationStepsLayoutProps } from '../types'; +import { CreatePackagePolicySinglePage } from '../../single_page_layout'; + +export const CreatePackagePolicyFromOnboardingHub: React.FC = ( + props +) => { + return ( + + ); +}; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/index.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/index.ts new file mode 100644 index 0000000000000..858898797cc93 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/onboarding_steps/index.ts @@ -0,0 +1,60 @@ +/* + * 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 { i18n } from '@kbn/i18n'; + +import { CreatePackagePolicyFromOnboardingHub } from './create_package_policy'; +import { AgentEnrollmentFromOnboardingHub } from './agent_enrollment'; +import { AddFleetServerStepFromOnboardingHub } from './add_fleet_server'; +import { ConfirmDataStepFromOnboardingHub } from './confirm_incoming_data'; +import { CheckFleetServerRequiredFromOnboardingHub } from './check_fleet_server_required'; + +const addIntegrationFromOnboardingHub = { + title: i18n.translate('xpack.fleet.createFirstPackagePolicy.addIntegrationStepTitle', { + defaultMessage: 'Add the integration', + }), + component: CreatePackagePolicyFromOnboardingHub, +}; + +const agentEnrollmentStepFromOnboardingHub = { + title: i18n.translate('xpack.fleet.createFirstPackagePolicy.agentEnrollmentStepTitle', { + defaultMessage: 'Add agent', + }), + component: AgentEnrollmentFromOnboardingHub, +}; + +const addFleetServerStepFromOnboardingHub = { + title: i18n.translate('xpack.fleet.createFirstPackagePolicy.addFleetServerStepTitle', { + defaultMessage: 'Add Fleet Server', + }), + component: AddFleetServerStepFromOnboardingHub, +}; + +const confirmDataStepFromOnboardingHub = { + title: i18n.translate('xpack.fleet.createFirstPackagePolicy.confirmDataStepTitle', { + defaultMessage: 'Confirm incoming data', + }), + component: ConfirmDataStepFromOnboardingHub, +}; + +const checkFleetServerRequirementsStep = { + title: i18n.translate( + 'xpack.fleet.createFirstPackagePolicy.checkFleetServerRequirementsStepTitle', + { + defaultMessage: 'Check Fleet Server requirements', + } + ), + component: CheckFleetServerRequiredFromOnboardingHub, +}; + +export const onboardingManagedSteps = [ + addIntegrationFromOnboardingHub, + checkFleetServerRequirementsStep, + addFleetServerStepFromOnboardingHub, + agentEnrollmentStepFromOnboardingHub, + confirmDataStepFromOnboardingHub, +]; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/types.ts b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/types.ts new file mode 100644 index 0000000000000..c812c1aa124b8 --- /dev/null +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/embedded_integration_flow/types.ts @@ -0,0 +1,56 @@ +/* + * 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 type { + FleetProxy, + DownloadSource, + AgentPolicy, + EnrollmentAPIKey, + PackageInfo, + RegistryPolicyTemplate, +} from '../../../../types'; +import type { EditPackagePolicyFrom } from '../types'; + +export interface EmbeddedIntegrationStepsLayoutProps { + fleetServerHost: string; + fleetProxy?: FleetProxy; + downloadSource?: DownloadSource; + agentPolicy?: AgentPolicy; + error?: Error; + enrollmentAPIKey?: EnrollmentAPIKey; + packageInfo: PackageInfo; + integrationInfo?: RegistryPolicyTemplate; + cancelClickHandler?: React.ReactEventHandler; + onBack: React.ReactEventHandler; + steps: EmbeddedFlowStep[]; + currentStep: number; + onNext: (params?: { selectedAgentPolicies?: AgentPolicy[]; toStep?: number }) => void; + setIsManaged: (isManaged: boolean) => void; + isManaged: boolean; + setEnrolledAgentIds: (agentIds: string[]) => void; + enrolledAgentIds: string[]; + onCancel: () => void; + prerelease: boolean; + handleViewAssets: () => void; + from: EditPackagePolicyFrom; + selectedAgentPolicies?: AgentPolicy[]; +} + +export interface EmbeddedIntegrationFlowProps { + from: EditPackagePolicyFrom; + queryParamsPolicyId?: string; + integrationName?: string; + prerelease: boolean; + onCancel: () => void; + onStepNext?: (step: number) => void; + handleViewAssets: () => void; +} + +export interface EmbeddedFlowStep { + title: string; + component: React.FC; +} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx index 3cf6b362e7376..12903adb008af 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/index.tsx @@ -8,27 +8,30 @@ import React, { useEffect } from 'react'; import { useMemo } from 'react'; import { useLocation, useRouteMatch } from 'react-router-dom'; +import { noop } from 'lodash/fp'; + import { useGetSettings } from '../../../hooks'; import type { AddToPolicyParams, EditPackagePolicyFrom } from './types'; import { CreatePackagePolicySinglePage } from './single_page_layout'; import { CreatePackagePolicyMultiPage } from './multi_page_layout'; +import { EmbeddedIntegrationFlow } from './embedded_integration_flow'; export const CreatePackagePolicyPage: React.FC<{ useMultiPageLayoutProp?: boolean; originFrom?: EditPackagePolicyFrom; - propPolicyId?: string; integrationName?: string; - setIntegrationStep?: (step: number) => void; - onCanceled?: () => void; + onStepNext?: (step: number) => void; + onCancel?: () => void; + handleViewAssets?: () => void; }> = ({ useMultiPageLayoutProp, originFrom, - propPolicyId, integrationName, - setIntegrationStep, - onCanceled, + onStepNext, + onCancel = noop, + handleViewAssets = noop, }) => { const { search } = useLocation(); const { params } = useRouteMatch(); @@ -69,13 +72,21 @@ export const CreatePackagePolicyPage: React.FC<{ const pageParams = { from, queryParamsPolicyId, - propPolicyId, - integrationName, prerelease, - setIntegrationStep, - onCanceled, }; + if (from === 'onboarding-hub') { + return ( + + ); + } + if (useMultiPageLayout) { return ; } diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/bottom_bar.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/bottom_bar.tsx index 90d25a35be875..9610e54bc0849 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/bottom_bar.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/bottom_bar.tsx @@ -123,60 +123,68 @@ export const AgentStandaloneBottomBar: React.FC<{ ); }; -export const CreatePackagePolicyFinalBottomBar: React.FC<{ +const FinalBottomBar: React.FC<{ pkgkey: string; }> = ({ pkgkey }) => { const isK8s = pkgkey.includes(FLEET_KUBERNETES_PACKAGE); const { getHref } = useLink(); const { getAbsolutePath } = useLink(); return ( - - + + - - - - - + + + - {!isK8s && ( - - - - - - )} - {isK8s && ( - - - - - - )} - + + {!isK8s && ( + + + + + + )} + {isK8s && ( + + + + + + )} + + ); +}; + +export const CreatePackagePolicyFinalBottomBar: React.FC<{ + pkgkey: string; +}> = ({ pkgkey }) => { + return ( + + ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx index d6d4c3abd63f5..485d1b5036597 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirm_incoming_data_with_preview.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import type { PropsWithChildren } from 'react'; import React from 'react'; import { EuiCallOut, @@ -124,12 +125,13 @@ const AgentDataPreview: React.FC<{ dataPreview: SearchHit[] }> = ({ dataPreview ); }; -export const ConfirmIncomingDataWithPreview: React.FunctionComponent = ({ +export const ConfirmIncomingDataWithPreview: React.FunctionComponent> = ({ agentIds, packageInfo, agentDataConfirmed, setAgentDataConfirmed, troubleshootLink, + children, }) => { const { incomingData, dataPreview, isLoading, hasReachedTimeout } = usePollingIncomingData({ agentIds, @@ -218,6 +220,7 @@ export const ConfirmIncomingDataWithPreview: React.FunctionComponent = ({ + {children} ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirming_incoming_data_standalone.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirming_incoming_data_standalone.tsx index 5ad7be6fce71a..e83d05bd82dbe 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirming_incoming_data_standalone.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/confirming_incoming_data_standalone.tsx @@ -5,6 +5,7 @@ * 2.0. */ +import type { PropsWithChildren } from 'react'; import React from 'react'; import { EuiCallOut, EuiText, EuiLink } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; @@ -13,8 +14,9 @@ interface Props { troubleshootLink: string; } -export const ConfirmIncomingDataStandalone: React.FunctionComponent = ({ +export const ConfirmIncomingDataStandalone: React.FunctionComponent> = ({ troubleshootLink, + children, }) => { return ( <> @@ -45,6 +47,7 @@ export const ConfirmIncomingDataStandalone: React.FunctionComponent = ({ /> + {children} ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/multi_page_steps_layout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/multi_page_steps_layout.tsx index d9539e318c142..44348b704d220 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/multi_page_steps_layout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/components/multi_page_steps_layout.tsx @@ -57,6 +57,7 @@ export const MultiPageStepsLayout: React.FunctionComponent = ({ packageInfoName, packageInfoVersion }) => ( + <> + + + +); + export const ConfirmDataPageStep: React.FC = (props) => { const { enrolledAgentIds, packageInfo, isManaged } = props; const core = useStartServices(); @@ -25,18 +35,11 @@ export const ConfirmDataPageStep: React.FC = (props) = const { docLinks } = core; const troubleshootLink = docLinks.links.fleet.troubleshooting; - const bottomBar = ( - <> - - - - ); - if (!isManaged) { return ( <> - {bottomBar} + ); } @@ -51,7 +54,9 @@ export const ConfirmDataPageStep: React.FC = (props) = troubleshootLink={troubleshootLink} /> - {!!agentDataConfirmed && bottomBar} + {!!agentDataConfirmed && ( + + )} ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/hooks/use_get_agent_policy_or_default.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/hooks/use_get_agent_policy_or_default.tsx index ead4496b0ba7a..d46594ef6bb3b 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/hooks/use_get_agent_policy_or_default.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/hooks/use_get_agent_policy_or_default.tsx @@ -5,9 +5,8 @@ * 2.0. */ -import { useEffect, useState, useMemo, useRef } from 'react'; +import { useEffect, useState, useMemo } from 'react'; import { i18n } from '@kbn/i18n'; -import { v4 as uuidv4 } from 'uuid'; import { generateNewAgentPolicyWithDefaults } from '../../../../../../../../common/services/generate_new_agent_policy'; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/index.tsx index 1d61e8e033358..ebfba7a24297a 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/multi_page_layout/index.tsx @@ -18,12 +18,6 @@ import { import type { AddToPolicyParams, CreatePackagePolicyParams } from '../types'; -import { useIntegrationsStateContext } from '../../../../../integrations/hooks'; - -import { CreatePackagePolicySinglePage } from '../single_page_layout'; - -import type { AgentPolicy } from '../../../../types'; - import { useGetAgentPolicyOrDefault } from './hooks'; import { @@ -48,13 +42,6 @@ const addIntegrationStep = { component: AddIntegrationPageStep, }; -const addIntegrationSingleLayoutStep = { - title: i18n.translate('xpack.fleet.createFirstPackagePolicy.addIntegrationStepTitle', { - defaultMessage: 'Add the integration', - }), - component: CreatePackagePolicySinglePage, -}; - const confirmDataStep = { title: i18n.translate('xpack.fleet.createFirstPackagePolicy.confirmDataStepTitle', { defaultMessage: 'Confirm incoming data', @@ -66,35 +53,25 @@ const fleetManagedSteps = [installAgentStep, addIntegrationStep, confirmDataStep const standaloneSteps = [addIntegrationStep, installAgentStep, confirmDataStep]; -const onboardingSteps = [addIntegrationSingleLayoutStep, installAgentStep, confirmDataStep]; - -export const CreatePackagePolicyMultiPage: CreatePackagePolicyParams = ({ +export const CreatePackagePolicyMultiPage: React.FC = ({ queryParamsPolicyId, prerelease, - from, - integrationName, - setIntegrationStep, - onCanceled, }) => { const { params } = useRouteMatch(); - // fixme - const { pkgkey: pkgkeyParam, policyId, integration: integrationParam } = params; - const { pkgkey: pkgKeyContext } = useIntegrationsStateContext(); - const pkgkey = pkgkeyParam || pkgKeyContext; + const { pkgkey, policyId, integration } = params; const { pkgName, pkgVersion } = splitPkgKey(pkgkey); - const [onSplash, setOnSplash] = useState(from !== 'onboarding-integration'); + const [onSplash, setOnSplash] = useState(true); const [currentStep, setCurrentStep] = useState(0); const [isManaged, setIsManaged] = useState(true); const { getHref } = useLink(); const [enrolledAgentIds, setEnrolledAgentIds] = useState([]); - const [selectedAgentPolicies, setSelectedAgentPolicies] = useState(); + const toggleIsManaged = (newIsManaged: boolean) => { setIsManaged(newIsManaged); setCurrentStep(0); }; - const integration = integrationName || integrationParam; - const agentPolicyId = selectedAgentPolicies?.[0]?.id || policyId || queryParamsPolicyId; + const agentPolicyId = policyId || queryParamsPolicyId; const { data: packageInfoData, error: packageInfoError, @@ -131,6 +108,8 @@ export const CreatePackagePolicyMultiPage: CreatePackagePolicyParams = ({ ...(agentPolicyId ? { agentPolicyId } : {}), }); + const steps = isManaged ? fleetManagedSteps : standaloneSteps; + if (onSplash || !packageInfo) { return ( { + const stepsNext = () => { if (currentStep === steps.length - 1) { return; } setCurrentStep(currentStep + 1); - setIntegrationStep(currentStep + 1); - if (props?.selectedAgentPolicies) { - setSelectedAgentPolicies(props?.selectedAgentPolicies); - } }; const stepsBack = () => { @@ -189,7 +157,6 @@ export const CreatePackagePolicyMultiPage: CreatePackagePolicyParams = ({ setIsManaged={toggleIsManaged} setEnrolledAgentIds={setEnrolledAgentIds} enrolledAgentIds={enrolledAgentIds} - onCanceled={onCanceled} /> ); }; diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/layout.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/layout.tsx index c5170d33eee68..d6138fc7dc7a2 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/layout.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/components/layout.tsx @@ -48,6 +48,7 @@ export const CreatePackagePolicySinglePageLayout: React.FunctionComponent<{ onClick: React.ReactEventHandler; }>; children: React.ReactNode; + withHeader?: boolean; }> = memo( ({ from, @@ -59,6 +60,7 @@ export const CreatePackagePolicySinglePageLayout: React.FunctionComponent<{ children, 'data-test-subj': dataTestSubj, tabs = [], + withHeader = true, }) => { const isAdd = useMemo(() => ['package', 'policy'].includes(from), [from]); const isEdit = useMemo(() => ['edit', 'package-edit'].includes(from), [from]); @@ -252,7 +254,8 @@ export const CreatePackagePolicySinglePageLayout: React.FunctionComponent<{ ) : undefined; const maxWidth = 800; - return ( + + return withHeader ? ( {children} + ) : ( + children ); } ); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx index 4c4c7b311cb06..181a7e51e1933 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/hooks/form.tsx @@ -133,6 +133,17 @@ const DEFAULT_PACKAGE_POLICY = { inputs: [], }; +export interface UseOnSubmitProps { + agentCount: number; + selectedPolicyTab: SelectedPolicyTab; + newAgentPolicy: NewAgentPolicy; + withSysMonitoring: boolean; + queryParamsPolicyId: string | undefined; + packageInfo?: PackageInfo; + integrationToEnable?: string; + hasFleetAddAgentsPrivileges: boolean; +} + export function useOnSubmit({ agentCount, selectedPolicyTab, @@ -142,16 +153,7 @@ export function useOnSubmit({ packageInfo, integrationToEnable, hasFleetAddAgentsPrivileges, -}: { - packageInfo?: PackageInfo; - newAgentPolicy: NewAgentPolicy; - withSysMonitoring: boolean; - selectedPolicyTab: SelectedPolicyTab; - agentCount: number; - queryParamsPolicyId: string | undefined; - integrationToEnable?: string; - hasFleetAddAgentsPrivileges: boolean; -}) { +}: UseOnSubmitProps) { const { notifications } = useStartServices(); const confirmForceInstall = useConfirmForceInstall(); const spaceSettings = useSpaceSettingsContext(); diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx index b206df51de00b..c05e8d988dce8 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agent_policy/create_package_policy_page/single_page_layout/index.tsx @@ -103,12 +103,13 @@ const CustomEuiBottomBar = styled(EuiBottomBar)` z-index: 50; `; -export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ +export const CreatePackagePolicySinglePage: React.FC = ({ from, queryParamsPolicyId, prerelease, - onNext, - onCanceled, + onAddAgent, + onCancel, + withHeader = true, }) => { const { agents: { enabled: isFleetEnabled }, @@ -116,7 +117,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ const hasFleetAddAgentsPrivileges = useAuthz().fleet.addAgents; const { params } = useRouteMatch(); const { pkgkey: pkgKeyContext } = useIntegrationsStateContext(); - const pkgkey = params.pkgkey || pkgKeyContext; + const pkgkey = params.pkgkey || pkgKeyContext || ''; const fleetStatus = useFleetStatus(); const { docLinks } = useStartServices(); @@ -195,22 +196,24 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ }); const handleNavigateAddAgent = useCallback(() => { - if (onNext) { - onNext({ selectedAgentPolicies: agentPolicies }); + if (onAddAgent) { + onAddAgent({ selectedAgentPolicies: agentPolicies }); } else { if (savedPackagePolicy) { navigateAddAgent(savedPackagePolicy); } } - }, [onNext, agentPolicies, savedPackagePolicy, navigateAddAgent]); + }, [onAddAgent, agentPolicies, savedPackagePolicy, navigateAddAgent]); const handleCancellation = useCallback(() => { - if (onCanceled) { - onCanceled(); + if (onCancel) { + onCancel(); } else { - navigateAddAgentHelp(savedPackagePolicy); + if (savedPackagePolicy) { + navigateAddAgentHelp(savedPackagePolicy); + } } - }, [onCanceled, savedPackagePolicy, navigateAddAgentHelp]); + }, [onCancel, savedPackagePolicy, navigateAddAgentHelp]); const setPolicyValidation = useCallback( (selectedTab: SelectedPolicyTab, updatedAgentPolicy: NewAgentPolicy) => { @@ -307,12 +310,22 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ () => ({ from, cancelUrl, - onCancel: cancelClickHandler, + onCancel: onCancel ?? cancelClickHandler, agentPolicies, packageInfo, integrationInfo, + withHeader, }), - [agentPolicies, cancelClickHandler, cancelUrl, from, integrationInfo, packageInfo] + [ + agentPolicies, + cancelClickHandler, + cancelUrl, + from, + integrationInfo, + onCancel, + packageInfo, + withHeader, + ] ); const stepSelectAgentPolicy = useMemo( @@ -531,7 +544,7 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ onConfirm={onSubmit} onCancel={() => { setFormState('VALID'); - onCanceled?.(); + onCancel?.(); }} /> )} @@ -649,11 +662,10 @@ export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({ - {/* eslint-disable-next-line @elastic/eui/href-or-on-click */} void; -}>; + onAddAgent: (param?: { selectedAgentPolicies: AgentPolicy[] }) => void; + onCancel: () => void; + withHeader?: boolean; +} diff --git a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx index 9ad6cc482fb1a..c74b6cf542175 100644 --- a/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx +++ b/x-pack/plugins/fleet/public/applications/fleet/sections/agents/agent_requirements_page/components/enrollment_recommendation.tsx @@ -21,7 +21,8 @@ import { useFlyoutContext, useStartServices } from '../../../../hooks'; export const EnrollmentRecommendation: React.FunctionComponent<{ showStandaloneTab: () => void; -}> = ({ showStandaloneTab }) => { + handleAddFleetServer?: () => void; +}> = ({ showStandaloneTab, handleAddFleetServer }) => { const flyoutContext = useFlyoutContext(); const { docLinks } = useStartServices(); @@ -80,7 +81,7 @@ export const EnrollmentRecommendation: React.FunctionComponent<{ - + void; } | { showEnrollmentRecommendation?: true; showStandaloneTab: () => void; + handleAddFleetServer?: () => void; } -> = ({ showStandaloneTab = () => {}, showEnrollmentRecommendation = true }) => { +> = ({ + showStandaloneTab = () => {}, + showEnrollmentRecommendation = true, + handleAddFleetServer, +}) => { const startService = useStartServices(); const deploymentUrl = startService.cloud?.deploymentUrl; const authz = useAuthz(); @@ -61,7 +67,10 @@ export const FleetServerRequirementPage: React.FunctionComponent< ) : permissionsError ? ( ) : showEnrollmentRecommendation ? ( - + ) : ( )} diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_fleet_integration_context.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_fleet_integration_context.tsx index d4005439a39e8..bbc0f71b123a1 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_fleet_integration_context.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_fleet_integration_context.tsx @@ -8,6 +8,7 @@ import React, { createContext } from 'react'; import { FleetStatusProvider, + FlyoutContextProvider, KibanaVersionContext, UIExtensionsContextProvider, } from '../../../hooks'; @@ -38,7 +39,9 @@ export const FleetIntegrationsStateContextProvider: React.FC<{ - {children} + + {children} + diff --git a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_integrations_state.tsx b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_integrations_state.tsx index 47243f2b30e30..3074b0f10ec05 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/hooks/use_integrations_state.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/hooks/use_integrations_state.tsx @@ -13,6 +13,8 @@ import { useIntraAppState } from '../../../hooks'; interface IntegrationsStateContextValue { getFromIntegrations(): string | undefined; + pkgkey?: string; + panel?: string; } const IntegrationsStateContext = createContext({ @@ -24,7 +26,6 @@ export const IntegrationsStateContextProvider: FunctionComponent<{ }> = ({ children }) => { const maybeState = useIntraAppState(); const stateRef = useRef(maybeState); - console.log('myState---', maybeState); const getFromIntegrations = useCallback(() => { return stateRef.current?.fromIntegrations; }, []); @@ -33,7 +34,6 @@ export const IntegrationsStateContextProvider: FunctionComponent<{ value={{ getFromIntegrations, pkgkey: maybeState?.pkgkey, - panel: maybeState?.panel, }} > {children} diff --git a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx index 3652a4cec81db..1279b287202c0 100644 --- a/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx +++ b/x-pack/plugins/fleet/public/applications/integrations/sections/epm/screens/detail/index.tsx @@ -134,21 +134,21 @@ export function Detail({ originFrom, routesEnabled = true, onAddIntegrationPolicyClick, + onDetailsTabClick, + selectedDetailsTab = 'overview', }: { originFrom?: string; routesEnabled?: boolean; onAddIntegrationPolicyClick?: () => void; + onDetailsTabClick?: (tabId: DetailViewPanelName) => void; + selectedDetailsTab?: DetailViewPanelName; }) { const { getId: getAgentPolicyId } = useAgentPolicyContext(); - const { - getFromIntegrations, - pkgkey: pkgKeyContext, - panel: panelContext, - } = useIntegrationsStateContext(); - const [selectedPanel, setSelectedPanel] = useState(panelContext); + const { getFromIntegrations, pkgkey: pkgKeyContext } = useIntegrationsStateContext(); + const { pkgkey: pkgkeyParam, panel: panelParam } = useParams(); const pkgkey = pkgkeyParam || pkgKeyContext; - const panel = panelParam || selectedPanel; + const panel = panelParam || selectedDetailsTab; const { getHref, getPath } = useLink(); const history = useHistory(); const { pathname, search, hash } = useLocation(); @@ -656,7 +656,7 @@ export function Detail({ isSelected: panel === 'overview', 'data-test-subj': `tab-overview`, href: - originFrom !== 'onboarding-integration' + originFrom !== 'onboarding-hub' ? getHref('integration_details_overview', { pkgkey: packageInfoKey, ...(integration ? { integration } : {}), @@ -664,7 +664,7 @@ export function Detail({ : undefined, onClick: (e: React.MouseEvent) => { e.preventDefault(); - setSelectedPanel('overview'); + onDetailsTabClick?.('overview'); }, }, ]; @@ -680,7 +680,7 @@ export function Detail({ ), isSelected: panel === 'policies', 'data-test-subj': `tab-policies`, - href: routesEnabled + href: onDetailsTabClick ? getHref('integration_details_policies', { pkgkey: packageInfoKey, ...(integration ? { integration } : {}), @@ -688,7 +688,7 @@ export function Detail({ : undefined, onClick: (e: React.MouseEvent) => { e.preventDefault(); - setSelectedPanel('policies'); + onDetailsTabClick?.('policies'); }, }); } @@ -710,7 +710,7 @@ export function Detail({ ), isSelected: panel === 'assets', 'data-test-subj': `tab-assets`, - href: routesEnabled + href: onDetailsTabClick ? getHref('integration_details_assets', { pkgkey: packageInfoKey, ...(integration ? { integration } : {}), @@ -718,7 +718,7 @@ export function Detail({ : undefined, onClick: (e: React.MouseEvent) => { e.preventDefault(); - setSelectedPanel('assets'); + onDetailsTabClick?.('assets'); }, }); } @@ -734,7 +734,7 @@ export function Detail({ ), isSelected: panel === 'settings', 'data-test-subj': `tab-settings`, - href: routesEnabled + href: onDetailsTabClick ? getHref('integration_details_settings', { pkgkey: packageInfoKey, ...(integration ? { integration } : {}), @@ -742,7 +742,7 @@ export function Detail({ : undefined, onClick: (e: React.MouseEvent) => { e.preventDefault(); - setSelectedPanel('settings'); + onDetailsTabClick?.('settings'); }, }); } @@ -758,7 +758,7 @@ export function Detail({ ), isSelected: panel === 'configs', 'data-test-subj': `tab-configs`, - href: routesEnabled + href: onDetailsTabClick ? getHref('integration_details_configs', { pkgkey: packageInfoKey, ...(integration ? { integration } : {}), @@ -766,7 +766,7 @@ export function Detail({ : undefined, onClick: (e: React.MouseEvent) => { e.preventDefault(); - setSelectedPanel('configs'); + onDetailsTabClick?.('configs'); }, }); } @@ -782,7 +782,7 @@ export function Detail({ ), isSelected: panel === 'custom', 'data-test-subj': `tab-custom`, - href: routesEnabled + href: onDetailsTabClick ? getHref('integration_details_custom', { pkgkey: packageInfoKey, ...(integration ? { integration } : {}), @@ -790,7 +790,7 @@ export function Detail({ : undefined, onClick: (e: React.MouseEvent) => { e.preventDefault(); - setSelectedPanel('custom'); + onDetailsTabClick?.('custom'); }, }); } @@ -806,7 +806,7 @@ export function Detail({ ), isSelected: panel === 'api-reference', 'data-test-subj': `tab-api-reference`, - href: routesEnabled + href: onDetailsTabClick ? getHref('integration_details_api_reference', { pkgkey: packageInfoKey, ...(integration ? { integration } : {}), @@ -814,7 +814,7 @@ export function Detail({ : undefined, onClick: (e: React.MouseEvent) => { e.preventDefault(); - setSelectedPanel('api-reference'); + onDetailsTabClick?.('api-reference'); }, }); } @@ -833,7 +833,7 @@ export function Detail({ showConfigTab, showCustomTab, showDocumentationTab, - routesEnabled, + onDetailsTabClick, numOfDeferredInstallations, ]); diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_agent_enrollment.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_agent_enrollment.tsx index f067351546bde..5a4a3146fbae1 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_agent_enrollment.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/confirm_agent_enrollment.tsx @@ -23,6 +23,7 @@ interface Props { policyId?: string; troubleshootLink: string; onClickViewAgents?: () => void; + onClickViewIncomingData?: () => void; agentCount: number; showLoading?: boolean; isLongEnrollment?: boolean; @@ -99,6 +100,7 @@ export const ConfirmAgentEnrollment: React.FunctionComponent = ({ policyId, troubleshootLink, onClickViewAgents, + onClickViewIncomingData, agentCount, showLoading = false, isLongEnrollment = false, @@ -198,6 +200,17 @@ export const ConfirmAgentEnrollment: React.FunctionComponent = ({ })} )} + {onClickViewIncomingData && ( + + {i18n.translate('xpack.fleet.agentEnrollment.confirmation.button', { + defaultMessage: 'View incoming data', + })} + + )} ); }; diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx index bcb92303d8510..c5cdc3a433de6 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/instructions.tsx @@ -9,6 +9,7 @@ import React, { useEffect } from 'react'; import { EuiText, EuiSpacer } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n-react'; +import type { FleetStatus } from '../../hooks'; import { useFleetStatus, useFleetServerStandalone, useGetEnrollmentSettings } from '../../hooks'; import { FleetServerRequirementPage } from '../../applications/fleet/sections/agents/agent_requirements_page'; import { FLEET_SERVER_PACKAGE } from '../../constants'; @@ -19,6 +20,22 @@ import type { InstructionProps } from './types'; import { ManagedSteps, StandaloneSteps } from './steps'; import { DefaultMissingRequirements } from './default_missing_requirements'; +export const shouldShowFleetServerEnrollment = ({ + isFleetServerStandalone, + isFleetServerPolicySelected, + enrollmentSettings, + fleetStatusMissingRequirements, +}: { + isFleetServerStandalone: boolean; + isFleetServerPolicySelected: boolean | undefined; + enrollmentSettings: any; + fleetStatusMissingRequirements: FleetStatus['missingRequirements']; +}) => + !isFleetServerStandalone && + !isFleetServerPolicySelected && + (!enrollmentSettings?.fleet_server.has_active || + (fleetStatusMissingRequirements ?? []).some((r) => r === FLEET_SERVER_PACKAGE)); + export const Instructions = (props: InstructionProps) => { const { isFleetServerPolicySelected, @@ -28,7 +45,8 @@ export const Instructions = (props: InstructionProps) => { setSelectionType, mode, setMode, - isIntegrationFlow, + from, + handleAddFleetServer, } = props; const fleetStatus = useFleetStatus(); const { isFleetServerStandalone } = useFleetServerStandalone(); @@ -42,11 +60,12 @@ export const Instructions = (props: InstructionProps) => { isFleetServerStandalone || (fleetStatus.isReady && enrollmentSettings?.fleet_server.has_active && fleetServerHost); - const showFleetServerEnrollment = - !isFleetServerStandalone && - !isFleetServerPolicySelected && - (!enrollmentSettings?.fleet_server.has_active || - (fleetStatus.missingRequirements ?? []).some((r) => r === FLEET_SERVER_PACKAGE)); + const showFleetServerEnrollment = shouldShowFleetServerEnrollment({ + isFleetServerStandalone, + isFleetServerPolicySelected, + enrollmentSettings, + fleetStatusMissingRequirements: fleetStatus.missingRequirements, + }); useEffect(() => { // If we detect a CloudFormation integration, we want to hide the selection type @@ -56,12 +75,12 @@ export const Instructions = (props: InstructionProps) => { props.cloudSecurityIntegration?.cloudShellUrl ) { setSelectionType(undefined); - } else if (!isIntegrationFlow && showAgentEnrollment) { + } else if (showAgentEnrollment) { setSelectionType('radio'); } else { setSelectionType('tabs'); } - }, [isIntegrationFlow, showAgentEnrollment, setSelectionType, props.cloudSecurityIntegration]); + }, [from, showAgentEnrollment, setSelectionType, props.cloudSecurityIntegration]); if (isLoadingEnrollmentSettings || isLoadingAgentPolicies) return ; @@ -71,7 +90,12 @@ export const Instructions = (props: InstructionProps) => { if (mode === 'managed') { if (showFleetServerEnrollment) { - return setMode('standalone')} />; + return ( + setMode('standalone')} + handleAddFleetServer={handleAddFleetServer} + /> + ); } else if (showAgentEnrollment) { return ( <> diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_enrollment_confirmation_step.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_enrollment_confirmation_step.tsx index 761b662917d46..374feae579c11 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_enrollment_confirmation_step.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/agent_enrollment_confirmation_step.tsx @@ -43,6 +43,7 @@ export const AgentEnrollmentConfirmationStep = ({ selectedPolicyId, troubleshootLink, onClickViewAgents, + onClickViewIncomingData, agentCount, showLoading, poll = true, @@ -51,6 +52,7 @@ export const AgentEnrollmentConfirmationStep = ({ selectedPolicyId?: string; troubleshootLink: string; onClickViewAgents?: () => void; + onClickViewIncomingData?: () => void; agentCount: number; poll?: boolean; showLoading?: boolean; @@ -72,6 +74,7 @@ export const AgentEnrollmentConfirmationStep = ({ policyId={selectedPolicyId} troubleshootLink={troubleshootLink} onClickViewAgents={onClickViewAgents} + onClickViewIncomingData={onClickViewIncomingData} agentCount={agentCount} showLoading={!isComplete || showLoading} isLongEnrollment={isLongEnrollment} diff --git a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx index 660aafa339b47..fe633efc16208 100644 --- a/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx +++ b/x-pack/plugins/fleet/public/components/agent_enrollment_flyout/steps/compute_steps.tsx @@ -157,9 +157,11 @@ export const ManagedSteps: React.FunctionComponent = ({ setMode, selectionType, onClickViewAgents, + onClickViewIncomingData, isK8s, cloudSecurityIntegration, installedPackagePolicy, + from, }) => { const core = useStartServices(); const { docLinks } = core; @@ -265,13 +267,14 @@ export const ManagedSteps: React.FunctionComponent = ({ AgentEnrollmentConfirmationStep({ selectedPolicyId: selectedPolicy?.id, onClickViewAgents, + onClickViewIncomingData, troubleshootLink: link, agentCount: enrolledAgentIds.length, isLongEnrollment: cloudSecurityIntegration !== undefined, }) ); } - if (selectedPolicy) { + if (selectedPolicy && from !== 'onboarding-hub') { steps.push( IncomingDataConfirmationStep({ agentIds: enrolledAgentIds, @@ -295,18 +298,20 @@ export const ManagedSteps: React.FunctionComponent = ({ selectionType, cloudSecurityIntegration, apiKeyData, + from, mode, setMode, enrollToken, + fleetServerHost, installManagedCommands, + gcpProjectId, isK8s, - fleetServerHost, onClickViewAgents, + onClickViewIncomingData, link, enrolledAgentIds, agentDataConfirmed, installedPackagePolicy, - gcpProjectId, ]); if (!agentVersion) { 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 0c2e51e294b14..6c872ffa655f1 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 @@ -5,6 +5,7 @@ * 2.0. */ +import type { EditPackagePolicyFrom } from '../../applications/fleet/sections/agent_policy/create_package_policy_page/types'; import type { AgentPolicy, DownloadSource, FleetProxy } from '../../types'; import type { InstalledIntegrationPolicy } from './use_get_agent_incoming_data'; @@ -67,13 +68,21 @@ export interface FlyOutProps extends BaseProps { selectedAgentPolicies?: AgentPolicy[]; } +export interface AgentPolicySelectionProps extends BaseProps { + onCancel?: () => void; + defaultMode?: FlyoutMode; + selectedAgentPolicies?: AgentPolicy[]; + onNext: () => void; + setEnrolledAgentIds: (ids: string[]) => void; +} export interface InstructionProps extends BaseProps { agentPolicies: AgentPolicy[]; selectedPolicy: AgentPolicy | undefined; setSelectedPolicyId: (policyId?: string) => void; refreshAgentPolicies: () => void; isLoadingAgentPolicies?: boolean; - onClickViewAgents: () => void; + onClickViewAgents?: () => void; + onClickViewIncomingData?: () => void; mode: FlyoutMode; setMode: (v: FlyoutMode) => void; selectionType: SelectionType; @@ -84,4 +93,7 @@ export interface InstructionProps extends BaseProps { fleetProxy?: FleetProxy; downloadSource?: DownloadSource; downloadSourceProxy?: FleetProxy; + hasIncomingDataStep?: boolean; + handleAddFleetServer?: () => void; + from?: EditPackagePolicyFrom; } diff --git a/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx b/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx index 019940dc92238..320a1be5fc4cd 100644 --- a/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx +++ b/x-pack/plugins/fleet/public/hooks/use_fleet_status.tsx @@ -25,7 +25,7 @@ export interface FleetStatusProviderProps { spaceId?: string; } -interface FleetStatus extends FleetStatusProviderProps { +export interface FleetStatus extends FleetStatusProviderProps { refetch: () => Promise; // This flag allows us to opt into displaying the Fleet Server enrollment instructions even if diff --git a/x-pack/plugins/fleet/public/index.ts b/x-pack/plugins/fleet/public/index.ts index eea00d893c9d1..18aaa72ac5f84 100644 --- a/x-pack/plugins/fleet/public/index.ts +++ b/x-pack/plugins/fleet/public/index.ts @@ -10,7 +10,6 @@ import type { PluginInitializerContext } from '@kbn/core/public'; import { lazy } from 'react'; import { FleetPlugin } from './plugin'; -import type { UIExtensionsStorage } from './types'; export type { GetPackagesResponse } from './types'; export { installationStatuses } from '../common/constants'; diff --git a/x-pack/plugins/fleet/public/types/intra_app_route_state.ts b/x-pack/plugins/fleet/public/types/intra_app_route_state.ts index e3ffef62ac4ec..640fb52b45c71 100644 --- a/x-pack/plugins/fleet/public/types/intra_app_route_state.ts +++ b/x-pack/plugins/fleet/public/types/intra_app_route_state.ts @@ -35,6 +35,9 @@ export interface CreatePackagePolicyRouteState { onSaveQueryParams?: { [key in OnSaveQueryParamKeys]?: OnSaveQueryParamOpts; }; + pkgkey?: string; + + panel?: string; } /** @@ -58,6 +61,10 @@ export interface IntegrationsAppBrowseRouteState { forAgentPolicyId: string; /** The integration tab the user navigated to details from */ fromIntegrations: 'installed' | 'updates_available' | undefined; + + pkgkey?: string; + + panel?: string; } /** diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integration_card_grid_tabs.tsx b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integration_card_grid_tabs.tsx index 5f9b291d92be9..fd20aa0c8129e 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integration_card_grid_tabs.tsx +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/integration_card_grid_tabs.tsx @@ -7,13 +7,11 @@ import React, { lazy, Suspense, useMemo, useCallback, useEffect, useRef, useState } from 'react'; import { - EuiButton, EuiButtonGroup, EuiFlexGroup, EuiFlexItem, EuiModal, EuiModalBody, - EuiModalFooter, EuiModalHeader, EuiPortal, EuiSkeletonText, @@ -24,6 +22,7 @@ import { noop } from 'lodash'; import { css } from '@emotion/react'; import { useKibana } from '@kbn/kibana-react-plugin/public'; +import type { DetailViewPanelName } from '@kbn/fleet-plugin/public/applications/integrations/sections/epm/screens/detail'; import { withLazyHook } from '../../../../../common/components/with_lazy_hook'; import { useStoredIntegrationSearchTerm, @@ -77,11 +76,13 @@ const FleetIntegrationsStateContextProvider = lazy(async () => ({ .then((pkg) => pkg.FleetIntegrationsStateContextProvider), })); -const integrationStepMap = { - 0: 'Add integration', - 1: 'Install Elastic Agent', - 2: 'Confirm incoming data', -} +const integrationStepMap = [ + 'Add integration', + 'Check fleet server requirement', + 'Add Fleet server', + 'Install Elastic Agent', + 'Confirm incoming data', +]; export const IntegrationsCardGridTabsComponent = React.memo( ({ installedIntegrationsCount, isAgentRequired, useAvailablePackages }) => { @@ -108,18 +109,19 @@ export const IntegrationsCardGridTabsComponent = React.memo(); const [modalView, setModalView] = useState<'overview' | 'configure-integration' | 'add-agent'>( 'overview' ); - const [integrationStep, setIntegrationStep] = useState(0); + const [integrationStep, onStepNext] = useState(0); const onAddIntegrationPolicyClick = useCallback(() => { setModalView('configure-integration'); }, []); const closeModal = useCallback(() => { setIsModalVisible(false); setModalView('overview'); - setIntegrationStep(0); + setSelectedDetailsTab('overview'); + onStepNext(0); }, []); const onCardClicked = useCallback((name: string) => { setIsModalVisible(true); @@ -138,6 +140,7 @@ export const IntegrationsCardGridTabsComponent = React.memo INTEGRATION_TABS_BY_ID[toggleIdSelected], [toggleIdSelected]); + const [selectedDetailsTab, setSelectedDetailsTab] = useState('overview'); const onSearchTermChanged = useCallback( (searchQuery: string) => { setSearchTerm(searchQuery); @@ -150,6 +153,15 @@ export const IntegrationsCardGridTabsComponent = React.memo { + setModalView('overview'); + setSelectedDetailsTab('assets'); + }, []); + + const onDetailsTabClick = useCallback((detailsTab: DetailViewPanelName) => { + setSelectedDetailsTab(detailsTab); + }, []); + useEffect(() => { setCategory(selectedTab.category ?? ''); setSelectedSubCategory(selectedTab.subCategory); @@ -261,30 +273,33 @@ export const IntegrationsCardGridTabsComponent = React.memo - {modalView === 'configure-integration' && ({`step indicator place holder. Integration step: ${integrationStepMap[integrationStep]}`})} + {modalView === 'configure-integration' && ( + {`step indicator place holder. Integration step: ${integrationStepMap[integrationStep]}`} + )} {modalView === 'overview' && ( )} {modalView === 'configure-integration' && ( )} diff --git a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.ts b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.ts index 0af94c763f7f1..09c9f92688b71 100644 --- a/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.ts +++ b/x-pack/plugins/security_solution/public/onboarding/components/onboarding_body/cards/integrations/use_integration_card_list.ts @@ -101,6 +101,7 @@ const addSecuritySpecificProps = ({ APP_UI_ID, { path: ONBOARDING_PATH, state: { pkgkey: card.pkgkey, onCancelUrl: onboardingLink } }, ], + onDoneNavigateTo: [APP_UI_ID, { path: ONBOARDING_PATH, state: { pkgkey: card.pkgkey } }], onCancelUrl: onboardingLink, onSaveNavigateTo: [APP_UI_ID, { path: ONBOARDING_PATH, state: { pkgkey: card.pkgkey } }], pkgkey: card.pkgkey,