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)
- [ ] ...
This commit is contained in:
Angela Chuang 2024-12-10 17:36:17 +00:00 committed by Karen Grigoryan
parent 637369f012
commit 4bed69c657
No known key found for this signature in database
40 changed files with 1073 additions and 216 deletions

View file

@ -22,11 +22,13 @@ import {
interface AdvancedTabProps {
onClose: () => void;
selectedPolicyId?: string;
handleContinueAddingAgentClick?: () => void;
}
export const AdvancedTab: React.FunctionComponent<AdvancedTabProps> = ({
selectedPolicyId,
onClose,
handleContinueAddingAgentClick,
}) => {
const {
isSelectFleetServerPolicyLoading,
@ -81,6 +83,7 @@ export const AdvancedTab: React.FunctionComponent<AdvancedTabProps> = ({
getConfirmFleetServerConnectionStep({
hasRecentlyEnrolledFleetServers,
disabled: !Boolean(serviceToken),
handleContinueAddingAgentClick,
}),
];

View file

@ -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: <QuickStartTab onClose={onClose} />,
content: (
<QuickStartTab
onClose={onClose}
handleContinueAddingAgentClick={handleContinueAddingAgentClick}
/>
),
'data-test-subj': 'fleetServerFlyoutTab-quickStart',
};
const advancedTab = {
id: 'advanced',
label: 'Advanced',
content: <AdvancedTab onClose={onClose} />,
content: (
<AdvancedTab
onClose={onClose}
handleContinueAddingAgentClick={handleContinueAddingAgentClick}
/>
),
'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 }>;

View file

@ -19,9 +19,13 @@ import { useLatestFleetServers } from './hooks/use_latest_fleet_servers';
interface Props {
onClose: () => void;
handleContinueAddingAgentClick?: () => void;
}
export const QuickStartTab: React.FunctionComponent<Props> = ({ onClose }) => {
export const QuickStartTab: React.FunctionComponent<Props> = ({
onClose,
handleContinueAddingAgentClick,
}) => {
const {
fleetServerHost,
setFleetServerHost,
@ -62,6 +66,7 @@ export const QuickStartTab: React.FunctionComponent<Props> = ({ onClose }) => {
getConfirmFleetServerConnectionStep({
hasRecentlyEnrolledFleetServers,
disabled: status !== 'success',
handleContinueAddingAgentClick,
}),
];

View file

@ -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 && (
<ConfirmFleetServerConnectionStepContent
hasRecentlyEnrolledFleetServers={hasRecentlyEnrolledFleetServers}
handleContinueAddingAgentClick={handleContinueAddingAgentClick}
/>
),
};
@ -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<{
<EuiButton
color="primary"
onClick={handleContinueClick}
onClick={handleContinueAddingAgentClick ?? handleContinueClick}
data-test-subj="fleetServerFlyoutContinueEnrollingButton"
>
<FormattedMessage

View file

@ -0,0 +1,37 @@
/*
* 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 { FormattedMessage } from '@kbn/i18n-react';
import { Error } from '../../../../components';
import type { EmbeddedIntegrationStepsLayoutProps } from './types';
export const EmbeddedIntegrationStepsLayout: React.FunctionComponent<
EmbeddedIntegrationStepsLayoutProps
> = (props) => {
const { steps, currentStep, error } = props;
if (error) {
return (
<Error
title={
<FormattedMessage
id="xpack.fleet.createPackagePolicy.errorLoadingPackageTitle"
defaultMessage="Error loading package information"
/>
}
error={error}
/>
);
}
const StepComponent = steps[currentStep].component;
return <StepComponent {...props} />;
};

View file

@ -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<EmbeddedIntegrationFlowProps> = ({
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<string[]>([]);
const [selectedAgentPolicies, setSelectedAgentPolicies] = useState<AgentPolicy[]>();
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 ? (
<EmbeddedIntegrationStepsLayout
fleetServerHost={fleetServerHost}
fleetProxy={fleetProxy}
downloadSource={downloadSource}
agentPolicy={selectedAgentPolicies?.[0] ?? agentPolicy}
enrollmentAPIKey={enrollmentAPIKey}
currentStep={currentStep}
steps={onboardingManagedSteps}
packageInfo={packageInfo}
integrationInfo={integrationInfo}
onNext={stepsNext}
onBack={stepsBack}
isManaged={isManaged}
setIsManaged={toggleIsManaged}
setEnrolledAgentIds={setEnrolledAgentIds}
enrolledAgentIds={enrolledAgentIds}
onCancel={onCancel}
prerelease={prerelease}
handleViewAssets={handleViewAssets}
from={from}
selectedAgentPolicies={selectedAgentPolicies}
/>
) : (
<Loading />
);
};

View file

@ -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<EmbeddedIntegrationStepsLayoutProps> = ({
onCancel,
onNext,
}) => {
const { tabs, currentTab, setCurrentTab, currentTabContent } = useFleetServerTabs(
onCancel,
onNext
);
const { permissionsError, isPermissionsLoading } = useCheckPermissions();
return (
<>
<Header tabs={tabs} currentTab={currentTab} onTabClick={(id) => setCurrentTab(id)} />
{!isPermissionsLoading && permissionsError === 'MISSING_FLEET_SERVER_SETUP_PRIVILEGES' ? (
<FleetServerMissingESPrivileges />
) : (
<>
<EuiSpacer size="xl" />
{currentTabContent}
</>
)}
</>
);
};

View file

@ -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<boolean>(false);
const [selectedApiKeyId, setSelectedAPIKeyId] = useState<string | undefined>();
const [mode, setMode] = useState<FlyoutMode>(isManaged ? 'managed' : 'standalone');
const [selectionType, setSelectionType] = useState<SelectionType>();
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 ? (
<Loading size="l" />
) : (
<Instructions
fleetServerHost={fleetServerHost}
fleetProxy={fleetProxy}
downloadSource={downloadSource}
downloadSourceProxy={downloadSourceProxy}
setSelectedPolicyId={setSelectedPolicyId}
agentPolicy={agentPolicy}
selectedPolicy={selectedPolicy}
agentPolicies={agentPolicies}
isFleetServerPolicySelected={isFleetServerPolicySelected}
isK8s={isK8s}
cloudSecurityIntegration={cloudSecurityIntegration}
refreshAgentPolicies={refreshAgentPolicies}
isLoadingAgentPolicies={isLoadingAgentPolicies}
mode={mode}
setMode={setMode}
selectionType={selectionType}
setSelectionType={setSelectionType}
selectedApiKeyId={selectedApiKeyId}
setSelectedAPIKeyId={setSelectedAPIKeyId}
onClickViewIncomingData={onClickViewIncomingData}
from={from}
handleAddFleetServer={handleAddFleetServer}
/>
);
};

View file

@ -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<boolean>(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 (
<FleetServerRequirementPage
showStandaloneTab={() => {
setIsManaged(false);
onNext({ toStep: currentStep + 2 });
}}
handleAddFleetServer={() => {
onNext();
}}
/>
);
}
return null;
};

View file

@ -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 (
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<EuiFlexItem grow={false}>
<EuiButtonEmpty color="text" size="s" onClick={handleAddAnotherIntegration}>
<FormattedMessage
id="xpack.fleet.createPackagePolicyBottomBar.addAnotherIntegration"
defaultMessage="Add another integration"
/>
</EuiButtonEmpty>
</EuiFlexItem>
</EuiFlexItem>
{!isK8s && (
<EuiFlexItem grow={false}>
<EuiButton color="success" fill size="m" onClick={handleViewAssets}>
<FormattedMessage
id="xpack.fleet.confirmIncomingData.viewDataAssetsButtonText'"
defaultMessage="View assets"
/>
</EuiButton>
</EuiFlexItem>
)}
{isK8s && (
<EuiFlexItem grow={false}>
<EuiButton
color="success"
fill
size="m"
// href={getAbsolutePath(
// '/app/dashboards#/view/kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c'
// )}
onClick={viewKubernetesMetricsDashboards}
>
<FormattedMessage
id="xpack.fleet.confirmIncomingData. '"
defaultMessage="View Kubernetes metrics dashboards"
/>
</EuiButton>
</EuiFlexItem>
)}
</EuiFlexGroup>
);
};
export const ConfirmDataStepFromOnboardingHub: React.FC<EmbeddedIntegrationStepsLayoutProps> = (
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<AgentPolicyDetailsDeployAgentAction>();
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 (
<ConfirmIncomingDataStandalone troubleshootLink={troubleshootLink}>
<FinalBottomBar
pkgkey={`${packageInfo.name}-${packageInfo.version}`}
handleAddAnotherIntegration={handleAddAnotherIntegration}
handleViewAssets={handleViewAssets}
viewKubernetesMetricsDashboards={viewKubernetesMetricsDashboards}
/>
</ConfirmIncomingDataStandalone>
);
}
return (
<ConfirmIncomingDataWithPreview
agentIds={enrolledAgentIds}
packageInfo={packageInfo}
agentDataConfirmed={agentDataConfirmed}
setAgentDataConfirmed={setAgentDataConfirmed}
troubleshootLink={troubleshootLink}
>
{!!agentDataConfirmed && (
<FinalBottomBar
pkgkey={`${packageInfo.name}-${packageInfo.version}`}
handleAddAnotherIntegration={handleAddAnotherIntegration}
handleViewAssets={handleViewAssets}
viewKubernetesMetricsDashboards={viewKubernetesMetricsDashboards}
/>
)}
</ConfirmIncomingDataWithPreview>
);
};

View file

@ -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<EmbeddedIntegrationStepsLayoutProps> = (
props
) => {
return (
<CreatePackagePolicySinglePage
{...props}
onAddAgent={props.onNext}
from="onboarding-hub"
withHeader={false}
/>
);
};

View file

@ -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,
];

View file

@ -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<EmbeddedIntegrationStepsLayoutProps>;
}

View file

@ -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<AddToPolicyParams>();
@ -69,13 +72,21 @@ export const CreatePackagePolicyPage: React.FC<{
const pageParams = {
from,
queryParamsPolicyId,
propPolicyId,
integrationName,
prerelease,
setIntegrationStep,
onCanceled,
};
if (from === 'onboarding-hub') {
return (
<EmbeddedIntegrationFlow
{...pageParams}
onCancel={onCancel}
handleViewAssets={handleViewAssets}
onStepNext={onStepNext}
integrationName={integrationName}
/>
);
}
if (useMultiPageLayout) {
return <CreatePackagePolicyMultiPage {...pageParams} />;
}

View file

@ -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 (
<CenteredRoundedBottomBar>
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexGroup justifyContent="spaceBetween" alignItems="center">
<EuiFlexItem grow={false}>
<EuiFlexItem grow={false}>
<EuiFlexItem grow={false}>
<EuiButtonEmpty color="text" size="s" href={getHref('integrations_all')}>
<FormattedMessage
id="xpack.fleet.createPackagePolicyBottomBar.addAnotherIntegration"
defaultMessage="Add another integration"
/>
</EuiButtonEmpty>
</EuiFlexItem>
<EuiButtonEmpty color="text" size="s" href={getHref('integrations_all')}>
<FormattedMessage
id="xpack.fleet.createPackagePolicyBottomBar.addAnotherIntegration"
defaultMessage="Add another integration"
/>
</EuiButtonEmpty>
</EuiFlexItem>
{!isK8s && (
<EuiFlexItem grow={false}>
<EuiButton
color="success"
fill
size="m"
href={getHref('integration_details_assets', {
pkgkey,
})}
>
<FormattedMessage
id="xpack.fleet.confirmIncomingData.viewDataAssetsButtonText'"
defaultMessage="View assets"
/>
</EuiButton>
</EuiFlexItem>
)}
{isK8s && (
<EuiFlexItem grow={false}>
<EuiButton
color="success"
fill
size="m"
href={getAbsolutePath(
'/app/dashboards#/view/kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c'
)}
>
<FormattedMessage
id="xpack.fleet.confirmIncomingData. '"
defaultMessage="View Kubernetes metrics dashboards"
/>
</EuiButton>
</EuiFlexItem>
)}
</EuiFlexGroup>
</EuiFlexItem>
{!isK8s && (
<EuiFlexItem grow={false}>
<EuiButton
color="success"
fill
size="m"
href={getHref('integration_details_assets', {
pkgkey,
})}
>
<FormattedMessage
id="xpack.fleet.confirmIncomingData.viewDataAssetsButtonText'"
defaultMessage="View assets"
/>
</EuiButton>
</EuiFlexItem>
)}
{isK8s && (
<EuiFlexItem grow={false}>
<EuiButton
color="success"
fill
size="m"
href={getAbsolutePath(
'/app/dashboards#/view/kubernetes-f4dc26db-1b53-4ea2-a78b-1bfab8ea267c'
)}
>
<FormattedMessage
id="xpack.fleet.confirmIncomingData. '"
defaultMessage="View Kubernetes metrics dashboards"
/>
</EuiButton>
</EuiFlexItem>
)}
</EuiFlexGroup>
);
};
export const CreatePackagePolicyFinalBottomBar: React.FC<{
pkgkey: string;
}> = ({ pkgkey }) => {
return (
<CenteredRoundedBottomBar>
<FinalBottomBar pkgkey={pkgkey} />
</CenteredRoundedBottomBar>
);
};

View file

@ -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<Props> = ({
export const ConfirmIncomingDataWithPreview: React.FunctionComponent<PropsWithChildren<Props>> = ({
agentIds,
packageInfo,
agentDataConfirmed,
setAgentDataConfirmed,
troubleshootLink,
children,
}) => {
const { incomingData, dataPreview, isLoading, hasReachedTimeout } = usePollingIncomingData({
agentIds,
@ -218,6 +220,7 @@ export const ConfirmIncomingDataWithPreview: React.FunctionComponent<Props> = ({
</EuiText>
<EuiSpacer size="m" />
<AgentDataPreview dataPreview={dataPreview} />
{children}
</>
);
};

View file

@ -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<Props> = ({
export const ConfirmIncomingDataStandalone: React.FunctionComponent<PropsWithChildren<Props>> = ({
troubleshootLink,
children,
}) => {
return (
<>
@ -45,6 +47,7 @@ export const ConfirmIncomingDataStandalone: React.FunctionComponent<Props> = ({
/>
</EuiCallOut>
</EuiText>
{children}
</>
);
};

View file

@ -57,6 +57,7 @@ export const MultiPageStepsLayout: React.FunctionComponent<MultiPageStepLayoutPr
);
const maxWidth = 866;
return (
<WithHeaderLayout
topContent={topContent}

View file

@ -17,6 +17,16 @@ import {
NotObscuredByBottomBar,
} from '..';
const BottomBar: React.FC<{
packageInfoName: string;
packageInfoVersion: string;
}> = ({ packageInfoName, packageInfoVersion }) => (
<>
<NotObscuredByBottomBar />
<CreatePackagePolicyFinalBottomBar pkgkey={`${packageInfoName}-${packageInfoVersion}`} />
</>
);
export const ConfirmDataPageStep: React.FC<MultiPageStepLayoutProps> = (props) => {
const { enrolledAgentIds, packageInfo, isManaged } = props;
const core = useStartServices();
@ -25,18 +35,11 @@ export const ConfirmDataPageStep: React.FC<MultiPageStepLayoutProps> = (props) =
const { docLinks } = core;
const troubleshootLink = docLinks.links.fleet.troubleshooting;
const bottomBar = (
<>
<NotObscuredByBottomBar />
<CreatePackagePolicyFinalBottomBar pkgkey={`${packageInfo.name}-${packageInfo.version}`} />
</>
);
if (!isManaged) {
return (
<>
<ConfirmIncomingDataStandalone troubleshootLink={troubleshootLink} />
{bottomBar}
<BottomBar packageInfoName={packageInfo.name} packageInfoVersion={packageInfo.version} />
</>
);
}
@ -51,7 +54,9 @@ export const ConfirmDataPageStep: React.FC<MultiPageStepLayoutProps> = (props) =
troubleshootLink={troubleshootLink}
/>
{!!agentDataConfirmed && bottomBar}
{!!agentDataConfirmed && (
<BottomBar packageInfoName={packageInfo.name} packageInfoVersion={packageInfo.version} />
)}
</>
);
};

View file

@ -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';

View file

@ -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<CreatePackagePolicyParams> = ({
queryParamsPolicyId,
prerelease,
from,
integrationName,
setIntegrationStep,
onCanceled,
}) => {
const { params } = useRouteMatch<AddToPolicyParams>();
// 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<string[]>([]);
const [selectedAgentPolicies, setSelectedAgentPolicies] = useState<AgentPolicy[]>();
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 (
<AddFirstIntegrationSplashScreen
@ -144,23 +123,12 @@ export const CreatePackagePolicyMultiPage: CreatePackagePolicyParams = ({
);
}
const steps =
from === 'onboarding-integration'
? onboardingSteps
: isManaged
? fleetManagedSteps
: standaloneSteps;
const stepsNext = (props?: { selectedAgentPolicies: AgentPolicy[] }) => {
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}
/>
);
};

View file

@ -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 ? (
<WithHeaderLayout
restrictHeaderWidth={maxWidth}
restrictWidth={maxWidth}
@ -264,6 +267,8 @@ export const CreatePackagePolicySinglePageLayout: React.FunctionComponent<{
>
{children}
</WithHeaderLayout>
) : (
children
);
}
);

View file

@ -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();

View file

@ -103,12 +103,13 @@ const CustomEuiBottomBar = styled(EuiBottomBar)`
z-index: 50;
`;
export const CreatePackagePolicySinglePage: CreatePackagePolicyParams = ({
export const CreatePackagePolicySinglePage: React.FC<CreatePackagePolicyParams> = ({
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<AddToPolicyParams>();
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 = ({
<EuiFlexItem grow={false}>
<EuiFlexGroup gutterSize="s" justifyContent="flexEnd">
<EuiFlexItem grow={false}>
{/* eslint-disable-next-line @elastic/eui/href-or-on-click */}
<EuiButtonEmpty
color="text"
href={cancelUrl}
onClick={cancelClickHandler}
{...(onCancel ? {} : { href: cancelUrl })}
onClick={onCancel ?? cancelClickHandler}
data-test-subj="createPackagePolicyCancelButton"
>
<FormattedMessage

View file

@ -5,7 +5,8 @@
* 2.0.
*/
import type React from 'react';
import type { AgentPolicy } from '../../../types';
export type EditPackagePolicyFrom =
| 'package'
| 'package-edit'
@ -14,7 +15,7 @@ export type EditPackagePolicyFrom =
| 'upgrade-from-fleet-policy-list'
| 'upgrade-from-integrations-policy-list'
| 'upgrade-from-extension'
| 'onboarding-integration';
| 'onboarding-hub';
export type PackagePolicyFormState =
| 'VALID'
@ -33,11 +34,11 @@ export interface AddToPolicyParams {
policyId?: string;
}
export type CreatePackagePolicyParams = React.FunctionComponent<{
export interface CreatePackagePolicyParams {
from: EditPackagePolicyFrom;
queryParamsPolicyId?: string;
propPolicyId?: string;
integrationName?: string;
prerelease: boolean;
onNext?: () => void;
}>;
onAddAgent: (param?: { selectedAgentPolicies: AgentPolicy[] }) => void;
onCancel: () => void;
withHeader?: boolean;
}

View file

@ -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<{
<EuiFlexGroup>
<EuiFlexItem grow={false}>
<EuiButton onClick={flyoutContext?.openFleetServerFlyout}>
<EuiButton onClick={handleAddFleetServer ?? flyoutContext?.openFleetServerFlyout}>
<FormattedMessage
id="xpack.fleet.enrollment.addFleetServerButton"
defaultMessage="Add Fleet Server"

View file

@ -36,12 +36,18 @@ export const FleetServerRequirementPage: React.FunctionComponent<
| {
showEnrollmentRecommendation?: false;
showStandaloneTab?: never;
handleAddFleetServer?: () => 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 ? (
<FleetServerMissingESPrivileges />
) : showEnrollmentRecommendation ? (
<EnrollmentRecommendation showStandaloneTab={showStandaloneTab} />
<EnrollmentRecommendation
showStandaloneTab={showStandaloneTab}
handleAddFleetServer={handleAddFleetServer}
/>
) : (
<AddFleetServerLanding />
)}

View file

@ -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<{
<UIExtensionsContextProvider values={{}}>
<FleetStatusProvider>
<PackageInstallProvider startServices={startServices}>
<IntegrationsStateContextProvider>{children}</IntegrationsStateContextProvider>
<FlyoutContextProvider>
<IntegrationsStateContextProvider>{children}</IntegrationsStateContextProvider>
</FlyoutContextProvider>
</PackageInstallProvider>
</FleetStatusProvider>
</UIExtensionsContextProvider>

View file

@ -13,6 +13,8 @@ import { useIntraAppState } from '../../../hooks';
interface IntegrationsStateContextValue {
getFromIntegrations(): string | undefined;
pkgkey?: string;
panel?: string;
}
const IntegrationsStateContext = createContext<IntegrationsStateContextValue>({
@ -24,7 +26,6 @@ export const IntegrationsStateContextProvider: FunctionComponent<{
}> = ({ children }) => {
const maybeState = useIntraAppState<undefined | IntegrationsAppBrowseRouteState>();
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}

View file

@ -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<DetailViewPanelName>(panelContext);
const { getFromIntegrations, pkgkey: pkgKeyContext } = useIntegrationsStateContext();
const { pkgkey: pkgkeyParam, panel: panelParam } = useParams<DetailParams>();
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<HTMLAnchorElement>) => {
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<HTMLAnchorElement>) => {
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<HTMLAnchorElement>) => {
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<HTMLAnchorElement>) => {
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<HTMLAnchorElement>) => {
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<HTMLAnchorElement>) => {
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<HTMLAnchorElement>) => {
e.preventDefault();
setSelectedPanel('api-reference');
onDetailsTabClick?.('api-reference');
},
});
}
@ -833,7 +833,7 @@ export function Detail({
showConfigTab,
showCustomTab,
showDocumentationTab,
routesEnabled,
onDetailsTabClick,
numOfDeferredInstallations,
]);

View file

@ -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<Props> = ({
policyId,
troubleshootLink,
onClickViewAgents,
onClickViewIncomingData,
agentCount,
showLoading = false,
isLongEnrollment = false,
@ -198,6 +200,17 @@ export const ConfirmAgentEnrollment: React.FunctionComponent<Props> = ({
})}
</EuiButton>
)}
{onClickViewIncomingData && (
<EuiButton
onClick={onClickViewIncomingData}
color="success"
data-test-subj="ViewIncomingDataButton"
>
{i18n.translate('xpack.fleet.agentEnrollment.confirmation.button', {
defaultMessage: 'View incoming data',
})}
</EuiButton>
)}
</EuiCallOut>
);
};

View file

@ -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 <Loading size="l" />;
@ -71,7 +90,12 @@ export const Instructions = (props: InstructionProps) => {
if (mode === 'managed') {
if (showFleetServerEnrollment) {
return <FleetServerRequirementPage showStandaloneTab={() => setMode('standalone')} />;
return (
<FleetServerRequirementPage
showStandaloneTab={() => setMode('standalone')}
handleAddFleetServer={handleAddFleetServer}
/>
);
} else if (showAgentEnrollment) {
return (
<>

View file

@ -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}

View file

@ -157,9 +157,11 @@ export const ManagedSteps: React.FunctionComponent<InstructionProps> = ({
setMode,
selectionType,
onClickViewAgents,
onClickViewIncomingData,
isK8s,
cloudSecurityIntegration,
installedPackagePolicy,
from,
}) => {
const core = useStartServices();
const { docLinks } = core;
@ -265,13 +267,14 @@ export const ManagedSteps: React.FunctionComponent<InstructionProps> = ({
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<InstructionProps> = ({
selectionType,
cloudSecurityIntegration,
apiKeyData,
from,
mode,
setMode,
enrollToken,
installManagedCommands,
isK8s,
fleetServerHost,
installManagedCommands,
gcpProjectId,
isK8s,
onClickViewAgents,
onClickViewIncomingData,
link,
enrolledAgentIds,
agentDataConfirmed,
installedPackagePolicy,
gcpProjectId,
]);
if (!agentVersion) {

View file

@ -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;
}

View file

@ -25,7 +25,7 @@ export interface FleetStatusProviderProps {
spaceId?: string;
}
interface FleetStatus extends FleetStatusProviderProps {
export interface FleetStatus extends FleetStatusProviderProps {
refetch: () => Promise<unknown>;
// This flag allows us to opt into displaying the Fleet Server enrollment instructions even if

View file

@ -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';

View file

@ -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;
}
/**

View file

@ -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<IntegrationsCardGridTabsProps>(
({ installedIntegrationsCount, isAgentRequired, useAvailablePackages }) => {
@ -108,18 +109,19 @@ export const IntegrationsCardGridTabsComponent = React.memo<IntegrationsCardGrid
);
const [isModalVisible, setIsModalVisible] = useState(false);
const [integrationName, setIntegrationName] = useState();
const [integrationName, setIntegrationName] = useState<string>();
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<IntegrationsCardGrid
});
const selectedTab = useMemo(() => INTEGRATION_TABS_BY_ID[toggleIdSelected], [toggleIdSelected]);
const [selectedDetailsTab, setSelectedDetailsTab] = useState<DetailViewPanelName>('overview');
const onSearchTermChanged = useCallback(
(searchQuery: string) => {
setSearchTerm(searchQuery);
@ -150,6 +153,15 @@ export const IntegrationsCardGridTabsComponent = React.memo<IntegrationsCardGrid
[selectedTab.showSearchTools, setSearchTerm, setSearchTermToStorage]
);
const handleViewAssets = useCallback(() => {
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<IntegrationsCardGrid
aria-labelledby={modalTitleId}
onClose={closeModal}
css={css`
width: 85%;
width: 1024px;
`}
maxWidth="90%"
>
{modalView === 'configure-integration' && (<EuiModalHeader>{`step indicator place holder. Integration step: ${integrationStepMap[integrationStep]}`}</EuiModalHeader>)}
{modalView === 'configure-integration' && (
<EuiModalHeader>{`step indicator place holder. Integration step: ${integrationStepMap[integrationStep]}`}</EuiModalHeader>
)}
<EuiModalBody>
<FleetIntegrationsStateContextProvider
values={{ startServices, useMultiPageLayoutProp: true }}
values={{ startServices, useMultiPageLayoutProp: true, kibanaVersion: '9.0.0' }}
>
{modalView === 'overview' && (
<Detail
onAddIntegrationPolicyClick={onAddIntegrationPolicyClick}
originFrom="onboarding-integration"
originFrom="onboarding-hub"
routesEnabled={false}
onDetailsTabClick={onDetailsTabClick}
selectedDetailsTab={selectedDetailsTab}
/>
)}
{modalView === 'configure-integration' && (
<CreatePackagePolicyPage
useMultiPageLayoutProp={true}
originFrom="onboarding-integration"
propPolicyId=""
originFrom="onboarding-hub"
integrationName={integrationName}
setIntegrationStep={setIntegrationStep}
onCanceled={closeModal}
onStepNext={onStepNext}
onCancel={closeModal}
handleViewAssets={handleViewAssets}
/>
)}
</FleetIntegrationsStateContextProvider>

View file

@ -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,