mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Unified Observability] Clean overview page feature toggle (#130650)
* Move old overview page to index * remove overview page feature toggle * Remove code for old alerts section * Fix translations * remove feature toggle from tests Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
5c485d405d
commit
b0fff69b2a
15 changed files with 287 additions and 731 deletions
|
@ -182,7 +182,6 @@ export default function ({ getService }: PluginFunctionalProviderContext) {
|
|||
'xpack.maps.showMapsInspectorAdapter (boolean)',
|
||||
'xpack.observability.unsafe.alertingExperience.enabled (boolean)',
|
||||
'xpack.observability.unsafe.cases.enabled (boolean)',
|
||||
'xpack.observability.unsafe.overviewNext.enabled (boolean)',
|
||||
'xpack.observability.unsafe.rules.enabled (boolean)',
|
||||
'xpack.osquery.actionEnabled (boolean)',
|
||||
'xpack.osquery.packs (boolean)',
|
||||
|
|
|
@ -63,7 +63,6 @@ describe('renderApp', () => {
|
|||
unsafe: {
|
||||
alertingExperience: { enabled: true },
|
||||
cases: { enabled: true },
|
||||
overviewNext: { enabled: false },
|
||||
rules: { enabled: true },
|
||||
},
|
||||
};
|
||||
|
|
|
@ -1,195 +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 {
|
||||
EuiBadge,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiLink,
|
||||
EuiText,
|
||||
EuiSpacer,
|
||||
EuiTitle,
|
||||
EuiButtonEmpty,
|
||||
EuiLoadingSpinner,
|
||||
EuiCallOut,
|
||||
EuiPanel,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import moment from 'moment';
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { EuiSelect } from '@elastic/eui';
|
||||
import { uniqBy } from 'lodash';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { usePluginContext } from '../../../../hooks/use_plugin_context';
|
||||
import { FETCH_STATUS, useFetcher } from '../../../../hooks/use_fetcher';
|
||||
import { getObservabilityAlerts } from '../../../../services/get_observability_alerts';
|
||||
import { paths } from '../../../../config';
|
||||
import { ObservabilityAppServices } from '../../../../application/types';
|
||||
|
||||
const ALL_TYPES = 'ALL_TYPES';
|
||||
const allTypes = {
|
||||
value: ALL_TYPES,
|
||||
text: i18n.translate('xpack.observability.overview.alert.allTypes', {
|
||||
defaultMessage: 'All types',
|
||||
}),
|
||||
};
|
||||
|
||||
export function AlertsSection() {
|
||||
const { config } = usePluginContext();
|
||||
const { http } = useKibana<ObservabilityAppServices>().services;
|
||||
const [filter, setFilter] = useState(ALL_TYPES);
|
||||
const manageLink = config.unsafe.alertingExperience.enabled
|
||||
? http.basePath.prepend(paths.observability.alerts)
|
||||
: http.basePath.prepend(paths.management.rules);
|
||||
|
||||
const { data, status } = useFetcher(() => getObservabilityAlerts({ http }), [http]);
|
||||
|
||||
const alerts = useMemo(() => data ?? [], [data]);
|
||||
|
||||
const filterOptions = useMemo(() => {
|
||||
if (!alerts) {
|
||||
return [];
|
||||
}
|
||||
return uniqBy(alerts, (alert) => alert.consumer).map(({ consumer }) => ({
|
||||
value: consumer,
|
||||
text: consumer,
|
||||
}));
|
||||
}, [alerts]);
|
||||
|
||||
const isError = status === FETCH_STATUS.FAILURE;
|
||||
const isLoading = status !== FETCH_STATUS.SUCCESS && !isError;
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<EuiFlexGroup alignItems="center" justifyContent="center" responsive={false}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="l" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
if (isError) {
|
||||
return (
|
||||
<EuiFlexGroup alignItems="center" justifyContent="spaceBetween" responsive={false}>
|
||||
<EuiFlexItem>
|
||||
<EuiCallOut
|
||||
title={i18n.translate('xpack.observability.overview.alert.errorTitle', {
|
||||
defaultMessage: "We couldn't load the alert data",
|
||||
})}
|
||||
color="danger"
|
||||
iconType="alert"
|
||||
>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.observability.overview.alert.errorMessage"
|
||||
defaultMessage="There was an error loading the alert data. Try again later."
|
||||
/>
|
||||
</p>
|
||||
</EuiCallOut>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
justifyContent="spaceBetween"
|
||||
responsive={false}
|
||||
gutterSize="s"
|
||||
>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiTitle size="xs">
|
||||
<h4>
|
||||
{i18n.translate('xpack.observability.overview.alerts.title', {
|
||||
defaultMessage: 'Alerts',
|
||||
})}
|
||||
</h4>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButtonEmpty iconType="sortRight" color="text" size="xs" href={manageLink}>
|
||||
{i18n.translate('xpack.observability.overview.alert.appLink', {
|
||||
defaultMessage: 'Show all alerts',
|
||||
})}
|
||||
</EuiButtonEmpty>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiSelect
|
||||
compressed
|
||||
id="filterAlerts"
|
||||
options={[allTypes, ...filterOptions]}
|
||||
value={filter}
|
||||
onChange={(e) => setFilter(e.target.value)}
|
||||
prepend="Show"
|
||||
/>
|
||||
<EuiSpacer size="s" />
|
||||
</EuiFlexItem>
|
||||
{alerts
|
||||
.filter((alert) => filter === ALL_TYPES || alert.consumer === filter)
|
||||
.map((alert, index) => {
|
||||
return (
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
<EuiPanel>
|
||||
<EuiFlexGroup direction="column" gutterSize="s" key={alert.id}>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLink
|
||||
href={http.basePath.prepend(paths.management.alertDetails(alert.id))}
|
||||
>
|
||||
<EuiText size="s">{alert.name}</EuiText>
|
||||
</EuiLink>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="xs">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge color="hollow">{alert.alertTypeId}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
{alert.tags.map((tag, idx) => {
|
||||
return (
|
||||
<EuiFlexItem key={idx} grow={false}>
|
||||
<EuiBadge color="default">{tag}</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
);
|
||||
})}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFlexGroup gutterSize="s" alignItems="center">
|
||||
{alert.muteAll && (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiBadge color="hollow">
|
||||
{i18n.translate('xpack.observability.overview.alerts.muted', {
|
||||
defaultMessage: 'Muted',
|
||||
})}
|
||||
</EuiBadge>
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiText color="subdued" size="xs">
|
||||
Last updated{' '}
|
||||
{moment.duration(moment().diff(alert.updatedAt)).humanize()} ago
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiPanel>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -48,7 +48,6 @@ describe('APMSection', () => {
|
|||
unsafe: {
|
||||
alertingExperience: { enabled: true },
|
||||
cases: { enabled: true },
|
||||
overviewNext: { enabled: false },
|
||||
rules: { enabled: true },
|
||||
},
|
||||
},
|
||||
|
|
|
@ -36,7 +36,6 @@ export interface ConfigSchema {
|
|||
alertingExperience: { enabled: boolean };
|
||||
rules: { enabled: boolean };
|
||||
cases: { enabled: boolean };
|
||||
overviewNext: { enabled: boolean };
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,87 +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 { shallow } from 'enzyme';
|
||||
import * as PluginContext from '../../hooks/use_plugin_context';
|
||||
import { PluginContextValue } from '../../context/plugin_context';
|
||||
import { OverviewPage } from '.';
|
||||
import { OverviewPage as OldOverviewPage } from './old_overview_page';
|
||||
import { OverviewPage as NewOverviewPage } from './overview_page';
|
||||
|
||||
describe('Overview page', () => {
|
||||
it('should render the old overview page when feature flag is disabled and queryParams are empty', () => {
|
||||
const pluginContext = {
|
||||
config: {
|
||||
unsafe: {
|
||||
overviewNext: { enabled: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(PluginContext, 'usePluginContext')
|
||||
.mockReturnValue(pluginContext as PluginContextValue);
|
||||
|
||||
const component = shallow(<OverviewPage routeParams={{ query: {} }} />);
|
||||
expect(component.find(OldOverviewPage)).toHaveLength(1);
|
||||
expect(component.find(NewOverviewPage)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should render the new overview page when feature flag is enabled and queryParams are empty', () => {
|
||||
const pluginContext = {
|
||||
config: {
|
||||
unsafe: {
|
||||
overviewNext: { enabled: true },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(PluginContext, 'usePluginContext')
|
||||
.mockReturnValue(pluginContext as PluginContextValue);
|
||||
|
||||
const component = shallow(<OverviewPage routeParams={{ query: {} }} />);
|
||||
expect(component.find(OldOverviewPage)).toHaveLength(0);
|
||||
expect(component.find(NewOverviewPage)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should render the new overview page when feature flag is enabled and alpha param is in the url', () => {
|
||||
const pluginContext = {
|
||||
config: {
|
||||
unsafe: {
|
||||
overviewNext: { enabled: true },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(PluginContext, 'usePluginContext')
|
||||
.mockReturnValue(pluginContext as PluginContextValue);
|
||||
|
||||
const component = shallow(<OverviewPage routeParams={{ query: { alpha: true } }} />);
|
||||
expect(component.find(OldOverviewPage)).toHaveLength(0);
|
||||
expect(component.find(NewOverviewPage)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should render the new overview page when feature flag is disabled and alpha param is in the url', () => {
|
||||
const pluginContext = {
|
||||
config: {
|
||||
unsafe: {
|
||||
overviewNext: { enabled: false },
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
jest
|
||||
.spyOn(PluginContext, 'usePluginContext')
|
||||
.mockReturnValue(pluginContext as PluginContextValue);
|
||||
|
||||
const component = shallow(<OverviewPage routeParams={{ query: { alpha: true } }} />);
|
||||
expect(component.find(OldOverviewPage)).toHaveLength(0);
|
||||
expect(component.find(NewOverviewPage)).toHaveLength(1);
|
||||
});
|
||||
});
|
|
@ -4,25 +4,300 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { RouteParams } from '../../routes';
|
||||
import {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiSpacer,
|
||||
EuiHorizontalRule,
|
||||
EuiButton,
|
||||
EuiFlyout,
|
||||
EuiFlyoutHeader,
|
||||
EuiTitle,
|
||||
EuiFlyoutBody,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React, { useMemo, useRef, useCallback, useState, useEffect } from 'react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import { observabilityFeatureId } from '../../../common';
|
||||
import { useTrackPageview, useUiTracker } from '../..';
|
||||
import { EmptySections } from '../../components/app/empty_sections';
|
||||
import { ObservabilityHeaderMenu } from '../../components/app/header';
|
||||
import { NewsFeed } from '../../components/app/news_feed';
|
||||
import { Resources } from '../../components/app/resources';
|
||||
import { DatePicker } from '../../components/shared/date_picker';
|
||||
import { useBreadcrumbs } from '../../hooks/use_breadcrumbs';
|
||||
import { useFetcher } from '../../hooks/use_fetcher';
|
||||
import { useHasData } from '../../hooks/use_has_data';
|
||||
import { usePluginContext } from '../../hooks/use_plugin_context';
|
||||
import { OverviewPage as OldOverviewPage } from './old_overview_page';
|
||||
import { OverviewPage as NewOverviewPage } from './overview_page';
|
||||
import { useAlertIndexNames } from '../../hooks/use_alert_index_names';
|
||||
import { RouteParams } from '../../routes';
|
||||
import { getNewsFeed } from '../../services/get_news_feed';
|
||||
import { getBucketSize } from '../../utils/get_bucket_size';
|
||||
import { getNoDataConfig } from '../../utils/no_data_config';
|
||||
import { DataSections } from './data_sections';
|
||||
import { LoadingObservability } from './loading_observability';
|
||||
import { AlertsTableTGrid } from '../alerts/containers/alerts_table_t_grid/alerts_table_t_grid';
|
||||
import { SectionContainer } from '../../components/app/section';
|
||||
import { ObservabilityAppServices } from '../../application/types';
|
||||
import { useGetUserCasesPermissions } from '../../hooks/use_get_user_cases_permissions';
|
||||
import { paths } from '../../config';
|
||||
import { useDatePickerContext } from '../../hooks/use_date_picker_context';
|
||||
import { ObservabilityStatusProgress } from '../../components/app/observability_status/observability_status_progress';
|
||||
import { ObservabilityStatus } from '../../components/app/observability_status';
|
||||
import { useGuidedSetupProgress } from '../../hooks/use_guided_setup_progress';
|
||||
|
||||
export type { BucketSize } from './old_overview_page';
|
||||
export type BucketSize = ReturnType<typeof calculateBucketSize>;
|
||||
|
||||
interface Props {
|
||||
routeParams: RouteParams<'/overview'>;
|
||||
}
|
||||
|
||||
export function OverviewPage(props: Props) {
|
||||
const { config } = usePluginContext();
|
||||
const alpha = props.routeParams.query.alpha;
|
||||
const CAPABILITIES_KEYS = ['logs', 'infrastructure', 'apm', 'uptime'];
|
||||
|
||||
if (config.unsafe.overviewNext.enabled || alpha) {
|
||||
return <NewOverviewPage {...props} />;
|
||||
} else {
|
||||
return <OldOverviewPage {...props} />;
|
||||
function calculateBucketSize({ start, end }: { start?: number; end?: number }) {
|
||||
if (start && end) {
|
||||
return getBucketSize({ start, end, minInterval: '60s' });
|
||||
}
|
||||
}
|
||||
|
||||
const ALERT_TABLE_STATE_STORAGE_KEY = 'xpack.observability.overview.alert.tableState';
|
||||
|
||||
export function OverviewPage({ routeParams }: Props) {
|
||||
const trackMetric = useUiTracker({ app: 'observability-overview' });
|
||||
useTrackPageview({ app: 'observability-overview', path: 'overview' });
|
||||
useTrackPageview({ app: 'observability-overview', path: 'overview', delay: 15000 });
|
||||
useBreadcrumbs([
|
||||
{
|
||||
text: i18n.translate('xpack.observability.breadcrumbs.overviewLinkText', {
|
||||
defaultMessage: 'Overview',
|
||||
}),
|
||||
},
|
||||
]);
|
||||
const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
|
||||
|
||||
const indexNames = useAlertIndexNames();
|
||||
const {
|
||||
cases,
|
||||
docLinks,
|
||||
http,
|
||||
application: { capabilities },
|
||||
} = useKibana<ObservabilityAppServices>().services;
|
||||
|
||||
const { ObservabilityPageTemplate, config } = usePluginContext();
|
||||
const { relativeStart, relativeEnd, absoluteStart, absoluteEnd } = useDatePickerContext();
|
||||
|
||||
const { data: newsFeed } = useFetcher(() => getNewsFeed({ http }), [http]);
|
||||
|
||||
const { hasAnyData, isAllRequestsComplete } = useHasData();
|
||||
const refetch = useRef<() => void>();
|
||||
|
||||
const { isGuidedSetupProgressDismissed } = useGuidedSetupProgress();
|
||||
|
||||
const bucketSize = useMemo(
|
||||
() =>
|
||||
calculateBucketSize({
|
||||
start: absoluteStart,
|
||||
end: absoluteEnd,
|
||||
}),
|
||||
[absoluteStart, absoluteEnd]
|
||||
);
|
||||
|
||||
const setRefetch = useCallback((ref) => {
|
||||
refetch.current = ref;
|
||||
}, []);
|
||||
|
||||
const handleGuidedSetupClick = useCallback(() => {
|
||||
if (isGuidedSetupProgressDismissed) {
|
||||
trackMetric({ metric: 'guided_setup_view_details_after_dismiss' });
|
||||
}
|
||||
|
||||
setIsFlyoutVisible(true);
|
||||
}, [trackMetric, isGuidedSetupProgressDismissed]);
|
||||
|
||||
const onTimeRangeRefresh = useCallback(() => {
|
||||
return refetch.current && refetch.current();
|
||||
}, []);
|
||||
|
||||
const CasesContext = cases.ui.getCasesContext();
|
||||
const userPermissions = useGetUserCasesPermissions();
|
||||
|
||||
useEffect(() => {
|
||||
if (hasAnyData !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
CAPABILITIES_KEYS.forEach((feature) => {
|
||||
if (capabilities[feature].show === false) {
|
||||
trackMetric({
|
||||
metric: `oblt_disabled_feature_${feature === 'infrastructure' ? 'metrics' : feature}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [capabilities, hasAnyData, trackMetric]);
|
||||
|
||||
if (hasAnyData === undefined) {
|
||||
return <LoadingObservability />;
|
||||
}
|
||||
|
||||
const hasData = hasAnyData === true || (isAllRequestsComplete === false ? undefined : false);
|
||||
|
||||
const noDataConfig = getNoDataConfig({
|
||||
hasData,
|
||||
basePath: http.basePath,
|
||||
docsLink: docLinks.links.observability.guide,
|
||||
});
|
||||
|
||||
const alertsLink = config.unsafe.alertingExperience.enabled
|
||||
? paths.observability.alerts
|
||||
: paths.management.rules;
|
||||
|
||||
return (
|
||||
<ObservabilityPageTemplate
|
||||
noDataConfig={noDataConfig}
|
||||
pageHeader={
|
||||
hasData
|
||||
? {
|
||||
children: (
|
||||
<PageHeader
|
||||
handleGuidedSetupClick={handleGuidedSetupClick}
|
||||
onTimeRangeRefresh={onTimeRangeRefresh}
|
||||
/>
|
||||
),
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{hasData && (
|
||||
<>
|
||||
<ObservabilityHeaderMenu />
|
||||
<ObservabilityStatusProgress onViewDetailsClick={() => setIsFlyoutVisible(true)} />
|
||||
<EuiFlexGroup direction="column" gutterSize="s">
|
||||
<EuiFlexItem>
|
||||
<SectionContainer
|
||||
title={i18n.translate('xpack.observability.overview.alerts.title', {
|
||||
defaultMessage: 'Alerts',
|
||||
})}
|
||||
hasError={false}
|
||||
appLink={{
|
||||
href: alertsLink,
|
||||
label: i18n.translate('xpack.observability.overview.alerts.appLink', {
|
||||
defaultMessage: 'Show alerts',
|
||||
}),
|
||||
}}
|
||||
showExperimentalBadge={true}
|
||||
>
|
||||
<CasesContext
|
||||
owner={[observabilityFeatureId]}
|
||||
userCanCrud={userPermissions?.crud ?? false}
|
||||
features={{ alerts: { sync: false } }}
|
||||
>
|
||||
<AlertsTableTGrid
|
||||
setRefetch={setRefetch}
|
||||
rangeFrom={relativeStart}
|
||||
rangeTo={relativeEnd}
|
||||
indexNames={indexNames}
|
||||
stateStorageKey={ALERT_TABLE_STATE_STORAGE_KEY}
|
||||
storage={new Storage(window.localStorage)}
|
||||
/>
|
||||
</CasesContext>
|
||||
</SectionContainer>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{/* Data sections */}
|
||||
{hasAnyData && <DataSections bucketSize={bucketSize} />}
|
||||
<EmptySections />
|
||||
</EuiFlexItem>
|
||||
<EuiSpacer size="s" />
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
{/* Resources / What's New sections */}
|
||||
<EuiFlexGroup direction="row">
|
||||
<EuiFlexItem grow={4}>
|
||||
{!!newsFeed?.items?.length && <NewsFeed items={newsFeed.items.slice(0, 3)} />}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={2}>
|
||||
<Resources />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
)}
|
||||
{isFlyoutVisible && (
|
||||
<EuiFlyout
|
||||
size="s"
|
||||
ownFocus
|
||||
onClose={() => setIsFlyoutVisible(false)}
|
||||
aria-labelledby="statusVisualizationFlyoutTitle"
|
||||
>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="m">
|
||||
<h2 id="statusVisualizationFlyoutTitle">
|
||||
<FormattedMessage
|
||||
id="xpack.observability.overview.statusVisualizationFlyoutTitle"
|
||||
defaultMessage="Guided setup"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText size="s">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.observability.overview.statusVisualizationFlyoutDescription"
|
||||
defaultMessage="Track your progress towards adding observability integrations and features."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<ObservabilityStatus />
|
||||
</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
)}
|
||||
</ObservabilityPageTemplate>
|
||||
);
|
||||
}
|
||||
|
||||
interface PageHeaderProps {
|
||||
handleGuidedSetupClick: () => void;
|
||||
onTimeRangeRefresh: () => void;
|
||||
}
|
||||
|
||||
function PageHeader({ handleGuidedSetupClick, onTimeRangeRefresh }: PageHeaderProps) {
|
||||
const { relativeStart, relativeEnd, refreshInterval, refreshPaused } = useDatePickerContext();
|
||||
return (
|
||||
<EuiFlexGroup wrap gutterSize="s" justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={1}>
|
||||
<EuiTitle>
|
||||
<h1 className="eui-textNoWrap">{overviewPageTitle}</h1>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<DatePicker
|
||||
rangeFrom={relativeStart}
|
||||
rangeTo={relativeEnd}
|
||||
refreshInterval={refreshInterval}
|
||||
refreshPaused={refreshPaused}
|
||||
onTimeRangeRefresh={onTimeRangeRefresh}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false} style={{ alignItems: 'flex-end' }}>
|
||||
<EuiButton color="text" iconType="wrench" onClick={handleGuidedSetupClick}>
|
||||
<FormattedMessage
|
||||
id="xpack.observability.overview.guidedSetupButton"
|
||||
defaultMessage="Guided setup"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
const overviewPageTitle = i18n.translate('xpack.observability.overview.pageTitle', {
|
||||
defaultMessage: 'Overview',
|
||||
});
|
||||
|
|
|
@ -1,302 +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 {
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiSpacer,
|
||||
EuiHorizontalRule,
|
||||
EuiButton,
|
||||
EuiFlyout,
|
||||
EuiFlyoutHeader,
|
||||
EuiTitle,
|
||||
EuiFlyoutBody,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import React, { useMemo, useRef, useCallback, useState, useEffect } from 'react';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { Storage } from '@kbn/kibana-utils-plugin/public';
|
||||
import { observabilityFeatureId } from '../../../common';
|
||||
import { useTrackPageview, useUiTracker } from '../..';
|
||||
import { EmptySections } from '../../components/app/empty_sections';
|
||||
import { ObservabilityHeaderMenu } from '../../components/app/header';
|
||||
import { NewsFeed } from '../../components/app/news_feed';
|
||||
import { Resources } from '../../components/app/resources';
|
||||
import { DatePicker } from '../../components/shared/date_picker';
|
||||
import { useBreadcrumbs } from '../../hooks/use_breadcrumbs';
|
||||
import { useFetcher } from '../../hooks/use_fetcher';
|
||||
import { useHasData } from '../../hooks/use_has_data';
|
||||
import { usePluginContext } from '../../hooks/use_plugin_context';
|
||||
import { useAlertIndexNames } from '../../hooks/use_alert_index_names';
|
||||
import { RouteParams } from '../../routes';
|
||||
import { getNewsFeed } from '../../services/get_news_feed';
|
||||
import { getBucketSize } from '../../utils/get_bucket_size';
|
||||
import { getNoDataConfig } from '../../utils/no_data_config';
|
||||
import { DataSections } from './data_sections';
|
||||
import { LoadingObservability } from './loading_observability';
|
||||
import { AlertsTableTGrid } from '../alerts/containers/alerts_table_t_grid/alerts_table_t_grid';
|
||||
import { SectionContainer } from '../../components/app/section';
|
||||
import { ObservabilityAppServices } from '../../application/types';
|
||||
import { useGetUserCasesPermissions } from '../../hooks/use_get_user_cases_permissions';
|
||||
import { paths } from '../../config';
|
||||
import { useDatePickerContext } from '../../hooks/use_date_picker_context';
|
||||
import { ObservabilityStatusProgress } from '../../components/app/observability_status/observability_status_progress';
|
||||
import { ObservabilityStatus } from '../../components/app/observability_status';
|
||||
import { useGuidedSetupProgress } from '../../hooks/use_guided_setup_progress';
|
||||
interface Props {
|
||||
routeParams: RouteParams<'/overview'>;
|
||||
}
|
||||
export type BucketSize = ReturnType<typeof calculateBucketSize>;
|
||||
|
||||
const CAPABILITIES_KEYS = ['logs', 'infrastructure', 'apm', 'uptime'];
|
||||
|
||||
function calculateBucketSize({ start, end }: { start?: number; end?: number }) {
|
||||
if (start && end) {
|
||||
return getBucketSize({ start, end, minInterval: '60s' });
|
||||
}
|
||||
}
|
||||
|
||||
const ALERT_TABLE_STATE_STORAGE_KEY = 'xpack.observability.overview.alert.tableState';
|
||||
|
||||
export function OverviewPage({ routeParams }: Props) {
|
||||
const trackMetric = useUiTracker({ app: 'observability-overview' });
|
||||
useTrackPageview({ app: 'observability-overview', path: 'overview' });
|
||||
useTrackPageview({ app: 'observability-overview', path: 'overview', delay: 15000 });
|
||||
useBreadcrumbs([
|
||||
{
|
||||
text: i18n.translate('xpack.observability.breadcrumbs.overviewLinkText', {
|
||||
defaultMessage: 'Overview',
|
||||
}),
|
||||
},
|
||||
]);
|
||||
const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
|
||||
|
||||
const indexNames = useAlertIndexNames();
|
||||
const {
|
||||
cases,
|
||||
docLinks,
|
||||
http,
|
||||
application: { capabilities },
|
||||
} = useKibana<ObservabilityAppServices>().services;
|
||||
|
||||
const { ObservabilityPageTemplate, config } = usePluginContext();
|
||||
const { relativeStart, relativeEnd, absoluteStart, absoluteEnd } = useDatePickerContext();
|
||||
|
||||
const { data: newsFeed } = useFetcher(() => getNewsFeed({ http }), [http]);
|
||||
|
||||
const { hasAnyData, isAllRequestsComplete } = useHasData();
|
||||
const refetch = useRef<() => void>();
|
||||
|
||||
const { isGuidedSetupProgressDismissed } = useGuidedSetupProgress();
|
||||
|
||||
const bucketSize = useMemo(
|
||||
() =>
|
||||
calculateBucketSize({
|
||||
start: absoluteStart,
|
||||
end: absoluteEnd,
|
||||
}),
|
||||
[absoluteStart, absoluteEnd]
|
||||
);
|
||||
|
||||
const setRefetch = useCallback((ref) => {
|
||||
refetch.current = ref;
|
||||
}, []);
|
||||
|
||||
const handleGuidedSetupClick = useCallback(() => {
|
||||
if (isGuidedSetupProgressDismissed) {
|
||||
trackMetric({ metric: 'guided_setup_view_details_after_dismiss' });
|
||||
}
|
||||
|
||||
setIsFlyoutVisible(true);
|
||||
}, [trackMetric, isGuidedSetupProgressDismissed]);
|
||||
|
||||
const onTimeRangeRefresh = useCallback(() => {
|
||||
return refetch.current && refetch.current();
|
||||
}, []);
|
||||
|
||||
const CasesContext = cases.ui.getCasesContext();
|
||||
const userPermissions = useGetUserCasesPermissions();
|
||||
|
||||
useEffect(() => {
|
||||
if (hasAnyData !== true) {
|
||||
return;
|
||||
}
|
||||
|
||||
CAPABILITIES_KEYS.forEach((feature) => {
|
||||
if (capabilities[feature].show === false) {
|
||||
trackMetric({
|
||||
metric: `oblt_disabled_feature_${feature === 'infrastructure' ? 'metrics' : feature}`,
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [capabilities, hasAnyData, trackMetric]);
|
||||
|
||||
if (hasAnyData === undefined) {
|
||||
return <LoadingObservability />;
|
||||
}
|
||||
|
||||
const hasData = hasAnyData === true || (isAllRequestsComplete === false ? undefined : false);
|
||||
|
||||
const noDataConfig = getNoDataConfig({
|
||||
hasData,
|
||||
basePath: http.basePath,
|
||||
docsLink: docLinks.links.observability.guide,
|
||||
});
|
||||
|
||||
const alertsLink = config.unsafe.alertingExperience.enabled
|
||||
? paths.observability.alerts
|
||||
: paths.management.rules;
|
||||
|
||||
return (
|
||||
<ObservabilityPageTemplate
|
||||
noDataConfig={noDataConfig}
|
||||
pageHeader={
|
||||
hasData
|
||||
? {
|
||||
children: (
|
||||
<PageHeader
|
||||
handleGuidedSetupClick={handleGuidedSetupClick}
|
||||
onTimeRangeRefresh={onTimeRangeRefresh}
|
||||
/>
|
||||
),
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{hasData && (
|
||||
<>
|
||||
<ObservabilityHeaderMenu />
|
||||
<ObservabilityStatusProgress onViewDetailsClick={() => setIsFlyoutVisible(true)} />
|
||||
<EuiFlexGroup direction="column" gutterSize="s">
|
||||
<EuiFlexItem>
|
||||
<SectionContainer
|
||||
title={i18n.translate('xpack.observability.overview.alerts.title', {
|
||||
defaultMessage: 'Alerts',
|
||||
})}
|
||||
hasError={false}
|
||||
appLink={{
|
||||
href: alertsLink,
|
||||
label: i18n.translate('xpack.observability.overview.alerts.appLink', {
|
||||
defaultMessage: 'Show alerts',
|
||||
}),
|
||||
}}
|
||||
showExperimentalBadge={true}
|
||||
>
|
||||
<CasesContext
|
||||
owner={[observabilityFeatureId]}
|
||||
userCanCrud={userPermissions?.crud ?? false}
|
||||
features={{ alerts: { sync: false } }}
|
||||
>
|
||||
<AlertsTableTGrid
|
||||
setRefetch={setRefetch}
|
||||
rangeFrom={relativeStart}
|
||||
rangeTo={relativeEnd}
|
||||
indexNames={indexNames}
|
||||
stateStorageKey={ALERT_TABLE_STATE_STORAGE_KEY}
|
||||
storage={new Storage(window.localStorage)}
|
||||
/>
|
||||
</CasesContext>
|
||||
</SectionContainer>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem>
|
||||
{/* Data sections */}
|
||||
{hasAnyData && <DataSections bucketSize={bucketSize} />}
|
||||
<EmptySections />
|
||||
</EuiFlexItem>
|
||||
<EuiSpacer size="s" />
|
||||
</EuiFlexGroup>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGroup>
|
||||
<EuiFlexItem>
|
||||
{/* Resources / What's New sections */}
|
||||
<EuiFlexGroup direction="row">
|
||||
<EuiFlexItem grow={4}>
|
||||
{!!newsFeed?.items?.length && <NewsFeed items={newsFeed.items.slice(0, 3)} />}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={2}>
|
||||
<Resources />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</>
|
||||
)}
|
||||
{isFlyoutVisible && (
|
||||
<EuiFlyout
|
||||
size="s"
|
||||
ownFocus
|
||||
onClose={() => setIsFlyoutVisible(false)}
|
||||
aria-labelledby="statusVisualizationFlyoutTitle"
|
||||
>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="m">
|
||||
<h2 id="statusVisualizationFlyoutTitle">
|
||||
<FormattedMessage
|
||||
id="xpack.observability.overview.statusVisualizationFlyoutTitle"
|
||||
defaultMessage="Guided setup"
|
||||
/>
|
||||
</h2>
|
||||
</EuiTitle>
|
||||
<EuiSpacer size="s" />
|
||||
<EuiText size="s">
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id="xpack.observability.overview.statusVisualizationFlyoutDescription"
|
||||
defaultMessage="Track your progress towards adding observability integrations and features."
|
||||
/>
|
||||
</p>
|
||||
</EuiText>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<ObservabilityStatus />
|
||||
</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
)}
|
||||
</ObservabilityPageTemplate>
|
||||
);
|
||||
}
|
||||
|
||||
interface PageHeaderProps {
|
||||
handleGuidedSetupClick: () => void;
|
||||
onTimeRangeRefresh: () => void;
|
||||
}
|
||||
|
||||
function PageHeader({ handleGuidedSetupClick, onTimeRangeRefresh }: PageHeaderProps) {
|
||||
const { relativeStart, relativeEnd, refreshInterval, refreshPaused } = useDatePickerContext();
|
||||
return (
|
||||
<EuiFlexGroup wrap gutterSize="s" justifyContent="flexEnd">
|
||||
<EuiFlexItem grow={1}>
|
||||
<EuiTitle>
|
||||
<h1 className="eui-textNoWrap">{overviewPageTitle}</h1>
|
||||
</EuiTitle>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<DatePicker
|
||||
rangeFrom={relativeStart}
|
||||
rangeTo={relativeEnd}
|
||||
refreshInterval={refreshInterval}
|
||||
refreshPaused={refreshPaused}
|
||||
onTimeRangeRefresh={onTimeRangeRefresh}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false} style={{ alignItems: 'flex-end' }}>
|
||||
<EuiButton color="text" iconType="wrench" onClick={handleGuidedSetupClick}>
|
||||
<FormattedMessage
|
||||
id="xpack.observability.overview.guidedSetupButton"
|
||||
defaultMessage="Guided setup"
|
||||
/>
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
}
|
||||
|
||||
const overviewPageTitle = i18n.translate('xpack.observability.overview.pageTitle', {
|
||||
defaultMessage: 'Overview',
|
||||
});
|
|
@ -85,7 +85,6 @@ const withCore = makeDecorator({
|
|||
unsafe: {
|
||||
alertingExperience: { enabled: true },
|
||||
cases: { enabled: true },
|
||||
overviewNext: { enabled: false },
|
||||
rules: { enabled: true },
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,110 +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 { i18n } from '@kbn/i18n';
|
||||
import React, { useState } from 'react';
|
||||
import { EuiButton, EuiFlyout, EuiFlyoutBody, EuiFlyoutHeader, EuiTitle } from '@elastic/eui';
|
||||
import { useKibana } from '@kbn/kibana-react-plugin/public';
|
||||
import { useTrackPageview } from '../..';
|
||||
import { DatePicker } from '../../components/shared/date_picker';
|
||||
import { useBreadcrumbs } from '../../hooks/use_breadcrumbs';
|
||||
import { useHasData } from '../../hooks/use_has_data';
|
||||
import { usePluginContext } from '../../hooks/use_plugin_context';
|
||||
import { useDatePickerContext } from '../../hooks/use_date_picker_context';
|
||||
import { RouteParams } from '../../routes';
|
||||
import { getNoDataConfig } from '../../utils/no_data_config';
|
||||
import { LoadingObservability } from './loading_observability';
|
||||
import { ObservabilityStatus } from '../../components/app/observability_status';
|
||||
import { ObservabilityAppServices } from '../../application/types';
|
||||
|
||||
interface Props {
|
||||
routeParams: RouteParams<'/overview'>;
|
||||
}
|
||||
|
||||
export function OverviewPage({ routeParams }: Props) {
|
||||
useTrackPageview({ app: 'observability-overview', path: 'overview' });
|
||||
useTrackPageview({ app: 'observability-overview', path: 'overview', delay: 15000 });
|
||||
useBreadcrumbs([
|
||||
{
|
||||
text: i18n.translate('xpack.observability.breadcrumbs.overviewLinkText', {
|
||||
defaultMessage: 'Overview',
|
||||
}),
|
||||
},
|
||||
]);
|
||||
|
||||
const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
|
||||
|
||||
const { docLinks, http } = useKibana<ObservabilityAppServices>().services;
|
||||
const { ObservabilityPageTemplate } = usePluginContext();
|
||||
|
||||
const { relativeStart, relativeEnd } = useDatePickerContext();
|
||||
|
||||
const relativeTime = { start: relativeStart, end: relativeEnd };
|
||||
|
||||
const { hasAnyData, isAllRequestsComplete } = useHasData();
|
||||
|
||||
if (hasAnyData === undefined) {
|
||||
return <LoadingObservability />;
|
||||
}
|
||||
|
||||
const hasData = hasAnyData === true || (isAllRequestsComplete === false ? undefined : false);
|
||||
|
||||
const noDataConfig = getNoDataConfig({
|
||||
hasData,
|
||||
basePath: http.basePath,
|
||||
docsLink: docLinks.links.observability.guide,
|
||||
});
|
||||
|
||||
const { refreshInterval = 10000, refreshPaused = true } = routeParams.query;
|
||||
|
||||
return (
|
||||
<ObservabilityPageTemplate
|
||||
noDataConfig={noDataConfig}
|
||||
pageHeader={
|
||||
hasData
|
||||
? {
|
||||
pageTitle: overviewPageTitle,
|
||||
rightSideItems: [
|
||||
<DatePicker
|
||||
rangeFrom={relativeTime.start}
|
||||
rangeTo={relativeTime.end}
|
||||
refreshInterval={refreshInterval}
|
||||
refreshPaused={refreshPaused}
|
||||
/>,
|
||||
],
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
{hasData && (
|
||||
<>
|
||||
<EuiButton onClick={() => setIsFlyoutVisible(true)}>Show observability status</EuiButton>
|
||||
{isFlyoutVisible && (
|
||||
<EuiFlyout
|
||||
size="s"
|
||||
ownFocus
|
||||
onClose={() => setIsFlyoutVisible(false)}
|
||||
aria-labelledby="flyout-id"
|
||||
>
|
||||
<EuiFlyoutHeader hasBorder>
|
||||
<EuiTitle size="m">
|
||||
<h2 id="flyout-id">Status</h2>
|
||||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody>
|
||||
<ObservabilityStatus />
|
||||
</EuiFlyoutBody>
|
||||
</EuiFlyout>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</ObservabilityPageTemplate>
|
||||
);
|
||||
}
|
||||
|
||||
const overviewPageTitle = i18n.translate('xpack.observability.overview.pageTitle', {
|
||||
defaultMessage: 'Overview',
|
||||
});
|
|
@ -26,7 +26,6 @@ const config = {
|
|||
unsafe: {
|
||||
alertingExperience: { enabled: true },
|
||||
cases: { enabled: true },
|
||||
overviewNext: { enabled: false },
|
||||
rules: { enabled: true },
|
||||
},
|
||||
};
|
||||
|
|
|
@ -35,7 +35,6 @@ export const config: PluginConfigDescriptor = {
|
|||
alertingExperience: schema.object({ enabled: schema.boolean({ defaultValue: true }) }),
|
||||
rules: schema.object({ enabled: schema.boolean({ defaultValue: true }) }),
|
||||
cases: schema.object({ enabled: schema.boolean({ defaultValue: true }) }),
|
||||
overviewNext: schema.object({ enabled: schema.boolean({ defaultValue: false }) }),
|
||||
}),
|
||||
}),
|
||||
};
|
||||
|
|
|
@ -21658,12 +21658,6 @@
|
|||
"xpack.observability.noDataConfig.beatsCard.title": "Ajouter des intégrations",
|
||||
"xpack.observability.noDataConfig.solutionName": "Observabilité",
|
||||
"xpack.observability.notAvailable": "S. O.",
|
||||
"xpack.observability.overview.alert.allTypes": "Tous les types",
|
||||
"xpack.observability.overview.alert.appLink": "Afficher toutes les alertes",
|
||||
"xpack.observability.overview.alert.errorMessage": "Une erreur s’est produite lors du chargement des données d’alerte. Réessayez plus tard.",
|
||||
"xpack.observability.overview.alert.errorTitle": "Impossible de charger les données d’alerte.",
|
||||
"xpack.observability.overview.alerts.appLink": "Afficher les alertes",
|
||||
"xpack.observability.overview.alerts.muted": "Muet",
|
||||
"xpack.observability.overview.alerts.title": "Alertes",
|
||||
"xpack.observability.overview.apm.appLink": "Afficher l’inventaire des services",
|
||||
"xpack.observability.overview.apm.services": "Services",
|
||||
|
|
|
@ -21815,12 +21815,6 @@
|
|||
"xpack.observability.noDataConfig.beatsCard.title": "統合の追加",
|
||||
"xpack.observability.noDataConfig.solutionName": "Observability",
|
||||
"xpack.observability.notAvailable": "N/A",
|
||||
"xpack.observability.overview.alert.allTypes": "すべてのタイプ",
|
||||
"xpack.observability.overview.alert.appLink": "すべてのアラートを表示",
|
||||
"xpack.observability.overview.alert.errorMessage": "アラートデータの読み込みエラーが発生しました。しばらくたってから再試行してください。",
|
||||
"xpack.observability.overview.alert.errorTitle": "アラートデータを読み込めませんでした",
|
||||
"xpack.observability.overview.alerts.appLink": "アラートを表示",
|
||||
"xpack.observability.overview.alerts.muted": "ミュート",
|
||||
"xpack.observability.overview.alerts.title": "アラート",
|
||||
"xpack.observability.overview.apm.appLink": "サービスインベントリを表示",
|
||||
"xpack.observability.overview.apm.services": "サービス",
|
||||
|
|
|
@ -21847,12 +21847,6 @@
|
|||
"xpack.observability.noDataConfig.beatsCard.title": "添加集成",
|
||||
"xpack.observability.noDataConfig.solutionName": "Observability",
|
||||
"xpack.observability.notAvailable": "不可用",
|
||||
"xpack.observability.overview.alert.allTypes": "所有类型",
|
||||
"xpack.observability.overview.alert.appLink": "显示所有告警",
|
||||
"xpack.observability.overview.alert.errorMessage": "加载告警数据时出现错误。请稍后重试。",
|
||||
"xpack.observability.overview.alert.errorTitle": "我们无法加载告警数据",
|
||||
"xpack.observability.overview.alerts.appLink": "显示告警",
|
||||
"xpack.observability.overview.alerts.muted": "已静音",
|
||||
"xpack.observability.overview.alerts.title": "告警",
|
||||
"xpack.observability.overview.apm.appLink": "显示服务库存",
|
||||
"xpack.observability.overview.apm.services": "服务",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue