From 7c22204b5a3db6caa449e21b4f48321112ff5609 Mon Sep 17 00:00:00 2001 From: Jen Huang Date: Thu, 14 May 2020 15:28:16 -0700 Subject: [PATCH] [Ingest Manager] Consolidate routing and add breadcrumbs to all pages (#66475) * Initial pass at normalizing links and redirects * Remove unused nav components * Normalize & centralize routing paths * Add breadcrumbs to all pages * Remove unused var * Change /epm to /integrations (#66030) * Fixes from review: kuery param, getPath cleanup, use chrome.docTitle, and teardown breadcrumb and doc title Co-authored-by: Elastic Machine --- .../ingest_manager/constants/index.ts | 13 +- .../ingest_manager/constants/page_paths.ts | 85 +++++++ .../ingest_manager/hooks/index.ts | 2 + .../ingest_manager/hooks/use_breadcrumbs.tsx | 222 +++++++++++++++++- .../ingest_manager/hooks/use_kibana_link.ts | 4 +- .../ingest_manager/hooks/use_link.ts | 24 +- .../applications/ingest_manager/index.tsx | 29 ++- .../ingest_manager/layouts/default.tsx | 16 +- .../components/linked_agent_count.tsx | 10 +- .../create_datasource_page/index.tsx | 47 +++- .../datasources/datasources_table.tsx | 13 +- .../components/datasources/no_datasources.tsx | 11 +- .../components/settings/index.tsx | 5 +- .../agent_config/details_page/constants.ts | 10 - .../details_page/hooks/use_details_uri.ts | 54 ----- .../agent_config/details_page/index.tsx | 96 ++++---- .../edit_datasource_page/index.tsx | 17 +- .../sections/agent_config/index.tsx | 42 ++-- .../sections/agent_config/list_page/index.tsx | 36 +-- .../sections/data_stream/index.tsx | 3 +- .../sections/data_stream/list_page/index.tsx | 4 +- .../sections/epm/components/package_card.tsx | 7 +- .../sections/epm/hooks/use_links.tsx | 25 -- .../epm/hooks/use_package_install.tsx | 16 +- .../ingest_manager/sections/epm/index.tsx | 10 +- .../epm/screens/detail/data_sources_panel.tsx | 15 +- .../sections/epm/screens/detail/header.tsx | 9 +- .../sections/epm/screens/detail/index.tsx | 8 +- .../epm/screens/detail/side_nav_links.tsx | 7 +- .../sections/epm/screens/home/index.tsx | 21 +- .../components/agent_details.tsx | 5 +- .../fleet/agent_details_page/index.tsx | 86 ++++--- .../sections/fleet/agent_list_page/index.tsx | 28 ++- .../agent_enrollment_flyout/index.tsx | 14 +- .../sections/fleet/components/index.tsx | 2 - .../sections/fleet/components/list_layout.tsx | 11 +- .../components/navigation/child_routes.tsx | 38 --- .../components/navigation/connected_link.tsx | 40 ---- .../enrollment_token_list_page/index.tsx | 2 + .../ingest_manager/sections/fleet/index.tsx | 17 +- .../overview/components/agent_section.tsx | 4 +- .../components/configuration_section.tsx | 4 +- .../components/datastream_section.tsx | 4 +- .../components/integration_section.tsx | 4 +- .../sections/overview/index.tsx | 4 +- .../plugins/ingest_manager/public/plugin.ts | 9 +- 46 files changed, 673 insertions(+), 460 deletions(-) create mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/page_paths.ts delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/constants.ts delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/hooks/use_details_uri.ts delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/navigation/child_routes.tsx delete mode 100644 x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/navigation/connected_link.tsx diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts index e9b736e379b58e..2936eea21805d7 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/index.ts @@ -14,17 +14,6 @@ export { DATASOURCE_SAVED_OBJECT_TYPE, } from '../../../../common'; -export const BASE_PATH = '/app/ingestManager'; -export const EPM_PATH = '/epm'; -export const EPM_LIST_ALL_PACKAGES_PATH = EPM_PATH; -export const EPM_LIST_INSTALLED_PACKAGES_PATH = `${EPM_PATH}/installed`; -export const EPM_DETAIL_VIEW_PATH = `${EPM_PATH}/detail/:pkgkey/:panel?`; -export const AGENT_CONFIG_PATH = '/configs'; -export const AGENT_CONFIG_DETAILS_PATH = `${AGENT_CONFIG_PATH}/`; -export const DATA_STREAM_PATH = '/data-streams'; -export const FLEET_PATH = '/fleet'; -export const FLEET_AGENTS_PATH = `${FLEET_PATH}/agents`; -export const FLEET_AGENT_DETAIL_PATH = `${FLEET_AGENTS_PATH}/`; -export const FLEET_ENROLLMENT_TOKENS_PATH = `/fleet/enrollment-tokens`; +export * from './page_paths'; export const INDEX_NAME = '.kibana'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/page_paths.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/page_paths.ts new file mode 100644 index 00000000000000..73771fa3cb343b --- /dev/null +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/constants/page_paths.ts @@ -0,0 +1,85 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export type StaticPage = + | 'overview' + | 'integrations' + | 'integrations_all' + | 'integrations_installed' + | 'configurations' + | 'configurations_list' + | 'fleet' + | 'fleet_enrollment_tokens' + | 'data_streams'; + +export type DynamicPage = + | 'integration_details' + | 'configuration_details' + | 'add_datasource_from_configuration' + | 'add_datasource_from_integration' + | 'edit_datasource' + | 'fleet_agent_list' + | 'fleet_agent_details'; + +export type Page = StaticPage | DynamicPage; + +export interface DynamicPagePathValues { + [key: string]: string; +} + +export const BASE_PATH = '/app/ingestManager'; + +// If routing paths are changed here, please also check to see if +// `pagePathGetters()`, below, needs any modifications +export const PAGE_ROUTING_PATHS = { + overview: '/', + integrations: '/integrations/:tabId?', + integrations_all: '/integrations', + integrations_installed: '/integrations/installed', + integration_details: '/integrations/detail/:pkgkey/:panel?', + configurations: '/configs', + configurations_list: '/configs', + configuration_details: '/configs/:configId/:tabId?', + configuration_details_yaml: '/configs/:configId/yaml', + configuration_details_settings: '/configs/:configId/settings', + add_datasource_from_configuration: '/configs/:configId/add-datasource', + add_datasource_from_integration: '/integrations/:pkgkey/add-datasource', + edit_datasource: '/configs/:configId/edit-datasource/:datasourceId', + fleet: '/fleet', + fleet_agent_list: '/fleet/agents', + fleet_agent_details: '/fleet/agents/:agentId/:tabId?', + fleet_agent_details_events: '/fleet/agents/:agentId', + fleet_agent_details_details: '/fleet/agents/:agentId/details', + fleet_enrollment_tokens: '/fleet/enrollment-tokens', + data_streams: '/data-streams', +}; + +export const pagePathGetters: { + [key in StaticPage]: () => string; +} & + { + [key in DynamicPage]: (values: DynamicPagePathValues) => string; + } = { + overview: () => '/', + integrations: () => '/integrations', + integrations_all: () => '/integrations', + integrations_installed: () => '/integrations/installed', + integration_details: ({ pkgkey, panel }) => + `/integrations/detail/${pkgkey}${panel ? `/${panel}` : ''}`, + configurations: () => '/configs', + configurations_list: () => '/configs', + configuration_details: ({ configId, tabId }) => `/configs/${configId}${tabId ? `/${tabId}` : ''}`, + add_datasource_from_configuration: ({ configId }) => `/configs/${configId}/add-datasource`, + add_datasource_from_integration: ({ pkgkey }) => `/integrations/${pkgkey}/add-datasource`, + edit_datasource: ({ configId, datasourceId }) => + `/configs/${configId}/edit-datasource/${datasourceId}`, + fleet: () => '/fleet', + fleet_agent_list: ({ kuery }) => `/fleet/agents${kuery ? `?kuery=${kuery}` : ''}`, + fleet_agent_details: ({ agentId, tabId }) => + `/fleet/agents/${agentId}${tabId ? `/${tabId}` : ''}`, + fleet_enrollment_tokens: () => '/fleet/enrollment-tokens', + data_streams: () => '/data-streams', +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts index 66c7333150fb7b..a752ad2a8912bb 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/index.ts @@ -8,6 +8,7 @@ export { useCapabilities } from './use_capabilities'; export { useCore, CoreContext } from './use_core'; export { useConfig, ConfigContext } from './use_config'; export { useSetupDeps, useStartDeps, DepsContext } from './use_deps'; +export { useBreadcrumbs } from './use_breadcrumbs'; export { useLink } from './use_link'; export { usePackageIconType, UsePackageIconType } from './use_package_icon_type'; export { usePagination, Pagination } from './use_pagination'; @@ -15,3 +16,4 @@ export { useDebounce } from './use_debounce'; export * from './use_request'; export * from './use_input'; export * from './use_url_params'; +export * from './use_fleet_status'; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_breadcrumbs.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_breadcrumbs.tsx index ff6656e969c93b..207c757fd5b165 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_breadcrumbs.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_breadcrumbs.tsx @@ -3,11 +3,225 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ - +import { i18n } from '@kbn/i18n'; import { ChromeBreadcrumb } from 'src/core/public'; +import { BASE_PATH, Page, DynamicPagePathValues, pagePathGetters } from '../constants'; import { useCore } from './use_core'; -export function useBreadcrumbs(newBreadcrumbs: ChromeBreadcrumb[]) { - const { chrome } = useCore(); - return chrome.setBreadcrumbs(newBreadcrumbs); +const BASE_BREADCRUMB: ChromeBreadcrumb = { + href: pagePathGetters.overview(), + text: i18n.translate('xpack.ingestManager.breadcrumbs.appTitle', { + defaultMessage: 'Ingest Manager', + }), +}; + +const breadcrumbGetters: { + [key in Page]: (values: DynamicPagePathValues) => ChromeBreadcrumb[]; +} = { + overview: () => [ + BASE_BREADCRUMB, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.overviewPageTitle', { + defaultMessage: 'Overview', + }), + }, + ], + integrations: () => [ + BASE_BREADCRUMB, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.integrationsPageTitle', { + defaultMessage: 'Integrations', + }), + }, + ], + integrations_all: () => [ + BASE_BREADCRUMB, + { + href: pagePathGetters.integrations(), + text: i18n.translate('xpack.ingestManager.breadcrumbs.integrationsPageTitle', { + defaultMessage: 'Integrations', + }), + }, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.allIntegrationsPageTitle', { + defaultMessage: 'All', + }), + }, + ], + integrations_installed: () => [ + BASE_BREADCRUMB, + { + href: pagePathGetters.integrations(), + text: i18n.translate('xpack.ingestManager.breadcrumbs.integrationsPageTitle', { + defaultMessage: 'Integrations', + }), + }, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.installedIntegrationsPageTitle', { + defaultMessage: 'Installed', + }), + }, + ], + integration_details: ({ pkgTitle }) => [ + BASE_BREADCRUMB, + { + href: pagePathGetters.integrations(), + text: i18n.translate('xpack.ingestManager.breadcrumbs.integrationsPageTitle', { + defaultMessage: 'Integrations', + }), + }, + { text: pkgTitle }, + ], + configurations: () => [ + BASE_BREADCRUMB, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.configurationsPageTitle', { + defaultMessage: 'Configurations', + }), + }, + ], + configurations_list: () => [ + BASE_BREADCRUMB, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.configurationsPageTitle', { + defaultMessage: 'Configurations', + }), + }, + ], + configuration_details: ({ configName }) => [ + BASE_BREADCRUMB, + { + href: pagePathGetters.configurations(), + text: i18n.translate('xpack.ingestManager.breadcrumbs.configurationsPageTitle', { + defaultMessage: 'Configurations', + }), + }, + { text: configName }, + ], + add_datasource_from_configuration: ({ configName, configId }) => [ + BASE_BREADCRUMB, + { + href: pagePathGetters.configurations(), + text: i18n.translate('xpack.ingestManager.breadcrumbs.configurationsPageTitle', { + defaultMessage: 'Configurations', + }), + }, + { + href: pagePathGetters.configuration_details({ configId }), + text: configName, + }, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.addDatasourcePageTitle', { + defaultMessage: 'Add data source', + }), + }, + ], + add_datasource_from_integration: ({ pkgTitle, pkgkey }) => [ + BASE_BREADCRUMB, + { + href: pagePathGetters.integrations(), + text: i18n.translate('xpack.ingestManager.breadcrumbs.integrationsPageTitle', { + defaultMessage: 'Integrations', + }), + }, + { + href: pagePathGetters.integration_details({ pkgkey }), + text: pkgTitle, + }, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.addDatasourcePageTitle', { + defaultMessage: 'Add data source', + }), + }, + ], + edit_datasource: ({ configName, configId }) => [ + BASE_BREADCRUMB, + { + href: pagePathGetters.configurations(), + text: i18n.translate('xpack.ingestManager.breadcrumbs.configurationsPageTitle', { + defaultMessage: 'Configurations', + }), + }, + { + href: pagePathGetters.configuration_details({ configId }), + text: configName, + }, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.editDatasourcePageTitle', { + defaultMessage: 'Edit data source', + }), + }, + ], + fleet: () => [ + BASE_BREADCRUMB, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetPageTitle', { + defaultMessage: 'Fleet', + }), + }, + ], + fleet_agent_list: () => [ + BASE_BREADCRUMB, + { + href: pagePathGetters.fleet(), + text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetPageTitle', { + defaultMessage: 'Fleet', + }), + }, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetAgentsPageTitle', { + defaultMessage: 'Agents', + }), + }, + ], + fleet_agent_details: ({ agentHost }) => [ + BASE_BREADCRUMB, + { + href: pagePathGetters.fleet(), + text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetPageTitle', { + defaultMessage: 'Fleet', + }), + }, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetAgentsPageTitle', { + defaultMessage: 'Agents', + }), + }, + { text: agentHost }, + ], + fleet_enrollment_tokens: () => [ + BASE_BREADCRUMB, + { + href: pagePathGetters.fleet(), + text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetPageTitle', { + defaultMessage: 'Fleet', + }), + }, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.fleetEnrollmentTokensPageTitle', { + defaultMessage: 'Enrollment tokens', + }), + }, + ], + data_streams: () => [ + BASE_BREADCRUMB, + { + text: i18n.translate('xpack.ingestManager.breadcrumbs.datastreamsPageTitle', { + defaultMessage: 'Data streams', + }), + }, + ], +}; + +export function useBreadcrumbs(page: Page, values: DynamicPagePathValues = {}) { + const { chrome, http } = useCore(); + const breadcrumbs: ChromeBreadcrumb[] = breadcrumbGetters[page](values).map(breadcrumb => ({ + ...breadcrumb, + href: breadcrumb.href ? http.basePath.prepend(`${BASE_PATH}#${breadcrumb.href}`) : undefined, + })); + const docTitle: string[] = [...breadcrumbs] + .reverse() + .map(breadcrumb => breadcrumb.text as string); + chrome.docTitle.change(docTitle); + chrome.setBreadcrumbs(breadcrumbs); } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_kibana_link.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_kibana_link.ts index f6c5b8bc03fce4..58537b2075c160 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_kibana_link.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_kibana_link.ts @@ -6,9 +6,9 @@ import { useCore } from './'; -const BASE_PATH = '/app/kibana'; +const KIBANA_BASE_PATH = '/app/kibana'; export function useKibanaLink(path: string = '/') { const core = useCore(); - return core.http.basePath.prepend(`${BASE_PATH}#${path}`); + return core.http.basePath.prepend(`${KIBANA_BASE_PATH}#${path}`); } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_link.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_link.ts index 333606cec8028f..1b17c5cb0b1f36 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_link.ts +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/hooks/use_link.ts @@ -4,10 +4,26 @@ * you may not use this file except in compliance with the Elastic License. */ -import { BASE_PATH } from '../constants'; +import { + BASE_PATH, + StaticPage, + DynamicPage, + DynamicPagePathValues, + pagePathGetters, +} from '../constants'; import { useCore } from './'; -export function useLink(path: string = '/') { +const getPath = (page: StaticPage | DynamicPage, values: DynamicPagePathValues = {}): string => { + return values ? pagePathGetters[page](values) : pagePathGetters[page as StaticPage](); +}; + +export const useLink = () => { const core = useCore(); - return core.http.basePath.prepend(`${BASE_PATH}#${path}`); -} + return { + getPath, + getHref: (page: StaticPage | DynamicPage, values?: DynamicPagePathValues) => { + const path = getPath(page, values); + return core.http.basePath.prepend(`${BASE_PATH}#${path}`); + }, + }; +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx index 3612497e723cdd..f6a386314272fa 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/index.tsx @@ -18,7 +18,7 @@ import { IngestManagerConfigType, IngestManagerStartDeps, } from '../../plugin'; -import { EPM_PATH, FLEET_PATH, AGENT_CONFIG_PATH, DATA_STREAM_PATH } from './constants'; +import { PAGE_ROUTING_PATHS } from './constants'; import { DefaultLayout, WithoutHeaderLayout } from './layouts'; import { Loading, Error } from './components'; import { IngestManagerOverview, EPMApp, AgentConfigApp, FleetApp, DataStreamApp } from './sections'; @@ -174,42 +174,42 @@ const IngestManagerRoutes = ({ ...rest }) => { } return ( - + - - + + - + - + - + - + - + - - + + - + ); }; @@ -265,3 +265,8 @@ export function renderApp( ReactDOM.unmountComponentAtNode(element); }; } + +export const teardownIngestManager = (coreStart: CoreStart) => { + coreStart.chrome.docTitle.reset(); + coreStart.chrome.setBreadcrumbs([]); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx index e9d7fcb1cf5c58..fbe7c736e2df45 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/layouts/default.tsx @@ -10,7 +10,6 @@ import { FormattedMessage } from '@kbn/i18n/react'; import { Section } from '../sections'; import { AlphaMessaging, SettingFlyout } from '../components'; import { useLink, useConfig } from '../hooks'; -import { EPM_PATH, FLEET_PATH, AGENT_CONFIG_PATH, DATA_STREAM_PATH } from '../constants'; interface Props { showSettings?: boolean; @@ -39,8 +38,8 @@ export const DefaultLayout: React.FunctionComponent = ({ section, children, }) => { + const { getHref } = useLink(); const { epm, fleet } = useConfig(); - const [isSettingsFlyoutOpen, setIsSettingsFlyoutOpen] = React.useState(false); return ( @@ -60,7 +59,7 @@ export const DefaultLayout: React.FunctionComponent = ({ - + = ({ = ({ defaultMessage="Integrations" /> - + = ({ = ({ defaultMessage="Fleet" /> - + ( ({ count, agentConfigId }) => { - const FLEET_URI = useLink(FLEET_AGENTS_PATH); + const { getHref } = useLink(); const displayValue = ( ( /> ); return count > 0 ? ( - + {displayValue} ) : ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx index 46233fdb59509d..fd5c4238f3466f 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/create_datasource_page/index.tsx @@ -17,16 +17,15 @@ import { EuiSpacer, } from '@elastic/eui'; import { EuiStepProps } from '@elastic/eui/src/components/steps/step'; -import { AGENT_CONFIG_DETAILS_PATH } from '../../../constants'; import { AgentConfig, PackageInfo, NewDatasource } from '../../../types'; import { useLink, + useBreadcrumbs, sendCreateDatasource, useCore, useConfig, sendGetAgentStatus, } from '../../../hooks'; -import { useLinks as useEPMLinks } from '../../epm/hooks'; import { ConfirmDeployConfigModal } from '../components'; import { CreateDatasourcePageLayout } from './components'; import { CreateDatasourceFrom, DatasourceFormState } from './types'; @@ -48,6 +47,7 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { const { params: { configId, pkgkey }, } = useRouteMatch(); + const { getHref, getPath } = useLink(); const history = useHistory(); const from: CreateDatasourceFrom = configId ? 'config' : 'package'; const [isNavDrawerLocked, setIsNavDrawerLocked] = useState(false); @@ -156,15 +156,11 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { } }; - // Cancel url - const CONFIG_URL = useLink( - `${AGENT_CONFIG_DETAILS_PATH}${agentConfig ? agentConfig.id : configId}` - ); - const PACKAGE_URL = useEPMLinks().toDetailView({ - name: (pkgkey || '-').split('-')[0], - version: (pkgkey || '-').split('-')[1], - }); - const cancelUrl = from === 'config' ? CONFIG_URL : PACKAGE_URL; + // Cancel path + const cancelUrl = + from === 'config' + ? getHref('configuration_details', { configId: agentConfig?.id || configId }) + : getHref('integration_details', { pkgkey }); // Save datasource const [formState, setFormState] = useState('INVALID'); @@ -186,7 +182,7 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { } const { error } = await saveDatasource(); if (!error) { - history.push(`${AGENT_CONFIG_DETAILS_PATH}${agentConfig ? agentConfig.id : configId}`); + history.push(getPath('configuration_details', { configId: agentConfig?.id || configId })); notifications.toasts.addSuccess({ title: i18n.translate('xpack.ingestManager.createDatasource.addedNotificationTitle', { defaultMessage: `Successfully added '{datasourceName}'`, @@ -280,6 +276,7 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { ) : null, }, ]; + return ( {formState === 'CONFIRM' && agentConfig && ( @@ -290,6 +287,16 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { onCancel={() => setFormState('VALID')} /> )} + {from === 'package' + ? packageInfo && ( + + ) + : agentConfig && ( + + )} {/* TODO #64541 - Remove classes */} @@ -331,3 +338,19 @@ export const CreateDatasourcePage: React.FunctionComponent = () => { ); }; + +const ConfigurationBreadcrumb: React.FunctionComponent<{ + configName: string; + configId: string; +}> = ({ configName, configId }) => { + useBreadcrumbs('add_datasource_from_configuration', { configName, configId }); + return null; +}; + +const IntegrationBreadcrumb: React.FunctionComponent<{ + pkgTitle: string; + pkgkey: string; +}> = ({ pkgTitle, pkgkey }) => { + useBreadcrumbs('add_datasource_from_integration', { pkgTitle, pkgkey }); + return null; +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/datasources/datasources_table.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/datasources/datasources_table.tsx index a0418c5f256c48..f93c49ec8c5afd 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/datasources/datasources_table.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/components/datasources/datasources_table.tsx @@ -20,7 +20,6 @@ import { AgentConfig, Datasource } from '../../../../../types'; import { TableRowActions } from '../../../components/table_row_actions'; import { DangerEuiContextMenuItem } from '../../../components/danger_eui_context_menu_item'; import { useCapabilities, useLink } from '../../../../../hooks'; -import { useAgentConfigLink } from '../../hooks/use_details_uri'; import { DatasourceDeleteProvider } from '../../../components/datasource_delete_provider'; import { useConfigRefresh } from '../../hooks/use_config'; import { PackageIcon } from '../../../../../components/package_icon'; @@ -54,9 +53,8 @@ export const DatasourcesTable: React.FunctionComponent = ({ config, ...rest }) => { + const { getHref } = useLink(); const hasWriteCapabilities = useCapabilities().write; - const addDatasourceLink = useAgentConfigLink('add-datasource', { configId: config.id }); - const editDatasourceLink = useLink(`/configs/${config.id}/edit-datasource`); const refreshConfig = useConfigRefresh(); // With the datasources provided on input, generate the list of datasources @@ -216,7 +214,10 @@ export const DatasourcesTable: React.FunctionComponent = ({ = ({ ], }, ], - [config, editDatasourceLink, hasWriteCapabilities, refreshConfig] + [config, getHref, hasWriteCapabilities, refreshConfig] ); return ( @@ -276,7 +277,7 @@ export const DatasourcesTable: React.FunctionComponent = ({ (({ configId }) => { + const { getHref } = useLink(); const hasWriteCapabilities = useCapabilities().write; - const addDatasourceLink = useAgentConfigLink('add-datasource', { configId }); return ( (({ configId }) => { /> } actions={ - + ( fleet: { enabled: isFleetEnabled }, } = useConfig(); const history = useHistory(); + const { getPath } = useLink(); const hasWriteCapabilites = useCapabilities().write; const refreshConfig = useConfigRefresh(); const [isNavDrawerLocked, setIsNavDrawerLocked] = useState(false); @@ -147,7 +148,7 @@ export const ConfigSettingsView = memo<{ config: AgentConfig }>( validation={validation} isEditing={true} onDelete={() => { - history.push(AGENT_CONFIG_PATH); + history.push(getPath('configurations_list')); }} /> {/* TODO #64541 - Remove classes */} diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/constants.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/constants.ts deleted file mode 100644 index 787791f985c7da..00000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/constants.ts +++ /dev/null @@ -1,10 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { AGENT_CONFIG_DETAILS_PATH } from '../../../constants'; - -export const DETAILS_ROUTER_PATH = `${AGENT_CONFIG_DETAILS_PATH}:configId`; -export const DETAILS_ROUTER_SUB_PATH = `${DETAILS_ROUTER_PATH}/:tabId`; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/hooks/use_details_uri.ts b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/hooks/use_details_uri.ts deleted file mode 100644 index 9332ce3e0f909c..00000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/hooks/use_details_uri.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ - -import { generatePath } from 'react-router-dom'; -import { useLink } from '../../../../hooks'; -import { AGENT_CONFIG_PATH } from '../../../../constants'; -import { DETAILS_ROUTER_PATH, DETAILS_ROUTER_SUB_PATH } from '../constants'; - -type AgentConfigUriArgs = - | ['list'] - | ['details', { configId: string }] - | ['details-yaml', { configId: string }] - | ['details-settings', { configId: string }] - | ['datasource', { configId: string; datasourceId: string }] - | ['add-datasource', { configId: string }]; - -/** - * Returns a Uri that starts at the Agent Config Route path (`/configs/`). - * These are good for use when needing to use React Router's redirect or - * `history.push(routePath)`. - * @param args - */ -export const useAgentConfigUri = (...args: AgentConfigUriArgs) => { - switch (args[0]) { - case 'list': - return AGENT_CONFIG_PATH; - case 'details': - return generatePath(DETAILS_ROUTER_PATH, args[1]); - case 'details-yaml': - return `${generatePath(DETAILS_ROUTER_SUB_PATH, { ...args[1], tabId: 'yaml' })}`; - case 'details-settings': - return `${generatePath(DETAILS_ROUTER_SUB_PATH, { ...args[1], tabId: 'settings' })}`; - case 'add-datasource': - return `${generatePath(DETAILS_ROUTER_SUB_PATH, { ...args[1], tabId: 'add-datasource' })}`; - case 'datasource': - const [, options] = args; - return `${generatePath(DETAILS_ROUTER_PATH, options)}?datasourceId=${options.datasourceId}`; - } - return '/'; -}; - -/** - * Returns a full Link that includes Kibana basepath (ex. `/app/ingestManager#/configs`). - * These are good for use in `href` properties - * @param args - */ -export const useAgentConfigLink = (...args: AgentConfigUriArgs) => { - const BASE_URI = useLink(''); - const AGENT_CONFIG_ROUTE = useAgentConfigUri(...args); - return `${BASE_URI}${AGENT_CONFIG_ROUTE}`; -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/index.tsx index 82879c174b7d3e..f80b981b69d3bd 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/details_page/index.tsx @@ -3,7 +3,7 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import React, { memo, useMemo, useState } from 'react'; +import React, { useMemo, useState } from 'react'; import { Redirect, useRouteMatch, Switch, Route } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedDate } from '@kbn/i18n/react'; @@ -21,13 +21,13 @@ import { } from '@elastic/eui'; import { Props as EuiTabProps } from '@elastic/eui/src/components/tabs/tab'; import styled from 'styled-components'; -import { useGetOneAgentConfig } from '../../../hooks'; +import { AgentConfig } from '../../../types'; +import { PAGE_ROUTING_PATHS } from '../../../constants'; +import { useGetOneAgentConfig, useLink, useBreadcrumbs } from '../../../hooks'; import { Loading } from '../../../components'; import { WithHeaderLayout } from '../../../layouts'; import { ConfigRefreshContext, useGetAgentStatus, AgentStatusRefreshContext } from './hooks'; import { LinkedAgentCount } from '../components'; -import { useAgentConfigLink } from './hooks/use_details_uri'; -import { DETAILS_ROUTER_PATH, DETAILS_ROUTER_SUB_PATH } from './constants'; import { ConfigDatasourcesView } from './components/datasources'; import { ConfigYamlView } from './components/yaml'; import { ConfigSettingsView } from './components/settings'; @@ -38,23 +38,11 @@ const Divider = styled.div` border-left: ${props => props.theme.eui.euiBorderThin}; `; -export const AgentConfigDetailsPage = memo(() => { - return ( - - - - - - - - - ); -}); - -export const AgentConfigDetailsLayout: React.FunctionComponent = () => { +export const AgentConfigDetailsPage: React.FunctionComponent = () => { const { params: { configId, tabId = '' }, } = useRouteMatch<{ configId: string; tabId?: string }>(); + const { getHref } = useLink(); const agentConfigRequest = useGetOneAgentConfig(configId); const agentConfig = agentConfigRequest.data ? agentConfigRequest.data.item : null; const { isLoading, error, sendRequest: refreshAgentConfig } = agentConfigRequest; @@ -63,17 +51,16 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { const { refreshAgentStatus } = agentStatusRequest; const agentStatus = agentStatusRequest.data?.results; - // Links - const configListLink = useAgentConfigLink('list'); - const configDetailsLink = useAgentConfigLink('details', { configId }); - const configDetailsYamlLink = useAgentConfigLink('details-yaml', { configId }); - const configDetailsSettingsLink = useAgentConfigLink('details-settings', { configId }); - const headerLeftContent = useMemo( () => ( - + { ) : null} ), - [configListLink, agentConfig, configId] + [getHref, agentConfig, configId] ); const headerRightContent = useMemo( @@ -184,7 +171,7 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { name: i18n.translate('xpack.ingestManager.configDetails.subTabs.datasourcesTabText', { defaultMessage: 'Data sources', }), - href: configDetailsLink, + href: getHref('configuration_details', { configId, tabId: 'datasources' }), isSelected: tabId === '' || tabId === 'datasources', }, { @@ -192,7 +179,7 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { name: i18n.translate('xpack.ingestManager.configDetails.subTabs.yamlTabText', { defaultMessage: 'YAML', }), - href: configDetailsYamlLink, + href: getHref('configuration_details', { configId, tabId: 'yaml' }), isSelected: tabId === 'yaml', }, { @@ -200,11 +187,11 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { name: i18n.translate('xpack.ingestManager.configDetails.subTabs.settingsTabText', { defaultMessage: 'Settings', }), - href: configDetailsSettingsLink, + href: getHref('configuration_details', { configId, tabId: 'settings' }), isSelected: tabId === 'settings', }, ]; - }, [configDetailsLink, configDetailsSettingsLink, configDetailsYamlLink, tabId]); + }, [getHref, configId, tabId]); if (redirectToAgentConfigList) { return ; @@ -254,28 +241,37 @@ export const AgentConfigDetailsLayout: React.FunctionComponent = () => { rightColumn={headerRightContent} tabs={(headerTabs as unknown) as EuiTabProps[]} > - - { - return ; - }} - /> - { - return ; - }} - /> - { - return ; - }} - /> - + ); }; + +const AgentConfigDetailsContent: React.FunctionComponent<{ agentConfig: AgentConfig }> = ({ + agentConfig, +}) => { + useBreadcrumbs('configuration_details', { configName: agentConfig.name }); + return ( + + { + return ; + }} + /> + { + return ; + }} + /> + { + return ; + }} + /> + + ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_datasource_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_datasource_page/index.tsx index 089a5a91df88a2..92be20a2761e22 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_datasource_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/edit_datasource_page/index.tsx @@ -16,10 +16,10 @@ import { EuiFlexItem, EuiSpacer, } from '@elastic/eui'; -import { AGENT_CONFIG_DETAILS_PATH } from '../../../constants'; import { AgentConfig, PackageInfo, NewDatasource } from '../../../types'; import { useLink, + useBreadcrumbs, useCore, useConfig, sendUpdateDatasource, @@ -53,6 +53,7 @@ export const EditDatasourcePage: React.FunctionComponent = () => { params: { configId, datasourceId }, } = useRouteMatch(); const history = useHistory(); + const { getHref, getPath } = useLink(); const [isNavDrawerLocked, setIsNavDrawerLocked] = useState(false); useEffect(() => { @@ -185,8 +186,7 @@ export const EditDatasourcePage: React.FunctionComponent = () => { }; // Cancel url - const CONFIG_URL = useLink(`${AGENT_CONFIG_DETAILS_PATH}${configId}`); - const cancelUrl = CONFIG_URL; + const cancelUrl = getHref('configuration_details', { configId }); // Save datasource const [formState, setFormState] = useState('INVALID'); @@ -208,7 +208,7 @@ export const EditDatasourcePage: React.FunctionComponent = () => { } const { error } = await saveDatasource(); if (!error) { - history.push(`${AGENT_CONFIG_DETAILS_PATH}${configId}`); + history.push(getPath('configuration_details', { configId })); notifications.toasts.addSuccess({ title: i18n.translate('xpack.ingestManager.editDatasource.updatedNotificationTitle', { defaultMessage: `Successfully updated '{datasourceName}'`, @@ -262,6 +262,7 @@ export const EditDatasourcePage: React.FunctionComponent = () => { /> ) : ( <> + {formState === 'CONFIRM' && ( { ); }; + +const Breadcrumb: React.FunctionComponent<{ configName: string; configId: string }> = ({ + configName, + configId, +}) => { + useBreadcrumbs('edit_datasource', { configName, configId }); + return null; +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/index.tsx index ef88aa5d17f1e4..74fa67078f7412 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/index.tsx @@ -5,26 +5,32 @@ */ import React from 'react'; import { HashRouter as Router, Switch, Route } from 'react-router-dom'; +import { PAGE_ROUTING_PATHS } from '../../constants'; +import { useBreadcrumbs } from '../../hooks'; import { AgentConfigListPage } from './list_page'; import { AgentConfigDetailsPage } from './details_page'; import { CreateDatasourcePage } from './create_datasource_page'; import { EditDatasourcePage } from './edit_datasource_page'; -export const AgentConfigApp: React.FunctionComponent = () => ( - - - - - - - - - - - - - - - - -); +export const AgentConfigApp: React.FunctionComponent = () => { + useBreadcrumbs('configurations'); + + return ( + + + + + + + + + + + + + + + + + ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx index 9b565a0452c961..ff3124d5748574 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/agent_config/list_page/index.tsx @@ -22,11 +22,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedDate } from '@kbn/i18n/react'; import { useHistory } from 'react-router-dom'; import { AgentConfig } from '../../../types'; -import { - AGENT_CONFIG_DETAILS_PATH, - AGENT_CONFIG_SAVED_OBJECT_TYPE, - AGENT_CONFIG_PATH, -} from '../../../constants'; +import { AGENT_CONFIG_SAVED_OBJECT_TYPE } from '../../../constants'; import { WithHeaderLayout } from '../../../layouts'; import { useCapabilities, @@ -35,11 +31,11 @@ import { useLink, useConfig, useUrlParams, + useBreadcrumbs, } from '../../../hooks'; import { CreateAgentConfigFlyout } from './components'; import { SearchBar } from '../../../components/search_bar'; import { LinkedAgentCount } from '../components'; -import { useAgentConfigLink } from '../details_page/hooks/use_details_uri'; import { TableRowActions } from '../components/table_row_actions'; const NO_WRAP_TRUNCATE_STYLE: CSSProperties = Object.freeze({ @@ -81,14 +77,17 @@ const AgentConfigListPageLayout: React.FunctionComponent = ({ children }) => ( const ConfigRowActions = memo<{ config: AgentConfig; onDelete: () => void }>( ({ config, onDelete }) => { + const { getHref } = useLink(); const hasWriteCapabilities = useCapabilities().write; - const detailsLink = useAgentConfigLink('details', { configId: config.id }); - const addDatasourceLink = useAgentConfigLink('add-datasource', { configId: config.id }); return ( + void }>( void }>( ); export const AgentConfigListPage: React.FunctionComponent<{}> = () => { + useBreadcrumbs('configurations_list'); + const { getHref, getPath } = useLink(); // Config information const hasWriteCapabilites = useCapabilities().write; const { fleet: { enabled: isFleetEnabled }, } = useConfig(); - // Base URL paths - const DETAILS_URI = useLink(AGENT_CONFIG_DETAILS_PATH); - // Table and search states const { urlParams, toUrlParams } = useUrlParams(); const [search, setSearch] = useState( @@ -142,14 +140,16 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => { (isOpen: boolean) => { if (isOpen !== isCreateAgentConfigFlyoutOpen) { if (isOpen) { - history.push(`${AGENT_CONFIG_PATH}?${toUrlParams({ ...urlParams, create: null })}`); + history.push( + `${getPath('configurations_list')}?${toUrlParams({ ...urlParams, create: null })}` + ); } else { const { create, ...params } = urlParams; - history.push(`${AGENT_CONFIG_PATH}?${toUrlParams(params)}`); + history.push(`${getPath('configurations_list')}?${toUrlParams(params)}`); } } }, - [history, isCreateAgentConfigFlyoutOpen, toUrlParams, urlParams] + [getPath, history, isCreateAgentConfigFlyoutOpen, toUrlParams, urlParams] ); // Fetch agent configs @@ -174,7 +174,7 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => { @@ -253,7 +253,7 @@ export const AgentConfigListPage: React.FunctionComponent<{}> = () => { } return cols; - }, [DETAILS_URI, isFleetEnabled, sendRequest]); + }, [getHref, isFleetEnabled, sendRequest]); const createAgentConfigButton = useMemo( () => ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/data_stream/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/data_stream/index.tsx index 7b0641e66fd43e..0fdba54a041454 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/data_stream/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/data_stream/index.tsx @@ -5,13 +5,14 @@ */ import React from 'react'; import { HashRouter as Router, Route, Switch } from 'react-router-dom'; +import { PAGE_ROUTING_PATHS } from '../../constants'; import { DataStreamListPage } from './list_page'; export const DataStreamApp: React.FunctionComponent = () => { return ( - + diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/data_stream/list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/data_stream/list_page/index.tsx index cff138c6a16ca0..e07d2a86d1a1f5 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/data_stream/list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/data_stream/list_page/index.tsx @@ -19,7 +19,7 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage, FormattedDate } from '@kbn/i18n/react'; import { DataStream } from '../../../types'; import { WithHeaderLayout } from '../../../layouts'; -import { useGetDataStreams, useStartDeps, usePagination } from '../../../hooks'; +import { useGetDataStreams, useStartDeps, usePagination, useBreadcrumbs } from '../../../hooks'; import { PackageIcon } from '../../../components/package_icon'; import { DataStreamRowActions } from './components/data_stream_row_actions'; @@ -55,6 +55,8 @@ const DataStreamListPageLayout: React.FunctionComponent = ({ children }) => ( ); export const DataStreamListPage: React.FunctionComponent<{}> = () => { + useBreadcrumbs('data_streams'); + const { data: { fieldFormats }, } = useStartDeps(); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_card.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_card.tsx index 41d1c0ee4f965e..e3d8cdc8f49850 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_card.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/components/package_card.tsx @@ -7,7 +7,7 @@ import React from 'react'; import styled from 'styled-components'; import { EuiCard } from '@elastic/eui'; import { PackageInfo, PackageListItem } from '../../../types'; -import { useLinks } from '../hooks'; +import { useLink } from '../../../hooks'; import { PackageIcon } from '../../../components/package_icon'; export interface BadgeProps { @@ -32,13 +32,12 @@ export function PackageCard({ icons, ...restProps }: PackageCardProps) { - const { toDetailView } = useLinks(); + const { getHref } = useLink(); let urlVersion = version; // if this is an installed package, link to the version installed if ('savedObject' in restProps) { urlVersion = restProps.savedObject.attributes.version || version; } - const url = toDetailView({ name, version: urlVersion }); return ( } - href={url} + href={getHref('integration_details', { pkgkey: `${name}-${urlVersion}` })} /> ); } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_links.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_links.tsx index d4ed3624a6e685..436163bafcfe4f 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_links.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_links.tsx @@ -3,32 +3,15 @@ * or more contributor license agreements. Licensed under the Elastic License; * you may not use this file except in compliance with the Elastic License. */ -import { generatePath } from 'react-router-dom'; import { useCore } from '../../../hooks/use_core'; import { PLUGIN_ID } from '../../../constants'; import { epmRouteService } from '../../../services'; -import { DetailViewPanelName } from '../../../types'; -import { BASE_PATH, EPM_PATH, EPM_DETAIL_VIEW_PATH } from '../../../constants'; - -// TODO: get this from server/packages/handlers.ts (move elsewhere?) -// seems like part of the name@version change -interface DetailParams { - name: string; - version: string; - panel?: DetailViewPanelName; - withAppRoot?: boolean; -} const removeRelativePath = (relativePath: string): string => new URL(relativePath, 'http://example.com').pathname; export function useLinks() { const { http } = useCore(); - function appRoot(path: string) { - // include '#' because we're using HashRouter - return http.basePath.prepend(BASE_PATH + '#' + path); - } - return { toAssets: (path: string) => http.basePath.prepend( @@ -49,13 +32,5 @@ export function useLinks() { const filePath = `${epmRouteService.getInfoPath(pkgkey)}/${imagePath}`; return http.basePath.prepend(filePath); }, - toListView: () => appRoot(EPM_PATH), - toDetailView: ({ name, version, panel, withAppRoot = true }: DetailParams) => { - // panel is optional, but `generatePath` won't accept `path: undefined` - // so use this to pass `{ pkgkey }` or `{ pkgkey, panel }` - const params = Object.assign({ pkgkey: `${name}-${version}` }, panel ? { panel } : {}); - const path = generatePath(EPM_DETAIL_VIEW_PATH, params); - return withAppRoot ? appRoot(path) : path; - }, }; } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_package_install.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_package_install.tsx index 244a9a2c7426e8..36b81e786b935a 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_package_install.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/hooks/use_package_install.tsx @@ -6,12 +6,12 @@ import createContainer from 'constate'; import React, { useCallback, useState } from 'react'; +import { useHistory } from 'react-router-dom'; import { FormattedMessage } from '@kbn/i18n/react'; import { NotificationsStart } from 'src/core/public'; import { toMountPoint } from '../../../../../../../../../src/plugins/kibana_react/public'; import { PackageInfo } from '../../../types'; -import { sendInstallPackage, sendRemovePackage } from '../../../hooks'; -import { useLinks } from '.'; +import { sendInstallPackage, sendRemovePackage, useLink } from '../../../hooks'; import { InstallStatus } from '../../../types'; interface PackagesInstall { @@ -29,7 +29,8 @@ type InstallPackageProps = Pick & { type SetPackageInstallStatusProps = Pick & PackageInstallItem; function usePackageInstall({ notifications }: { notifications: NotificationsStart }) { - const { toDetailView } = useLinks(); + const history = useHistory(); + const { getPath } = useLink(); const [packages, setPackage] = useState({}); const setPackageInstallStatus = useCallback( @@ -88,12 +89,11 @@ function usePackageInstall({ notifications }: { notifications: NotificationsStar } else { setPackageInstallStatus({ name, status: InstallStatus.installed, version }); if (fromUpdate) { - const settingsUrl = toDetailView({ - name, - version, + const settingsPath = getPath('integration_details', { + pkgkey: `${name}-${version}`, panel: 'settings', }); - window.location.href = settingsUrl; + history.push(settingsPath); } notifications.toasts.addSuccess({ title: toMountPoint( @@ -113,7 +113,7 @@ function usePackageInstall({ notifications }: { notifications: NotificationsStar }); } }, - [getPackageInstallStatus, notifications.toasts, setPackageInstallStatus, toDetailView] + [getPackageInstallStatus, notifications.toasts, setPackageInstallStatus, getPath, history] ); const uninstallPackage = useCallback( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx index 2c8ee7ca2fcf3f..ca1a8df534044f 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/index.tsx @@ -6,24 +6,26 @@ import React from 'react'; import { HashRouter as Router, Switch, Route } from 'react-router-dom'; -import { useConfig } from '../../hooks'; +import { PAGE_ROUTING_PATHS } from '../../constants'; +import { useConfig, useBreadcrumbs } from '../../hooks'; import { CreateDatasourcePage } from '../agent_config/create_datasource_page'; import { EPMHomePage } from './screens/home'; import { Detail } from './screens/detail'; export const EPMApp: React.FunctionComponent = () => { + useBreadcrumbs('integrations'); const { epm } = useConfig(); return epm.enabled ? ( - + - + - + diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/data_sources_panel.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/data_sources_panel.tsx index c82b7ed2297a7c..7459c943fa831c 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/data_sources_panel.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/data_sources_panel.tsx @@ -7,29 +7,22 @@ import React, { Fragment } from 'react'; import { EuiTitle } from '@elastic/eui'; import { Redirect } from 'react-router-dom'; -import { useLinks, useGetPackageInstallStatus } from '../../hooks'; +import { useGetPackageInstallStatus } from '../../hooks'; import { InstallStatus } from '../../../../types'; +import { useLink } from '../../../../hooks'; interface DataSourcesPanelProps { name: string; version: string; } export const DataSourcesPanel = ({ name, version }: DataSourcesPanelProps) => { - const { toDetailView } = useLinks(); + const { getPath } = useLink(); const getPackageInstallStatus = useGetPackageInstallStatus(); const packageInstallStatus = getPackageInstallStatus(name); // if they arrive at this page and the package is not installed, send them to overview // this happens if they arrive with a direct url or they uninstall while on this tab if (packageInstallStatus.status !== InstallStatus.installed) - return ( - - ); + return ; return ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx index cf51296d468a99..5c2d1373d0b0ef 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/header.tsx @@ -9,11 +9,9 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiFlexGroup, EuiFlexItem, EuiPage, EuiTitle, IconType, EuiButton } from '@elastic/eui'; import { PackageInfo } from '../../../../types'; -import { EPM_PATH } from '../../../../constants'; import { useCapabilities, useLink } from '../../../../hooks'; import { IconPanel } from '../../components/icon_panel'; import { NavButtonBack } from '../../components/nav_button_back'; -import { useLinks } from '../../hooks'; import { CenterColumn, LeftColumn, RightColumn } from './layout'; import { UpdateIcon } from '../../components/icons'; @@ -36,14 +34,13 @@ export function Header(props: HeaderProps) { installedVersion = props.savedObject.attributes.version; } const hasWriteCapabilites = useCapabilities().write; - const { toListView } = useLinks(); - const ADD_DATASOURCE_URI = useLink(`${EPM_PATH}/${name}-${version}/add-datasource`); + const { getHref } = useLink(); const updateAvailable = installedVersion && installedVersion < latestVersion ? true : false; return ( & Pick; export function DetailLayout(props: LayoutProps) { - const { name: packageName, version, icons, restrictWidth } = props; + const { name: packageName, version, icons, restrictWidth, title: packageTitle } = props; const iconType = usePackageIconType({ packageName, version, icons }); + useBreadcrumbs('integration_details', { pkgTitle: packageTitle }); return ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/side_nav_links.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/side_nav_links.tsx index aa63cf2ba175df..65a437269ec6ab 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/side_nav_links.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/detail/side_nav_links.tsx @@ -8,7 +8,8 @@ import styled from 'styled-components'; import { i18n } from '@kbn/i18n'; import { EuiButtonEmpty, EuiButtonEmptyProps } from '@elastic/eui'; import { PackageInfo, entries, DetailViewPanelName, InstallStatus } from '../../../../types'; -import { useLinks, useGetPackageInstallStatus } from '../../hooks'; +import { useLink } from '../../../../hooks'; +import { useGetPackageInstallStatus } from '../../hooks'; export type NavLinkProps = Pick & { active: DetailViewPanelName; @@ -27,7 +28,7 @@ const PanelDisplayNames: Record = { }; export function SideNavLinks({ name, version, active }: NavLinkProps) { - const { toDetailView } = useLinks(); + const { getHref } = useLink(); const getPackageInstallStatus = useGetPackageInstallStatus(); const packageInstallStatus = getPackageInstallStatus(name); @@ -35,7 +36,7 @@ export function SideNavLinks({ name, version, active }: NavLinkProps) { {entries(PanelDisplayNames).map(([panel, display]) => { const Link = styled(EuiButtonEmpty).attrs({ - href: toDetailView({ name, version, panel }), + href: getHref('integration_details', { pkgkey: `${name}-${version}`, panel }), })` font-weight: ${p => active === panel diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx index 983a322de1088c..84ad3593a5bf16 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/epm/screens/home/index.tsx @@ -8,11 +8,8 @@ import React, { useState } from 'react'; import { useRouteMatch, Switch, Route } from 'react-router-dom'; import { Props as EuiTabProps } from '@elastic/eui/src/components/tabs/tab'; import { i18n } from '@kbn/i18n'; -import { - EPM_LIST_ALL_PACKAGES_PATH, - EPM_LIST_INSTALLED_PACKAGES_PATH, -} from '../../../../constants'; -import { useLink, useGetCategories, useGetPackages } from '../../../../hooks'; +import { PAGE_ROUTING_PATHS } from '../../../../constants'; +import { useLink, useGetCategories, useGetPackages, useBreadcrumbs } from '../../../../hooks'; import { WithHeaderLayout } from '../../../../layouts'; import { CategorySummaryItem } from '../../../../types'; import { PackageListGrid } from '../../components/package_list_grid'; @@ -23,9 +20,7 @@ export function EPMHomePage() { const { params: { tabId }, } = useRouteMatch<{ tabId?: string }>(); - - const ALL_PACKAGES_URI = useLink(EPM_LIST_ALL_PACKAGES_PATH); - const INSTALLED_PACKAGES_URI = useLink(EPM_LIST_INSTALLED_PACKAGES_PATH); + const { getHref } = useLink(); return ( - + - + @@ -65,6 +60,7 @@ export function EPMHomePage() { } function InstalledPackages() { + useBreadcrumbs('integrations_installed'); const { data: allPackages, isLoading: isLoadingPackages } = useGetPackages(); const [selectedCategory, setSelectedCategory] = useState(''); @@ -117,6 +113,7 @@ function InstalledPackages() { } function AvailablePackages() { + useBreadcrumbs('integrations_all'); const [selectedCategory, setSelectedCategory] = useState(''); const { data: categoryPackagesRes, isLoading: isLoadingPackages } = useGetPackages({ category: selectedCategory, diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/agent_details.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/agent_details.tsx index 6a1e6dc226903b..03f1a67fe95abc 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/agent_details.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/components/agent_details.tsx @@ -14,7 +14,6 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { Agent, AgentConfig } from '../../../../types'; -import { AGENT_CONFIG_DETAILS_PATH } from '../../../../constants'; import { useLink } from '../../../../hooks'; import { AgentHealth } from '../../components'; @@ -22,7 +21,7 @@ export const AgentDetailsContent: React.FunctionComponent<{ agent: Agent; agentConfig?: AgentConfig; }> = memo(({ agent, agentConfig }) => { - const agentConfigUrl = useLink(AGENT_CONFIG_DETAILS_PATH); + const { getHref } = useLink(); return ( {[ @@ -53,7 +52,7 @@ export const AgentDetailsContent: React.FunctionComponent<{ defaultMessage: 'Agent configuration', }), description: agentConfig ? ( - + {agentConfig.name || agent.config_id} ) : ( diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/index.tsx index aa46f7cf976cdd..2ebc495d5dda76 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_details_page/index.tsx @@ -19,16 +19,13 @@ import { import { Props as EuiTabProps } from '@elastic/eui/src/components/tabs/tab'; import { FormattedMessage } from '@kbn/i18n/react'; import { i18n } from '@kbn/i18n'; -import { AgentRefreshContext } from './hooks'; -import { - FLEET_AGENTS_PATH, - FLEET_AGENT_DETAIL_PATH, - AGENT_CONFIG_DETAILS_PATH, -} from '../../../constants'; +import { Agent, AgentConfig } from '../../../types'; +import { PAGE_ROUTING_PATHS } from '../../../constants'; import { Loading, Error } from '../../../components'; -import { useGetOneAgent, useGetOneAgentConfig, useLink } from '../../../hooks'; +import { useGetOneAgent, useGetOneAgentConfig, useLink, useBreadcrumbs } from '../../../hooks'; import { WithHeaderLayout } from '../../../layouts'; import { AgentHealth } from '../components'; +import { AgentRefreshContext } from './hooks'; import { AgentEventsTable, AgentDetailsActionMenu, AgentDetailsContent } from './components'; const Divider = styled.div` @@ -41,6 +38,7 @@ export const AgentDetailsPage: React.FunctionComponent = () => { const { params: { agentId, tabId = '' }, } = useRouteMatch<{ agentId: string; tabId?: string }>(); + const { getHref } = useLink(); const { isLoading, isInitialRequest, @@ -56,16 +54,16 @@ export const AgentDetailsPage: React.FunctionComponent = () => { sendRequest: sendAgentConfigRequest, } = useGetOneAgentConfig(agentData?.item?.config_id); - const agentListUrl = useLink(FLEET_AGENTS_PATH); - const agentActivityTabUrl = useLink(`${FLEET_AGENT_DETAIL_PATH}${agentId}/activity`); - const agentDetailsTabUrl = useLink(`${FLEET_AGENT_DETAIL_PATH}${agentId}/details`); - const agentConfigUrl = useLink(AGENT_CONFIG_DETAILS_PATH); - const headerLeftContent = useMemo( () => ( - + { ), - [agentData, agentId, agentListUrl] + [agentData, agentId, getHref] ); const headerRightContent = useMemo( @@ -114,7 +112,9 @@ export const AgentDetailsPage: React.FunctionComponent = () => { content: isAgentConfigLoading ? ( ) : agentConfigData?.item ? ( - + {agentConfigData.item.name || agentData.item.config_id} ) : ( @@ -143,7 +143,7 @@ export const AgentDetailsPage: React.FunctionComponent = () => { ) : ( undefined ), - [agentConfigData, agentConfigUrl, agentData, isAgentConfigLoading] + [agentConfigData, agentData, getHref, isAgentConfigLoading] ); const headerTabs = useMemo(() => { @@ -153,7 +153,7 @@ export const AgentDetailsPage: React.FunctionComponent = () => { name: i18n.translate('xpack.ingestManager.agentDetails.subTabs.activityLogTab', { defaultMessage: 'Activity log', }), - href: agentActivityTabUrl, + href: getHref('fleet_agent_details', { agentId, tabId: 'activity' }), isSelected: !tabId || tabId === 'activity', }, { @@ -161,11 +161,11 @@ export const AgentDetailsPage: React.FunctionComponent = () => { name: i18n.translate('xpack.ingestManager.agentDetails.subTabs.detailsTab', { defaultMessage: 'Agent details', }), - href: agentDetailsTabUrl, + href: getHref('fleet_agent_details', { agentId, tabId: 'details' }), isSelected: tabId === 'details', }, ]; - }, [agentActivityTabUrl, agentDetailsTabUrl, tabId]); + }, [getHref, agentId, tabId]); return ( { error={error} /> ) : agentData && agentData.item ? ( - - { - return ( - - ); - }} - /> - { - return ; - }} - /> - + ) : ( { error={i18n.translate( 'xpack.ingestManager.agentDetails.agentNotFoundErrorDescription', { - defaultMessage: 'Cannot found agent ID {agentId}', + defaultMessage: 'Cannot find agent ID {agentId}', values: { agentId, }, @@ -233,3 +218,32 @@ export const AgentDetailsPage: React.FunctionComponent = () => { ); }; + +const AgentDetailsPageContent: React.FunctionComponent<{ + agent: Agent; + agentConfig?: AgentConfig; +}> = ({ agent, agentConfig }) => { + useBreadcrumbs('fleet_agent_details', { + agentHost: + typeof agent.local_metadata.host === 'object' && + typeof agent.local_metadata.host.hostname === 'string' + ? agent.local_metadata.host.hostname + : '-', + }); + return ( + + { + return ; + }} + /> + { + return ; + }} + /> + + ); +}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx index 84056df2aca320..56cc0028f0cf94 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/agent_list_page/index.tsx @@ -34,17 +34,14 @@ import { useGetAgents, useUrlParams, useLink, + useBreadcrumbs, } from '../../../hooks'; -import { ConnectedLink, AgentReassignConfigFlyout } from '../components'; +import { AgentReassignConfigFlyout } from '../components'; import { SearchBar } from '../../../components/search_bar'; import { AgentHealth } from '../components/agent_health'; import { AgentUnenrollProvider } from '../components/agent_unenroll_provider'; import { AgentStatusKueryHelper } from '../../../services'; -import { - FLEET_AGENT_DETAIL_PATH, - AGENT_CONFIG_DETAILS_PATH, - AGENT_SAVED_OBJECT_TYPE, -} from '../../../constants'; +import { AGENT_SAVED_OBJECT_TYPE } from '../../../constants'; const NO_WRAP_TRUNCATE_STYLE: CSSProperties = Object.freeze({ overflow: 'hidden', @@ -77,8 +74,8 @@ const statusFilters = [ const RowActions = React.memo<{ agent: Agent; onReassignClick: () => void; refresh: () => void }>( ({ agent, refresh, onReassignClick }) => { + const { getHref } = useLink(); const hasWriteCapabilites = useCapabilities().write; - const DETAILS_URI = useLink(FLEET_AGENT_DETAIL_PATH); const [isOpen, setIsOpen] = useState(false); const handleCloseMenu = useCallback(() => setIsOpen(false), [setIsOpen]); const handleToggleMenu = useCallback(() => setIsOpen(!isOpen), [isOpen]); @@ -101,7 +98,11 @@ const RowActions = React.memo<{ agent: Agent; onReassignClick: () => void; refre > + = () => { + useBreadcrumbs('fleet_agent_list'); + const { getHref } = useLink(); const defaultKuery: string = (useUrlParams().urlParams.kuery as string) || ''; const hasWriteCapabilites = useCapabilities().write; + // Agent data states const [showInactive, setShowInactive] = useState(false); @@ -241,8 +245,6 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { const agentConfigs = agentConfigsRequest.data ? agentConfigsRequest.data.items : []; const { isLoading: isAgentConfigsLoading } = agentConfigsRequest; - const CONFIG_DETAILS_URI = useLink(AGENT_CONFIG_DETAILS_PATH); - const columns = [ { field: 'local_metadata.host.hostname', @@ -250,9 +252,9 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { defaultMessage: 'Host', }), render: (host: string, agent: Agent) => ( - + {safeMetadata(host)} - + ), }, { @@ -274,7 +276,7 @@ export const AgentListPage: React.FunctionComponent<{}> = () => { diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/index.tsx index ff7c2f705e7b75..43173124d6bae8 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/agent_enrollment_flyout/index.tsx @@ -23,10 +23,14 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import { AgentConfig } from '../../../../types'; import { EnrollmentStepAgentConfig } from './config_selection'; -import { useGetOneEnrollmentAPIKey, useCore, useGetSettings, useLink } from '../../../../hooks'; +import { + useGetOneEnrollmentAPIKey, + useCore, + useGetSettings, + useLink, + useFleetStatus, +} from '../../../../hooks'; import { ManualInstructions } from '../../../../components/enrollment_instructions'; -import { FLEET_PATH } from '../../../../constants'; -import { useFleetStatus } from '../../../../hooks/use_fleet_status'; interface Props { onClose: () => void; @@ -37,9 +41,9 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ onClose, agentConfigs = [], }) => { + const { getHref } = useLink(); const core = useCore(); const fleetStatus = useFleetStatus(); - const fleetLink = useLink(FLEET_PATH); const [selectedAPIKeyId, setSelectedAPIKeyId] = useState(); @@ -120,7 +124,7 @@ export const AgentEnrollmentFlyout: React.FunctionComponent = ({ defaultMessage="Fleet needs to be set up before agents can be enrolled. {link}" values={{ link: ( - + = ({ children }) => { + const { getHref } = useLink(); const hasWriteCapabilites = useCapabilities().write; const agentStatusRequest = useGetAgentStatus(undefined, { pollIntervalMs: REFRESH_INTERVAL_MS, @@ -163,8 +164,8 @@ export const ListLayout: React.FunctionComponent<{}> = ({ children }) => { defaultMessage="Agents" /> ), - isSelected: routeMatch.path === FLEET_AGENTS_PATH, - href: useLink(FLEET_AGENTS_PATH), + isSelected: routeMatch.path === PAGE_ROUTING_PATHS.fleet_agent_list, + href: getHref('fleet_agent_list'), }, { name: ( @@ -173,8 +174,8 @@ export const ListLayout: React.FunctionComponent<{}> = ({ children }) => { defaultMessage="Enrollment tokens" /> ), - isSelected: routeMatch.path === FLEET_ENROLLMENT_TOKENS_PATH, - href: useLink(FLEET_ENROLLMENT_TOKENS_PATH), + isSelected: routeMatch.path === PAGE_ROUTING_PATHS.fleet_enrollment_tokens, + href: getHref('fleet_enrollment_tokens'), }, ] as unknown) as EuiTabProps[] } diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/navigation/child_routes.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/navigation/child_routes.tsx deleted file mode 100644 index 8af0e0a5cbc257..00000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/navigation/child_routes.tsx +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import React from 'react'; -import { Route, Switch } from 'react-router-dom'; - -interface RouteConfig { - path: string; - component: React.ComponentType; - routes?: RouteConfig[]; -} - -export const ChildRoutes: React.FunctionComponent<{ - routes?: RouteConfig[]; - useSwitch?: boolean; - [other: string]: any; -}> = ({ routes, useSwitch = true, ...rest }) => { - if (!routes) { - return null; - } - const Parent = useSwitch ? Switch : React.Fragment; - return ( - - {routes.map(route => ( - { - const Component = route.component; - return ; - }} - /> - ))} - - ); -}; diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/navigation/connected_link.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/navigation/connected_link.tsx deleted file mode 100644 index 489ee85ffe28ac..00000000000000 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/components/navigation/connected_link.tsx +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License; - * you may not use this file except in compliance with the Elastic License. - */ -import React from 'react'; -import { EuiLink } from '@elastic/eui'; -import { Link, withRouter } from 'react-router-dom'; - -export function ConnectedLinkComponent({ - location, - path, - query, - disabled, - children, - ...props -}: { - location: any; - path: string; - disabled: boolean; - query: any; - [key: string]: any; -}) { - if (disabled) { - return ; - } - - // Shorthand for pathname - const pathname = path || _.get(props.to, 'pathname') || location.pathname; - - return ( - - ); -} - -export const ConnectedLink = withRouter(ConnectedLinkComponent); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/index.tsx index c11e3a49c7693e..add495ce0c194d 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/enrollment_token_list_page/index.tsx @@ -20,6 +20,7 @@ import { import { FormattedMessage, FormattedDate } from '@kbn/i18n/react'; import { ENROLLMENT_API_KEYS_SAVED_OBJECT_TYPE } from '../../../constants'; import { + useBreadcrumbs, usePagination, useGetEnrollmentAPIKeys, useGetAgentConfigs, @@ -125,6 +126,7 @@ const DeleteButton: React.FunctionComponent<{ apiKey: EnrollmentAPIKey; refresh: }; export const EnrollmentTokenListPage: React.FunctionComponent<{}> = () => { + useBreadcrumbs('fleet_enrollment_tokens'); const [flyoutOpen, setFlyoutOpen] = useState(false); const [search, setSearch] = useState(''); const { pagination, setPagination, pageSizeOptions } = usePagination(); diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx index c820a9b867b635..9bb77ca44b8485 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/fleet/index.tsx @@ -5,17 +5,18 @@ */ import React from 'react'; import { HashRouter as Router, Route, Switch, Redirect } from 'react-router-dom'; +import { PAGE_ROUTING_PATHS } from '../../constants'; import { Loading } from '../../components'; -import { useConfig, useCore } from '../../hooks'; +import { useConfig, useCore, useFleetStatus, useBreadcrumbs } from '../../hooks'; import { AgentListPage } from './agent_list_page'; import { SetupPage } from './setup_page'; import { AgentDetailsPage } from './agent_details_page'; import { NoAccessPage } from './error_pages/no_access'; import { EnrollmentTokenListPage } from './enrollment_token_list_page'; import { ListLayout } from './components/list_layout'; -import { useFleetStatus } from '../../hooks/use_fleet_status'; export const FleetApp: React.FunctionComponent = () => { + useBreadcrumbs('fleet'); const core = useCore(); const { fleet } = useConfig(); @@ -41,16 +42,20 @@ export const FleetApp: React.FunctionComponent = () => { return ( - } /> - + } + /> + - + - + diff --git a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_section.tsx b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_section.tsx index 0f6d3c5b55ce69..6e61a55466e879 100644 --- a/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_section.tsx +++ b/x-pack/plugins/ingest_manager/public/applications/ingest_manager/sections/overview/components/agent_section.tsx @@ -16,10 +16,10 @@ import { import { OverviewPanel } from './overview_panel'; import { OverviewStats } from './overview_stats'; import { useLink, useGetAgentStatus } from '../../../hooks'; -import { FLEET_PATH } from '../../../constants'; import { Loading } from '../../fleet/components'; export const OverviewAgentSection = () => { + const { getHref } = useLink(); const agentStatusRequest = useGetAgentStatus({}); return ( @@ -34,7 +34,7 @@ export const OverviewAgentSection = () => { /> - + = ({ agentConfigs, }) => { + const { getHref } = useLink(); const datasourcesRequest = useGetDatasources({ page: 1, perPage: 10000, @@ -40,7 +40,7 @@ export const OverviewConfigurationSection: React.FC<{ agentConfigs: AgentConfig[ /> - + { + const { getHref } = useLink(); const datastreamRequest = useGetDataStreams(); const { data: { fieldFormats }, @@ -55,7 +55,7 @@ export const OverviewDatastreamSection: React.FC = () => { /> - + { + const { getHref } = useLink(); const packagesRequest = useGetPackages(); const res = packagesRequest.data?.response; const total = res?.length ?? 0; @@ -40,7 +40,7 @@ export const OverviewIntegrationSection: React.FC = () => { /> - + { + useBreadcrumbs('overview'); + // Agent enrollment flyout state const [isEnrollmentFlyoutOpen, setIsEnrollmentFlyoutOpen] = useState(false); diff --git a/x-pack/plugins/ingest_manager/public/plugin.ts b/x-pack/plugins/ingest_manager/public/plugin.ts index 2c6ed9d81744e4..fd4e08f6194958 100644 --- a/x-pack/plugins/ingest_manager/public/plugin.ts +++ b/x-pack/plugins/ingest_manager/public/plugin.ts @@ -64,8 +64,13 @@ export class IngestManagerPlugin IngestManagerStartDeps, IngestManagerStart ]; - const { renderApp } = await import('./applications/ingest_manager'); - return renderApp(coreStart, params, deps, startDeps, config); + const { renderApp, teardownIngestManager } = await import('./applications/ingest_manager'); + const unmount = renderApp(coreStart, params, deps, startDeps, config); + + return () => { + unmount(); + teardownIngestManager(coreStart); + }; }, }); }