diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx index cd893c17369889c..da55f274bd77ceb 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/DetailView/index.tsx @@ -16,7 +16,6 @@ import { EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Location } from 'history'; import { first } from 'lodash'; import React from 'react'; import { useHistory } from 'react-router-dom'; @@ -58,7 +57,6 @@ const TransactionLinkName = euiStyled.div` interface Props { errorGroup: APIReturnType<'GET /api/apm/services/{serviceName}/errors/{groupId}'>; urlParams: IUrlParams; - location: Location; } // TODO: Move query-string-based tabs into a re-usable component? @@ -70,7 +68,7 @@ function getCurrentTab( return selectedTab ? selectedTab : first(tabs) || {}; } -export function DetailView({ errorGroup, urlParams, location }: Props) { +export function DetailView({ errorGroup, urlParams }: Props) { const history = useHistory(); const { transaction, error, occurrencesCount } = errorGroup; diff --git a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx index 0e300818327ff5e..a7f11ae4c5726aa 100644 --- a/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx +++ b/x-pack/plugins/apm/public/components/app/ErrorGroupDetails/index.tsx @@ -16,7 +16,6 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { Fragment } from 'react'; -import { RouteComponentProps } from 'react-router-dom'; import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { useTrackPageview } from '../../../../../observability/public'; import { NOT_AVAILABLE_LABEL } from '../../../../common/i18n'; @@ -65,43 +64,42 @@ function ErrorGroupHeader({ isUnhandled?: boolean; }) { return ( - <> - - + + + +

+ {i18n.translate('xpack.apm.errorGroupDetails.errorGroupTitle', { + defaultMessage: 'Error group {errorGroupId}', + values: { + errorGroupId: getShortGroupId(groupId), + }, + })} +

+
+
+ + {isUnhandled && ( - -

- {i18n.translate('xpack.apm.errorGroupDetails.errorGroupTitle', { - defaultMessage: 'Error group {errorGroupId}', - values: { - errorGroupId: getShortGroupId(groupId), - }, - })} -

-
+ + {i18n.translate('xpack.apm.errorGroupDetails.unhandledLabel', { + defaultMessage: 'Unhandled', + })} +
- - {isUnhandled && ( - - - {i18n.translate('xpack.apm.errorGroupDetails.unhandledLabel', { - defaultMessage: 'Unhandled', - })} - - - )} -
- + )} +
); } -type ErrorGroupDetailsProps = RouteComponentProps<{ +interface ErrorGroupDetailsProps { groupId: string; serviceName: string; -}>; +} -export function ErrorGroupDetails({ location, match }: ErrorGroupDetailsProps) { - const { serviceName, groupId } = match.params; +export function ErrorGroupDetails({ + serviceName, + groupId, +}: ErrorGroupDetailsProps) { const { urlParams } = useUrlParams(); const { environment, kuery, start, end } = urlParams; const { data: errorGroupData } = useFetcher( @@ -198,11 +196,7 @@ export function ErrorGroupDetails({ location, match }: ErrorGroupDetailsProps) { {showDetails && ( - + )} ); diff --git a/x-pack/plugins/apm/public/components/app/Main/route_config/index.tsx b/x-pack/plugins/apm/public/components/app/Main/route_config/index.tsx index 6d58b9a2575bb1e..105a86d9f7574af 100644 --- a/x-pack/plugins/apm/public/components/app/Main/route_config/index.tsx +++ b/x-pack/plugins/apm/public/components/app/Main/route_config/index.tsx @@ -5,17 +5,14 @@ * 2.0. */ -import { isFunction } from 'lodash'; import { i18n } from '@kbn/i18n'; import React from 'react'; import { RouteComponentProps } from 'react-router-dom'; -import { ApmServiceContextProvider } from '../../../../context/apm_service/apm_service_context'; import { getServiceNodeName } from '../../../../../common/service_nodes'; import { APMRouteDefinition } from '../../../../application/routes'; import { toQuery } from '../../../shared/Links/url_helpers'; import { ErrorGroupDetails } from '../../ErrorGroupDetails'; import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; -import { ServiceDetails } from '../../service_details'; import { ServiceNodeMetrics } from '../../service_node_metrics'; import { Settings } from '../../Settings'; import { AgentConfigurations } from '../../Settings/AgentConfigurations'; @@ -29,12 +26,19 @@ import { EditAgentConfigurationRouteHandler, } from './route_handlers/agent_configuration'; import { enableServiceOverview } from '../../../../../common/ui_settings_keys'; - -import { renderAsRedirectTo } from '../../../routing/render_as_redirect'; +import { redirectTo } from '../../../routing/render_as_redirect'; import { ApmMainTemplate } from '../../../routing/templates/apm_main_template'; import { serviceInventoryRoutes } from '../../../routing/views/service_inventory'; import { traceOverviewRoute } from '../../../routing/views/trace_overview'; import { serviceMapRoute } from '../../../routing/views/service_map'; +import { ApmServiceTemplate } from '../../../routing/templates/apm_service_template'; +import { ServiceProfiling } from '../../service_profiling'; +import { ErrorGroupOverview } from '../../error_group_overview'; +import { ServiceMap } from '../../service_map'; +import { ServiceNodeOverview } from '../../service_node_overview'; +import { ServiceMetrics } from '../../service_metrics'; +import { ServiceOverview } from '../../service_overview'; +import { TransactionOverview } from '../../transaction_overview'; // These component function definitions are used below with the `component` // property of the route definitions. @@ -43,119 +47,207 @@ import { serviceMapRoute } from '../../../routing/views/service_map'; // new component every render. This results in the existing component unmounting // and the new component mounting instead of just updating the existing component. -function ServiceDetailsErrors( +function ServiceDetailsErrorsRouteView( props: RouteComponentProps<{ serviceName: string }> ) { - return ; + const { serviceName } = props.match.params; + return ( + + + + ); } -function ServiceDetailsMetrics( +function ErrorGroupDetailsRouteView( + props: RouteComponentProps<{ serviceName: string; groupId: string }> +) { + const { serviceName, groupId } = props.match.params; + return ( + + + + ); +} + +function ServiceDetailsMetricsRouteView( props: RouteComponentProps<{ serviceName: string }> ) { - return ; + const { serviceName } = props.match.params; + return ( + + + + ); } -function ServiceDetailsNodes( +function ServiceDetailsNodesRouteView( props: RouteComponentProps<{ serviceName: string }> ) { - return ; + const { serviceName } = props.match.params; + return ( + + + + ); } -function ServiceDetailsOverview( +function ServiceDetailsOverviewRouteView( props: RouteComponentProps<{ serviceName: string }> ) { - return ; + const { serviceName } = props.match.params; + return ( + + + + ); } -function ServiceDetailsServiceMap( +function ServiceDetailsServiceMapRouteView( props: RouteComponentProps<{ serviceName: string }> ) { - return ; + const { serviceName } = props.match.params; + return ( + + + + ); } -function ServiceDetailsTransactions( +function ServiceDetailsTransactionsRouteView( props: RouteComponentProps<{ serviceName: string }> ) { - return ; + const { serviceName } = props.match.params; + return ( + + + + ); } -function ServiceDetailsProfiling( +function ServiceDetailsProfilingRouteView( props: RouteComponentProps<{ serviceName: string }> ) { - return ; + const { serviceName } = props.match.params; + return ( + + + + ); } -function SettingsAgentConfiguration(props: RouteComponentProps<{}>) { +function ServiceNodeMetricsRouteView( + props: RouteComponentProps<{ + serviceName: string; + serviceNodeName: string; + }> +) { + const { serviceName, serviceNodeName } = props.match.params; return ( - - - + + + ); } -function SettingsAnomalyDetection(props: RouteComponentProps<{}>) { +function TransactionDetailsRouteView( + props: RouteComponentProps<{ serviceName: string }> +) { + const { serviceName } = props.match.params; return ( - - - + + + ); } -function SettingsApmIndices(props: RouteComponentProps<{}>) { +function SettingsAgentConfigurationRouteView(props: RouteComponentProps<{}>) { return ( - - - + + + + + ); } -const SettingsApmIndicesTitle = i18n.translate( - 'xpack.apm.breadcrumb.settings.indicesTitle', - { defaultMessage: 'Indices' } -); -function SettingsCustomizeUI(props: RouteComponentProps<{}>) { +function SettingsAnomalyDetectionRouteView(props: RouteComponentProps<{}>) { return ( - - - + + + + + ); } -function DefaultServicePageRouteHandler( - props: RouteComponentProps<{ serviceName: string }> -) { - const { uiSettings } = useApmPluginContext().core; - const { serviceName } = props.match.params; - if (uiSettings.get(enableServiceOverview)) { - return renderAsRedirectTo(`/services/${serviceName}/overview`)(props); - } - return renderAsRedirectTo(`/services/${serviceName}/transactions`)(props); +function SettingsApmIndicesRouteView(props: RouteComponentProps<{}>) { + return ( + + + + + + ); } +function SettingsCustomizeUI(props: RouteComponentProps<{}>) { + return ( + + + + + + ); +} + +const SettingsApmIndicesTitle = i18n.translate( + 'xpack.apm.views.settings.indices.title', + { defaultMessage: 'Indices' } +); + const SettingsAgentConfigurationTitle = i18n.translate( - 'xpack.apm.breadcrumb.settings.agentConfigurationTitle', + 'xpack.apm.views.settings.agentConfiguration.title', { defaultMessage: 'Agent Configuration' } ); const CreateAgentConfigurationTitle = i18n.translate( - 'xpack.apm.breadcrumb.settings.createAgentConfigurationTitle', + 'xpack.apm.views.settings.createAgentConfiguration.title', { defaultMessage: 'Create Agent Configuration' } ); const EditAgentConfigurationTitle = i18n.translate( - 'xpack.apm.breadcrumb.settings.editAgentConfigurationTitle', + 'xpack.apm.views.settings.editAgentConfiguration.title', { defaultMessage: 'Edit Agent Configuration' } ); const SettingsCustomizeUITitle = i18n.translate( - 'xpack.apm.breadcrumb.settings.customizeUI', - { - defaultMessage: 'Customize UI', - } + 'xpack.apm.views.settings.customizeUI.title', + { defaultMessage: 'Customize UI' } ); const SettingsAnomalyDetectionTitle = i18n.translate( - 'xpack.apm.breadcrumb.settings.anomalyDetection', - { - defaultMessage: 'Anomaly detection', - } + 'xpack.apm.views.settings.anomalyDetection.title', + { defaultMessage: 'Anomaly detection' } ); +const SettingsTitle = i18n.translate('xpack.apm.views.listSettings.title', { + defaultMessage: 'Settings', +}); /** * The array of route definitions to be used when the application * creates the routes. @@ -174,57 +266,43 @@ export const routes: APMRouteDefinition[] = [ { exact: true, path: '/settings', - render: renderAsRedirectTo('/settings/agent-configuration'), - breadcrumb: i18n.translate('xpack.apm.breadcrumb.listSettingsTitle', { - defaultMessage: 'Settings', - }), + render: redirectTo('/settings/agent-configuration'), + breadcrumb: SettingsTitle, }, { exact: true, path: '/settings/agent-configuration', - component: withApmMainTemplate(SettingsAgentConfiguration, { - pageTitle: SettingsAgentConfigurationTitle, - }), + component: SettingsAgentConfigurationRouteView, breadcrumb: SettingsAgentConfigurationTitle, }, { exact: true, path: '/settings/agent-configuration/create', - component: withApmMainTemplate(CreateAgentConfigurationRouteHandler, { - pageTitle: CreateAgentConfigurationTitle, - }), + component: CreateAgentConfigurationRouteHandler, breadcrumb: CreateAgentConfigurationTitle, }, { exact: true, path: '/settings/agent-configuration/edit', breadcrumb: EditAgentConfigurationTitle, - component: withApmMainTemplate(EditAgentConfigurationRouteHandler, { - pageTitle: EditAgentConfigurationTitle, - }), + component: EditAgentConfigurationRouteHandler, }, { exact: true, path: '/settings/apm-indices', - component: withApmMainTemplate(SettingsApmIndices, { - pageTitle: SettingsApmIndicesTitle, - }), + component: SettingsApmIndicesRouteView, breadcrumb: SettingsApmIndicesTitle, }, { exact: true, path: '/settings/customize-ui', - component: withApmMainTemplate(SettingsCustomizeUI, { - pageTitle: SettingsCustomizeUITitle, - }), + component: SettingsCustomizeUI, breadcrumb: SettingsCustomizeUITitle, }, { exact: true, path: '/settings/anomaly-detection', - component: withApmMainTemplate(SettingsAnomalyDetection, { - pageTitle: SettingsAnomalyDetectionTitle, - }), + component: SettingsAnomalyDetectionRouteView, breadcrumb: SettingsAnomalyDetectionTitle, }, @@ -235,54 +313,43 @@ export const routes: APMRouteDefinition[] = [ exact: true, path: '/services/:serviceName', breadcrumb: ({ match }) => match.params.serviceName, - component: DefaultServicePageRouteHandler, + component: RedirectToDefaultServiceRouteView, }, { exact: true, path: '/services/:serviceName/overview', - breadcrumb: i18n.translate('xpack.apm.breadcrumb.overviewTitle', { + breadcrumb: i18n.translate('xpack.apm.views.overview.title', { defaultMessage: 'Overview', }), - component: withApmServiceContext(ServiceDetailsOverview, { - pageTitle: ({ match }) => match.params.serviceName, + component: ServiceDetailsOverviewRouteView, + }, + { + exact: true, + path: '/services/:serviceName/transactions', + component: ServiceDetailsTransactionsRouteView, + breadcrumb: i18n.translate('xpack.apm.views.transactions.title', { + defaultMessage: 'Transactions', }), }, - // errors { exact: true, path: '/services/:serviceName/errors/:groupId', - component: withApmServiceContext(ErrorGroupDetails, { - pageTitle: ({ match }) => match.params.serviceName, - }), + component: ErrorGroupDetailsRouteView, breadcrumb: ({ match }) => match.params.groupId, }, { exact: true, path: '/services/:serviceName/errors', - component: withApmServiceContext(ServiceDetailsErrors, { - pageTitle: ({ match }) => match.params.serviceName, - }), - breadcrumb: i18n.translate('xpack.apm.breadcrumb.errorsTitle', { + component: ServiceDetailsErrorsRouteView, + breadcrumb: i18n.translate('xpack.apm.views.errors.title', { defaultMessage: 'Errors', }), }, - { - exact: true, - path: '/services/:serviceName/transactions', - component: withApmServiceContext(ServiceDetailsTransactions, { - pageTitle: ({ match }) => match.params.serviceName, - }), - breadcrumb: i18n.translate('xpack.apm.breadcrumb.transactionsTitle', { - defaultMessage: 'Transactions', - }), - }, { exact: true, path: '/services/:serviceName/metrics', - component: withApmServiceContext(ServiceDetailsMetrics, { - pageTitle: ({ match }) => match.params.serviceName, - }), - breadcrumb: i18n.translate('xpack.apm.breadcrumb.metricsTitle', { + component: ServiceDetailsMetricsRouteView, + breadcrumb: i18n.translate('xpack.apm.views.metrics.title', { defaultMessage: 'Metrics', }), }, @@ -290,10 +357,8 @@ export const routes: APMRouteDefinition[] = [ { exact: true, path: '/services/:serviceName/nodes', - component: withApmServiceContext(ServiceDetailsNodes, { - pageTitle: ({ match }) => match.params.serviceName, - }), - breadcrumb: i18n.translate('xpack.apm.breadcrumb.nodesTitle', { + component: ServiceDetailsNodesRouteView, + breadcrumb: i18n.translate('xpack.apm.views.nodes.title', { defaultMessage: 'JVMs', }), }, @@ -301,17 +366,13 @@ export const routes: APMRouteDefinition[] = [ { exact: true, path: '/services/:serviceName/nodes/:serviceNodeName/metrics', - component: withApmServiceContext(ServiceNodeMetrics, { - pageTitle: ({ match }) => match.params.serviceName, - }), + component: ServiceNodeMetricsRouteView, breadcrumb: ({ match }) => getServiceNodeName(match.params.serviceNodeName), }, { exact: true, path: '/services/:serviceName/transactions/view', - component: withApmServiceContext(TransactionDetails, { - pageTitle: ({ match }) => match.params.serviceName, - }), + component: TransactionDetailsRouteView, breadcrumb: ({ location }) => { const query = toQuery(location.search); return query.transactionName as string; @@ -320,24 +381,19 @@ export const routes: APMRouteDefinition[] = [ { exact: true, path: '/services/:serviceName/profiling', - component: withApmServiceContext(ServiceDetailsProfiling, { - pageTitle: ({ match }) => match.params.serviceName, - }), - breadcrumb: i18n.translate('xpack.apm.breadcrumb.serviceProfilingTitle', { + component: ServiceDetailsProfilingRouteView, + breadcrumb: i18n.translate('xpack.apm.views.serviceProfiling.title', { defaultMessage: 'Profiling', }), }, { exact: true, path: '/services/:serviceName/service-map', - component: withApmServiceContext(ServiceDetailsServiceMap, { - pageTitle: ({ match }) => match.params.serviceName, - }), - breadcrumb: i18n.translate('xpack.apm.breadcrumb.serviceMapTitle', { + component: ServiceDetailsServiceMapRouteView, + breadcrumb: i18n.translate('xpack.apm.views.serviceMap.title', { defaultMessage: 'Service Map', }), }, - /* * Utilility routes */ @@ -349,53 +405,13 @@ export const routes: APMRouteDefinition[] = [ }, ]; -interface Options { - pageTitle: string | ((props: any) => any); - environmentSelector?: boolean; -} - -{ - /* */ -} - -function withApmServiceContext( - WrappedComponent: React.ComponentType, - options: Options -) { - return (props: any) => { - const pageTitle = isFunction(options.pageTitle) - ? options.pageTitle(props) - : options.pageTitle; - - return ( - - - - - - ); - }; -} - -function withApmMainTemplate( - WrappedComponent: React.ComponentType, - options: Options +function RedirectToDefaultServiceRouteView( + props: RouteComponentProps<{ serviceName: string }> ) { - return (props: T) => { - const pageTitle = isFunction(options.pageTitle) - ? options.pageTitle(props) - : options.pageTitle; - - return ( - - - - ); - }; + const { uiSettings } = useApmPluginContext().core; + const { serviceName } = props.match.params; + if (uiSettings.get(enableServiceOverview)) { + return redirectTo(`/services/${serviceName}/overview`)(props); + } + return redirectTo(`/services/${serviceName}/transactions`)(props); } diff --git a/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx b/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx index 21aca0d05741d68..62f871bc3f78587 100644 --- a/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/error_group_overview/index.tsx @@ -68,42 +68,38 @@ export function ErrorGroupOverview({ serviceName }: ErrorGroupOverviewProps) { useTrackPageview({ app: 'apm', path: 'error_group_overview', delay: 15000 }); if (!errorDistributionData || !errorGroupListData) { - return ; + return null; } return ( - <> - + + + + + + - - - - - - + + + +

Errors

+
+ - - - -

Errors

-
- - - -
-
-
- + + + +
); } diff --git a/x-pack/plugins/apm/public/components/app/service_details/index.tsx b/x-pack/plugins/apm/public/components/app/service_details/index.tsx deleted file mode 100644 index 9c6db66c377f72f..000000000000000 --- a/x-pack/plugins/apm/public/components/app/service_details/index.tsx +++ /dev/null @@ -1,20 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import React from 'react'; -import { RouteComponentProps } from 'react-router-dom'; -import { ServiceDetailTabs } from './service_detail_tabs'; - -interface Props extends RouteComponentProps<{ serviceName: string }> { - tab: React.ComponentProps['tab']; -} - -export function ServiceDetails({ match, tab }: Props) { - const { serviceName } = match.params; - - return ; -} diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_detail_tabs.tsx b/x-pack/plugins/apm/public/components/app/service_details/service_detail_tabs.tsx deleted file mode 100644 index 46d62136b4cf41a..000000000000000 --- a/x-pack/plugins/apm/public/components/app/service_details/service_detail_tabs.tsx +++ /dev/null @@ -1,213 +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 - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ - -import { EuiTab } from '@elastic/eui'; -import { i18n } from '@kbn/i18n'; -import React, { ReactNode } from 'react'; -import { EuiBetaBadge } from '@elastic/eui'; -import { EuiFlexItem } from '@elastic/eui'; -import { EuiFlexGroup } from '@elastic/eui'; -import { EuiTabs } from '@elastic/eui'; -import { isJavaAgentName, isRumAgentName } from '../../../../common/agent_name'; -import { enableServiceOverview } from '../../../../common/ui_settings_keys'; -import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; -import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; -import { useUrlParams } from '../../../context/url_params_context/use_url_params'; -import { useErrorOverviewHref } from '../../shared/Links/apm/ErrorOverviewLink'; -import { useMetricOverviewHref } from '../../shared/Links/apm/MetricOverviewLink'; -import { useServiceMapHref } from '../../shared/Links/apm/ServiceMapLink'; -import { useServiceNodeOverviewHref } from '../../shared/Links/apm/ServiceNodeOverviewLink'; -import { useServiceOverviewHref } from '../../shared/Links/apm/service_overview_link'; -import { useTransactionsOverviewHref } from '../../shared/Links/apm/transaction_overview_link'; -import { useServiceProfilingHref } from '../../shared/Links/apm/service_profiling_link'; -import { ErrorGroupOverview } from '../error_group_overview'; -import { ServiceMap } from '../service_map'; -import { ServiceNodeOverview } from '../service_node_overview'; -import { ServiceMetrics } from '../service_metrics'; -import { ServiceOverview } from '../service_overview'; -import { TransactionOverview } from '../transaction_overview'; -import { ServiceProfiling } from '../service_profiling'; -import { Correlations } from '../correlations'; -import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; - -interface Tab { - key: string; - href: string; - text: ReactNode; - hidden?: boolean; - render: () => ReactNode; -} - -interface Props { - serviceName: string; - tab: - | 'errors' - | 'metrics' - | 'nodes' - | 'overview' - | 'service-map' - | 'profiling' - | 'transactions'; -} - -export function ServiceDetailTabs({ serviceName, tab }: Props) { - const { agentName, transactionType } = useApmServiceContext(); - const { - core: { uiSettings }, - config, - } = useApmPluginContext(); - const { - urlParams: { latencyAggregationType }, - } = useUrlParams(); - - const overviewTab = { - key: 'overview', - href: useServiceOverviewHref({ serviceName, transactionType }), - text: i18n.translate('xpack.apm.serviceDetails.overviewTabLabel', { - defaultMessage: 'Overview', - }), - render: () => ( - - ), - }; - - const transactionsTab = { - key: 'transactions', - href: useTransactionsOverviewHref({ - serviceName, - latencyAggregationType, - transactionType, - }), - text: i18n.translate('xpack.apm.serviceDetails.transactionsTabLabel', { - defaultMessage: 'Transactions', - }), - render: () => , - }; - - const errorsTab = { - key: 'errors', - href: useErrorOverviewHref(serviceName), - text: i18n.translate('xpack.apm.serviceDetails.errorsTabLabel', { - defaultMessage: 'Errors', - }), - render: () => { - return ; - }, - }; - - const serviceMapTab = { - key: 'service-map', - href: useServiceMapHref(serviceName), - text: i18n.translate('xpack.apm.home.serviceMapTabLabel', { - defaultMessage: 'Service Map', - }), - render: () => , - }; - - const nodesListTab = { - key: 'nodes', - href: useServiceNodeOverviewHref(serviceName), - text: i18n.translate('xpack.apm.serviceDetails.nodesTabLabel', { - defaultMessage: 'JVMs', - }), - render: () => , - }; - - const metricsTab = { - key: 'metrics', - href: useMetricOverviewHref(serviceName), - text: i18n.translate('xpack.apm.serviceDetails.metricsTabLabel', { - defaultMessage: 'Metrics', - }), - render: () => - agentName ? ( - - ) : null, - }; - - const profilingTab = { - key: 'profiling', - href: useServiceProfilingHref({ serviceName }), - hidden: !config.profilingEnabled, - text: ( - - - {i18n.translate('xpack.apm.serviceDetails.profilingTabLabel', { - defaultMessage: 'Profiling', - })} - - - - - - ), - render: () => , - }; - - const tabs: Tab[] = [transactionsTab, errorsTab]; - - if (uiSettings.get(enableServiceOverview)) { - tabs.unshift(overviewTab); - } - - if (isJavaAgentName(agentName)) { - tabs.push(nodesListTab); - } else if (agentName && !isRumAgentName(agentName)) { - tabs.push(metricsTab); - } - - tabs.push(serviceMapTab, profilingTab); - - const selectedTab = tabs.find((serviceTab) => serviceTab.key === tab); - - return ( - <> - - {tabs - .filter((t) => !t.hidden) - .map(({ href, key, text }) => ( - - {text} - - ))} -
- -
-
- {selectedTab ? selectedTab.render() : null} - - ); -} - -// Since our `EuiTab` components have `APMLink`s inside of them and not just -// `href`s, we need to override the color of the links inside or they will all -// be the primary color. -const StyledTabs = euiStyled(EuiTabs)` - padding-bottom: ${({ theme }) => `${theme.eui.gutterTypes.gutterMedium}`}; - margin-bottom: ${({ theme }) => `${theme.eui.gutterTypes.gutterMedium}`}; - border-bottom: ${({ theme }) => theme.eui.euiBorderThin}; - background: ${({ theme }) => theme.eui.euiColorEmptyShade}; -`; diff --git a/x-pack/plugins/apm/public/components/app/service_map/index.tsx b/x-pack/plugins/apm/public/components/app/service_map/index.tsx index b338d1e4ab03dca..714228d58f9620d 100644 --- a/x-pack/plugins/apm/public/components/app/service_map/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_map/index.tsx @@ -7,7 +7,6 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; import React, { PropsWithChildren, ReactNode } from 'react'; -import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { isActivePlatinumLicense } from '../../../../common/license_check'; import { useTrackPageview } from '../../../../../observability/public'; import { @@ -18,7 +17,6 @@ import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; import { useLicenseContext } from '../../../context/license/use_license_context'; import { useTheme } from '../../../hooks/use_theme'; import { useUrlParams } from '../../../context/url_params_context/use_url_params'; -import { DatePicker } from '../../shared/DatePicker'; import { LicensePrompt } from '../../shared/license_prompt'; import { Controls } from './Controls'; import { Cytoscape } from './Cytoscape'; @@ -28,31 +26,16 @@ import { EmptyPrompt } from './empty_prompt'; import { Popover } from './Popover'; import { TimeoutPrompt } from './timeout_prompt'; import { useRefDimensions } from './useRefDimensions'; +import { SearchBar } from '../../shared/search_bar'; interface ServiceMapProps { serviceName?: string; } -const ServiceMapDatePickerFlexGroup = euiStyled(EuiFlexGroup)` - padding: ${({ theme }) => theme.eui.euiSizeM}; - border-bottom: ${({ theme }) => theme.eui.euiBorderThin}; - margin: 0; -`; - -function DatePickerSection() { - return ( - - - - - - ); -} - function PromptContainer({ children }: { children: ReactNode }) { return ( <> - + - + +
- - - - - {data.charts.map((chart) => ( - - - - - - ))} - - - - + + + {data.charts.map((chart) => ( + + + + + + ))} + + + ); } diff --git a/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx b/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx index 6198fa1e6816713..79620cc554264a9 100644 --- a/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_node_metrics/index.tsx @@ -18,7 +18,6 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { RouteComponentProps } from 'react-router-dom'; import { euiStyled } from '../../../../../../../src/plugins/kibana_react/common'; import { SERVICE_NODE_NAME_MISSING } from '../../../../common/service_nodes'; import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context'; @@ -48,16 +47,18 @@ const MetadataFlexGroup = euiStyled(EuiFlexGroup)` `${theme.eui.paddingSizes.m} 0 0 ${theme.eui.paddingSizes.m}`}; `; -type ServiceNodeMetricsProps = RouteComponentProps<{ +interface ServiceNodeMetricsProps { serviceName: string; serviceNodeName: string; -}>; +} -export function ServiceNodeMetrics({ match }: ServiceNodeMetricsProps) { +export function ServiceNodeMetrics({ + serviceName, + serviceNodeName, +}: ServiceNodeMetricsProps) { const { urlParams: { kuery, start, end }, } = useUrlParams(); - const { serviceName, serviceNodeName } = match.params; const { agentName } = useApmServiceContext(); const { data } = useServiceMetricChartsFetcher({ serviceNodeName }); @@ -167,7 +168,6 @@ export function ServiceNodeMetrics({ match }: ServiceNodeMetricsProps) { )} - {agentName && ( diff --git a/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx index 25dd4a838daee82..4595dd7cab857c5 100644 --- a/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_node_overview/index.tsx @@ -143,22 +143,18 @@ function ServiceNodeOverview({ serviceName }: ServiceNodeOverviewProps) { ]; return ( - <> - - - - - - + + + ); } diff --git a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx index 93172d629eab5a7..43ebe9bebac5ea9 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/index.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { useTrackPageview } from '../../../../../observability/public'; import { isRumAgentName } from '../../../../common/agent_name'; import { AnnotationsContextProvider } from '../../../context/annotations/annotations_context'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; import { ChartPointerEventContextProvider } from '../../../context/chart_pointer_event/chart_pointer_event_context'; import { useBreakPoints } from '../../../hooks/use_break_points'; import { LatencyChart } from '../../shared/charts/latency_chart'; @@ -29,14 +30,12 @@ import { ServiceOverviewTransactionsTable } from './service_overview_transaction export const chartHeight = 288; interface ServiceOverviewProps { - agentName?: string; serviceName: string; } -export function ServiceOverview({ - agentName, - serviceName, -}: ServiceOverviewProps) { +export function ServiceOverview({ serviceName }: ServiceOverviewProps) { + const { agentName } = useApmServiceContext(); + useTrackPageview({ app: 'apm', path: 'service_overview' }); useTrackPageview({ app: 'apm', path: 'service_overview', delay: 15000 }); @@ -49,8 +48,6 @@ export function ServiceOverview({ return ( - - diff --git a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/intance_details.tsx b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/intance_details.tsx index ba1da7e6dd6eb9c..5c2bbd9e20c59e0 100644 --- a/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/intance_details.tsx +++ b/x-pack/plugins/apm/public/components/app/service_overview/service_overview_instances_table/intance_details.tsx @@ -32,10 +32,7 @@ import { pct } from '../../../../style/variables'; import { getAgentIcon } from '../../../shared/AgentIcon/get_agent_icon'; import { KeyValueFilterList } from '../../../shared/key_value_filter_list'; import { pushNewItemToKueryBar } from '../../../shared/KueryBar/utils'; -import { - getCloudIcon, - getContainerIcon, -} from '../../service_details/service_icons'; +import { getCloudIcon, getContainerIcon } from '../../../shared/service_icons'; import { useInstanceDetailsFetcher } from './use_instance_details_fetcher'; type ServiceInstanceDetails = APIReturnType<'GET /api/apm/services/{serviceName}/service_overview_instances/details/{serviceNodeName}'>; diff --git a/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx b/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx index 9d552fc8dad2a6d..e664088c0a2b384 100644 --- a/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx +++ b/x-pack/plugins/apm/public/components/app/service_profiling/index.tsx @@ -83,8 +83,6 @@ export function ServiceProfiling({ return ( <> - - diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/TransactionTabs.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/TransactionTabs.tsx index 7f8ffb62d9e7288..ae58e6f60cf09cb 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/TransactionTabs.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/TransactionTabs.tsx @@ -7,7 +7,6 @@ import { EuiSpacer, EuiTab, EuiTabs } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { Location } from 'history'; import React from 'react'; import { useHistory } from 'react-router-dom'; import { LogStream } from '../../../../../../infra/public'; @@ -19,7 +18,6 @@ import { WaterfallContainer } from './WaterfallContainer'; import { IWaterfall } from './WaterfallContainer/Waterfall/waterfall_helpers/waterfall_helpers'; interface Props { - location: Location; transaction: Transaction; urlParams: IUrlParams; waterfall: IWaterfall; @@ -27,7 +25,6 @@ interface Props { } export function TransactionTabs({ - location, transaction, urlParams, waterfall, @@ -47,9 +44,9 @@ export function TransactionTabs({ { history.replace({ - ...location, + ...history.location, search: fromQuery({ - ...toQuery(location.search), + ...toQuery(history.location.search), detailTab: key, }), }); @@ -66,7 +63,6 @@ export function TransactionTabs({ ; urlParams: IUrlParams; waterfall: IWaterfall; exceedsMax: boolean; }) { return ( void; + toggleFlyout: ({ history }: { history: History }) => void; } export function WaterfallFlyout({ waterfallItemId, waterfall, - location, toggleFlyout, }: Props) { const history = useHistory(); @@ -52,14 +44,14 @@ export function WaterfallFlyout({ totalDuration={waterfall.duration} span={currentItem.doc} parentTransaction={parentTransaction} - onClose={() => toggleFlyout({ history, location })} + onClose={() => toggleFlyout({ history })} /> ); case 'transaction': return ( toggleFlyout({ history, location })} + onClose={() => toggleFlyout({ history })} rootTransactionDuration={ waterfall.rootTransaction?.transaction.duration.us } diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/Waterfall/accordion_waterfall.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/Waterfall/accordion_waterfall.tsx index baced34ad3e56f1..b0721791081fac2 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/Waterfall/accordion_waterfall.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/Waterfall/accordion_waterfall.tsx @@ -6,7 +6,6 @@ */ import { EuiAccordion, EuiAccordionProps } from '@elastic/eui'; -import { Location } from 'history'; import { isEmpty } from 'lodash'; import React, { useState } from 'react'; import { euiStyled } from '../../../../../../../../../../src/plugins/kibana_react/common'; @@ -23,7 +22,6 @@ interface AccordionWaterfallProps { level: number; duration: IWaterfall['duration']; waterfallItemId?: string; - location: Location; errorsPerTransaction: IWaterfall['errorsPerTransaction']; childrenByParentId: Record; onToggleEntryTransaction?: () => void; @@ -100,7 +98,6 @@ export function AccordionWaterfall(props: AccordionWaterfallProps) { duration, childrenByParentId, waterfallItemId, - location, errorsPerTransaction, timelineMargins, onClickWaterfallItem, @@ -160,7 +157,6 @@ export function AccordionWaterfall(props: AccordionWaterfallProps) { item={child} level={nextLevel} waterfallItemId={waterfallItemId} - location={location} errorsPerTransaction={errorsPerTransaction} duration={duration} childrenByParentId={childrenByParentId} diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx index 4be595ac16c6cc5..d7613699221b488 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/Waterfall/index.tsx @@ -7,7 +7,7 @@ import { EuiButtonEmpty, EuiCallOut } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { History, Location } from 'history'; +import { History } from 'history'; import React, { useState } from 'react'; import { useHistory } from 'react-router-dom'; import { euiStyled } from '../../../../../../../../../../src/plugins/kibana_react/common'; @@ -40,14 +40,12 @@ const TIMELINE_MARGINS = { const toggleFlyout = ({ history, item, - location, }: { history: History; item?: IWaterfallItem; - location: Location; }) => { history.replace({ - ...location, + ...history.location, search: fromQuery({ ...toQuery(location.search), flyoutDetailTab: undefined, @@ -63,15 +61,9 @@ const WaterfallItemsContainer = euiStyled.div` interface Props { waterfallItemId?: string; waterfall: IWaterfall; - location: Location; exceedsMax: boolean; } -export function Waterfall({ - waterfall, - exceedsMax, - waterfallItemId, - location, -}: Props) { +export function Waterfall({ waterfall, exceedsMax, waterfallItemId }: Props) { const history = useHistory(); const [isAccordionOpen, setIsAccordionOpen] = useState(true); const itemContainerHeight = 58; // TODO: This is a nasty way to calculate the height of the svg element. A better approach should be found @@ -97,13 +89,12 @@ export function Waterfall({ item={entryWaterfallTransaction} level={0} waterfallItemId={waterfallItemId} - location={location} errorsPerTransaction={waterfall.errorsPerTransaction} duration={duration} childrenByParentId={childrenByParentId} timelineMargins={TIMELINE_MARGINS} onClickWaterfallItem={(item: IWaterfallItem) => - toggleFlyout({ history, item, location }) + toggleFlyout({ history, item }) } onToggleEntryTransaction={() => setIsAccordionOpen((isOpen) => !isOpen)} /> @@ -148,7 +139,6 @@ export function Waterfall({ diff --git a/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/index.tsx b/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/index.tsx index 11de1ac85c434d3..817e747551d95c3 100644 --- a/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/index.tsx +++ b/x-pack/plugins/apm/public/components/app/transaction_details/WaterfallWithSummmary/WaterfallContainer/index.tsx @@ -5,7 +5,6 @@ * 2.0. */ -import { Location } from 'history'; import React from 'react'; import { keyBy } from 'lodash'; import { useParams } from 'react-router-dom'; @@ -19,13 +18,11 @@ import { WaterfallLegends } from './WaterfallLegends'; interface Props { urlParams: IUrlParams; - location: Location; waterfall: IWaterfall; exceedsMax: boolean; } export function WaterfallContainer({ - location, urlParams, waterfall, exceedsMax, @@ -84,7 +81,6 @@ export function WaterfallContainer({
; - -export function TransactionDetails({ - location, - match, -}: TransactionDetailsProps) { +export function TransactionDetails() { const { urlParams } = useUrlParams(); const history = useHistory(); const { @@ -82,10 +77,6 @@ export function TransactionDetails({ return ( <> - - - -

{transactionName}

@@ -116,7 +107,6 @@ export function TransactionDetails({ - - diff --git a/x-pack/plugins/apm/public/components/routing/app_root.tsx b/x-pack/plugins/apm/public/components/routing/app_root.tsx index f5c72991e34cec5..4f7d810a1557460 100644 --- a/x-pack/plugins/apm/public/components/routing/app_root.tsx +++ b/x-pack/plugins/apm/public/components/routing/app_root.tsx @@ -36,49 +36,6 @@ const MainContainer = euiStyled.div` height: 100%; `; -function MountApmHeaderActionMenu() { - const { setHeaderActionMenu } = useApmPluginContext().appMountParameters; - - return ( - - - - ); -} - -function ApmThemeProvider({ children }: { children: React.ReactNode }) { - const [darkMode] = useUiSetting$('theme:darkMode'); - - return ( - ({ - ...outerTheme, - eui: darkMode ? euiDarkVars : euiLightVars, - darkMode, - })} - > - {children} - - ); -} - -function App() { - useBreadcrumbs(routes); - - return ( - - - - - - {routes.map((route, i) => ( - - ))} - - - ); -} - export function ApmAppRoot({ apmPluginContextValue, pluginsStart, @@ -100,7 +57,19 @@ export function ApmAppRoot({ - + + + + + + {routes.map((route, i) => ( + + ))} + + @@ -112,3 +81,30 @@ export function ApmAppRoot({ ); } + +function MountApmHeaderActionMenu() { + useBreadcrumbs(routes); + const { setHeaderActionMenu } = useApmPluginContext().appMountParameters; + + return ( + + + + ); +} + +function ApmThemeProvider({ children }: { children: React.ReactNode }) { + const [darkMode] = useUiSetting$('theme:darkMode'); + + return ( + ({ + ...outerTheme, + eui: darkMode ? euiDarkVars : euiLightVars, + darkMode, + })} + > + {children} + + ); +} diff --git a/x-pack/plugins/apm/public/components/routing/render_as_redirect.tsx b/x-pack/plugins/apm/public/components/routing/render_as_redirect.tsx index 6c838bfd4eb1ece..68ff2fce77f1377 100644 --- a/x-pack/plugins/apm/public/components/routing/render_as_redirect.tsx +++ b/x-pack/plugins/apm/public/components/routing/render_as_redirect.tsx @@ -12,7 +12,7 @@ import { Redirect, RouteComponentProps } from 'react-router-dom'; * Given a path, redirect to that location, preserving the search and maintaining * backward-compatibilty with legacy (pre-7.9) hash-based URLs. */ -export function renderAsRedirectTo(to: string) { +export function redirectTo(to: string) { return ({ location }: RouteComponentProps<{}>) => { let resolvedUrl: URL | undefined; diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx index 4ae5c5755c0e7d1..71bca7a77cd968b 100644 --- a/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_main_template.tsx @@ -7,7 +7,6 @@ import React from 'react'; import { useKibana } from '../../../../../../../src/plugins/kibana_react/public'; - import { ApmPluginStartDeps } from '../../../plugin'; import { EnvironmentFilter } from '../../shared/EnvironmentFilter'; @@ -25,7 +24,7 @@ export function ApmMainTemplate({ environmentSelector = true, children, }: { - pageTitle: string; + pageTitle: React.ReactNode; environmentSelector?: boolean; children: React.ReactNode; }) { diff --git a/x-pack/plugins/apm/public/components/routing/templates/apm_service_template.tsx b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template.tsx new file mode 100644 index 000000000000000..8e14faf3607ae5a --- /dev/null +++ b/x-pack/plugins/apm/public/components/routing/templates/apm_service_template.tsx @@ -0,0 +1,217 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import React from 'react'; +import { i18n } from '@kbn/i18n'; +import { + EuiFlexGroup, + EuiFlexItem, + EuiTitle, + EuiTabs, + EuiTab, + EuiBetaBadge, +} from '@elastic/eui'; +import { ApmMainTemplate } from './apm_main_template'; +import { ApmServiceContextProvider } from '../../../context/apm_service/apm_service_context'; +import { enableServiceOverview } from '../../../../common/ui_settings_keys'; +import { isJavaAgentName, isRumAgentName } from '../../../../common/agent_name'; +import { ServiceIcons } from '../../shared/service_icons'; +import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; +import { useErrorOverviewHref } from '../../shared/Links/apm/ErrorOverviewLink'; +import { useMetricOverviewHref } from '../../shared/Links/apm/MetricOverviewLink'; +import { useServiceMapHref } from '../../shared/Links/apm/ServiceMapLink'; +import { useServiceNodeOverviewHref } from '../../shared/Links/apm/ServiceNodeOverviewLink'; +import { useServiceOverviewHref } from '../../shared/Links/apm/service_overview_link'; +import { useServiceProfilingHref } from '../../shared/Links/apm/service_profiling_link'; +import { useTransactionsOverviewHref } from '../../shared/Links/apm/transaction_overview_link'; +import { useUrlParams } from '../../../context/url_params_context/use_url_params'; +import { Correlations } from '../../app/correlations'; +import { SearchBar } from '../../shared/search_bar'; + +interface Tab { + key: TabKey; + href: string; + text: React.ReactNode; + hidden?: boolean; +} + +type TabKey = + | 'errors' + | 'metrics' + | 'nodes' + | 'overview' + | 'service-map' + | 'profiling' + | 'transactions'; + +export function ApmServiceTemplate({ + children, + serviceName, + selectedTab, + searchBarOptions, +}: { + children: React.ReactNode; + serviceName: string; + selectedTab: TabKey; + searchBarOptions?: { + hidden?: boolean; + showTransactionTypeSelector?: boolean; + showTimeComparison?: boolean; + }; +}) { + return ( + + + + +

{serviceName}

+
+
+ + + +
+ + } + > + + + + + + + + + + + + + {children} + +
+ ); +} + +function TabNavigation({ + serviceName, + selectedTab, +}: { + serviceName: string; + selectedTab: TabKey; +}) { + const { agentName, transactionType } = useApmServiceContext(); + const { core, config } = useApmPluginContext(); + const { urlParams } = useUrlParams(); + + const tabs: Tab[] = [ + { + key: 'overview', + href: useServiceOverviewHref({ serviceName, transactionType }), + text: i18n.translate('xpack.apm.serviceDetails.overviewTabLabel', { + defaultMessage: 'Overview', + }), + hidden: !core.uiSettings.get(enableServiceOverview), + }, + { + key: 'transactions', + href: useTransactionsOverviewHref({ + serviceName, + latencyAggregationType: urlParams.latencyAggregationType, + transactionType, + }), + text: i18n.translate('xpack.apm.serviceDetails.transactionsTabLabel', { + defaultMessage: 'Transactions', + }), + }, + { + key: 'errors', + href: useErrorOverviewHref(serviceName), + text: i18n.translate('xpack.apm.serviceDetails.errorsTabLabel', { + defaultMessage: 'Errors', + }), + }, + { + key: 'nodes', + href: useServiceNodeOverviewHref(serviceName), + text: i18n.translate('xpack.apm.serviceDetails.nodesTabLabel', { + defaultMessage: 'JVMs', + }), + hidden: !isJavaAgentName(agentName), + }, + { + key: 'metrics', + href: useMetricOverviewHref(serviceName), + text: i18n.translate('xpack.apm.serviceDetails.metricsTabLabel', { + defaultMessage: 'Metrics', + }), + hidden: + !agentName || isRumAgentName(agentName) || isJavaAgentName(agentName), + }, + { + key: 'service-map', + href: useServiceMapHref(serviceName), + text: i18n.translate('xpack.apm.home.serviceMapTabLabel', { + defaultMessage: 'Service Map', + }), + }, + { + key: 'profiling', + href: useServiceProfilingHref({ serviceName }), + hidden: !config.profilingEnabled, + text: ( + + + {i18n.translate('xpack.apm.serviceDetails.profilingTabLabel', { + defaultMessage: 'Profiling', + })} + + + + + + ), + }, + ]; + + return ( + + {tabs + .filter((t) => !t.hidden) + .map(({ href, key, text }) => ( + + {text} + + ))} + + ); +} diff --git a/x-pack/plugins/apm/public/components/routing/views/service_inventory.tsx b/x-pack/plugins/apm/public/components/routing/views/service_inventory.tsx index da147ced12291ae..7027f655acf72cb 100644 --- a/x-pack/plugins/apm/public/components/routing/views/service_inventory.tsx +++ b/x-pack/plugins/apm/public/components/routing/views/service_inventory.tsx @@ -8,7 +8,7 @@ import { i18n } from '@kbn/i18n'; import { RouteComponentProps } from 'react-router-dom'; import React from 'react'; -import { renderAsRedirectTo } from '../../routing/render_as_redirect'; +import { redirectTo } from '../../routing/render_as_redirect'; import { ApmMainTemplate } from '../../routing/templates/apm_main_template'; import { ServiceInventory } from '../../app/service_inventory'; @@ -20,7 +20,7 @@ export const serviceInventoryRoutes = [ { exact: true, path: '/', - render: renderAsRedirectTo('/services'), + render: redirectTo('/services'), breadcrumb: 'APM', }, diff --git a/x-pack/plugins/apm/public/components/shared/search_bar.tsx b/x-pack/plugins/apm/public/components/shared/search_bar.tsx index e5aba0e76e6b39b..17497e1fb4b30a8 100644 --- a/x-pack/plugins/apm/public/components/shared/search_bar.tsx +++ b/x-pack/plugins/apm/public/components/shared/search_bar.tsx @@ -15,7 +15,6 @@ import { import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n/react'; import React from 'react'; -import { euiStyled } from '../../../../../../src/plugins/kibana_react/common'; import { enableInspectEsQueries } from '../../../../observability/public'; import { useApmPluginContext } from '../../context/apm_plugin/use_apm_plugin_context'; import { useKibanaUrl } from '../../hooks/useKibanaUrl'; @@ -27,6 +26,8 @@ import { TimeComparison } from './time_comparison'; import { TransactionTypeSelect } from './transaction_type_select'; interface Props { + hidden?: boolean; + showKueryBar?: boolean; showTimeComparison?: boolean; showTransactionTypeSelector?: boolean; } @@ -78,10 +79,17 @@ function DebugQueryCallout() { } export function SearchBar({ + hidden = false, + showKueryBar = true, showTimeComparison = false, showTransactionTypeSelector = false, }: Props) { const { isSmall, isMedium, isLarge, isXl, isXXL } = useBreakPoints(); + + if (hidden) { + return null; + } + return ( <> @@ -101,9 +109,12 @@ export function SearchBar({ )} - - - + + {showKueryBar && ( + + + + )} diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_icons/alert_details.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/alert_details.tsx similarity index 84% rename from x-pack/plugins/apm/public/components/app/service_details/service_icons/alert_details.tsx rename to x-pack/plugins/apm/public/components/shared/service_icons/alert_details.tsx index 0066480230c6bff..9f6378ccb449713 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_icons/alert_details.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/alert_details.tsx @@ -14,12 +14,12 @@ import { RULE_ID, RULE_NAME, } from '@kbn/rule-data-utils/target/technical_field_names'; -import { parseTechnicalFields } from '../../../../../../rule_registry/common'; -import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; -import { useApmPluginContext } from '../../../../context/apm_plugin/use_apm_plugin_context'; -import { APIReturnType } from '../../../../services/rest/createCallApmApi'; -import { asPercent, asDuration } from '../../../../../common/utils/formatters'; -import { TimestampTooltip } from '../../../shared/TimestampTooltip'; +import { parseTechnicalFields } from '../../../../../rule_registry/common'; +import { useUrlParams } from '../../../context/url_params_context/use_url_params'; +import { useApmPluginContext } from '../../../context/apm_plugin/use_apm_plugin_context'; +import { APIReturnType } from '../../../services/rest/createCallApmApi'; +import { asPercent, asDuration } from '../../../../common/utils/formatters'; +import { TimestampTooltip } from '../TimestampTooltip'; interface AlertDetailProps { alerts: APIReturnType<'GET /api/apm/services/{serviceName}/alerts'>['alerts']; diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_icons/cloud_details.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/cloud_details.tsx similarity index 97% rename from x-pack/plugins/apm/public/components/app/service_details/service_icons/cloud_details.tsx rename to x-pack/plugins/apm/public/components/shared/service_icons/cloud_details.tsx index 2e19bc684d68152..2e8fcfa1df67252 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_icons/cloud_details.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/cloud_details.tsx @@ -9,7 +9,7 @@ import { EuiBadge, EuiDescriptionList } from '@elastic/eui'; import { EuiDescriptionListProps } from '@elastic/eui/src/components/description_list/description_list'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { APIReturnType } from '../../../../services/rest/createCallApmApi'; +import { APIReturnType } from '../../../services/rest/createCallApmApi'; type ServiceDetailsReturnType = APIReturnType<'GET /api/apm/services/{serviceName}/metadata/details'>; diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_icons/container_details.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/container_details.tsx similarity index 94% rename from x-pack/plugins/apm/public/components/app/service_details/service_icons/container_details.tsx rename to x-pack/plugins/apm/public/components/shared/service_icons/container_details.tsx index efc9a46526cf876..b590a67409d9ee6 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_icons/container_details.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/container_details.tsx @@ -9,8 +9,8 @@ import { EuiDescriptionList } from '@elastic/eui'; import { EuiDescriptionListProps } from '@elastic/eui/src/components/description_list/description_list'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { asInteger } from '../../../../../common/utils/formatters'; -import { APIReturnType } from '../../../../services/rest/createCallApmApi'; +import { asInteger } from '../../../../common/utils/formatters'; +import { APIReturnType } from '../../../services/rest/createCallApmApi'; type ServiceDetailsReturnType = APIReturnType<'GET /api/apm/services/{serviceName}/metadata/details'>; diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_icons/icon_popover.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/icon_popover.tsx similarity index 93% rename from x-pack/plugins/apm/public/components/app/service_details/service_icons/icon_popover.tsx rename to x-pack/plugins/apm/public/components/shared/service_icons/icon_popover.tsx index 79f93ea76ee51f8..05305558564f137 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_icons/icon_popover.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/icon_popover.tsx @@ -13,8 +13,8 @@ import { EuiPopoverTitle, } from '@elastic/eui'; import React from 'react'; -import { FETCH_STATUS } from '../../../../hooks/use_fetcher'; -import { px } from '../../../../style/variables'; +import { FETCH_STATUS } from '../../../hooks/use_fetcher'; +import { px } from '../../../style/variables'; interface IconPopoverProps { title: string; diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.test.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/index.test.tsx similarity index 95% rename from x-pack/plugins/apm/public/components/app/service_details/service_icons/index.test.tsx rename to x-pack/plugins/apm/public/components/shared/service_icons/index.test.tsx index 6027e8b1d07c59c..d66625f613cdcc2 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.test.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/index.test.tsx @@ -11,14 +11,14 @@ import { merge } from 'lodash'; // import { renderWithTheme } from '../../../../utils/testHelpers'; import React, { ReactNode } from 'react'; import { createKibanaReactContext } from 'src/plugins/kibana_react/public'; -import { MockUrlParamsContextProvider } from '../../../../context/url_params_context/mock_url_params_context_provider'; -import { ApmPluginContextValue } from '../../../../context/apm_plugin/apm_plugin_context'; +import { MockUrlParamsContextProvider } from '../../../context/url_params_context/mock_url_params_context_provider'; +import { ApmPluginContextValue } from '../../../context/apm_plugin/apm_plugin_context'; import { mockApmPluginContextValue, MockApmPluginContextWrapper, -} from '../../../../context/apm_plugin/mock_apm_plugin_context'; -import * as fetcherHook from '../../../../hooks/use_fetcher'; -import { ServiceIcons } from './'; +} from '../../../context/apm_plugin/mock_apm_plugin_context'; +import * as fetcherHook from '../../../hooks/use_fetcher'; +import { ServiceIcons } from '.'; import { EuiThemeProvider } from 'src/plugins/kibana_react/common'; const KibanaReactContext = createKibanaReactContext({ diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/index.tsx similarity index 91% rename from x-pack/plugins/apm/public/components/app/service_details/service_icons/index.tsx rename to x-pack/plugins/apm/public/components/shared/service_icons/index.tsx index f7bed4e09a696da..d64605da2bc3f9b 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_icons/index.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/index.tsx @@ -8,12 +8,12 @@ import { EuiFlexGroup, EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import React, { ReactChild, useState } from 'react'; -import { useApmServiceContext } from '../../../../context/apm_service/use_apm_service_context'; -import { useTheme } from '../../../../hooks/use_theme'; -import { ContainerType } from '../../../../../common/service_metadata'; -import { useUrlParams } from '../../../../context/url_params_context/use_url_params'; -import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher'; -import { getAgentIcon } from '../../../shared/AgentIcon/get_agent_icon'; +import { useApmServiceContext } from '../../../context/apm_service/use_apm_service_context'; +import { useTheme } from '../../../hooks/use_theme'; +import { ContainerType } from '../../../../common/service_metadata'; +import { useUrlParams } from '../../../context/url_params_context/use_url_params'; +import { FETCH_STATUS, useFetcher } from '../../../hooks/use_fetcher'; +import { getAgentIcon } from '../AgentIcon/get_agent_icon'; import { CloudDetails } from './cloud_details'; import { ContainerDetails } from './container_details'; import { IconPopover } from './icon_popover'; diff --git a/x-pack/plugins/apm/public/components/app/service_details/service_icons/service_details.tsx b/x-pack/plugins/apm/public/components/shared/service_icons/service_details.tsx similarity index 96% rename from x-pack/plugins/apm/public/components/app/service_details/service_icons/service_details.tsx rename to x-pack/plugins/apm/public/components/shared/service_icons/service_details.tsx index ed503a5cb34a033..1828465fff450ae 100644 --- a/x-pack/plugins/apm/public/components/app/service_details/service_icons/service_details.tsx +++ b/x-pack/plugins/apm/public/components/shared/service_icons/service_details.tsx @@ -9,7 +9,7 @@ import { EuiDescriptionList } from '@elastic/eui'; import { EuiDescriptionListProps } from '@elastic/eui/src/components/description_list/description_list'; import { i18n } from '@kbn/i18n'; import React from 'react'; -import { APIReturnType } from '../../../../services/rest/createCallApmApi'; +import { APIReturnType } from '../../../services/rest/createCallApmApi'; type ServiceDetailsReturnType = APIReturnType<'GET /api/apm/services/{serviceName}/metadata/details'>;