diff --git a/x-pack/plugins/ml/common/types/storage.ts b/x-pack/plugins/ml/common/types/storage.ts index cb80b17bda583..a74bbea0e3aff 100644 --- a/x-pack/plugins/ml/common/types/storage.ts +++ b/x-pack/plugins/ml/common/types/storage.ts @@ -14,6 +14,7 @@ export const ML_GETTING_STARTED_CALLOUT_DISMISSED = 'ml.gettingStarted.isDismiss export const ML_FROZEN_TIER_PREFERENCE = 'ml.frozenDataTierPreference'; export const ML_ANOMALY_EXPLORER_PANELS = 'ml.anomalyExplorerPanels'; export const ML_NOTIFICATIONS_LAST_CHECKED_AT = 'ml.notificationsLastCheckedAt'; +export const ML_OVERVIEW_PANELS = 'ml.overviewPanels'; export type PartitionFieldConfig = | { @@ -52,6 +53,12 @@ export interface AnomalyExplorerPanelsState { mainPage: { size: number }; } +export interface OverviewPanelsState { + nodes: boolean; + adJobs: boolean; + dfaJobs: boolean; +} + export interface MlStorageRecord { [key: string]: unknown; [ML_ENTITY_FIELDS_CONFIG]: PartitionFieldsConfig; @@ -60,6 +67,7 @@ export interface MlStorageRecord { [ML_FROZEN_TIER_PREFERENCE]: FrozenTierPreference; [ML_ANOMALY_EXPLORER_PANELS]: AnomalyExplorerPanelsState | undefined; [ML_NOTIFICATIONS_LAST_CHECKED_AT]: number | undefined; + [ML_OVERVIEW_PANELS]: OverviewPanelsState; } export type MlStorage = Partial | null; @@ -78,6 +86,8 @@ export type TMlStorageMapped = T extends typeof ML_ENTIT ? AnomalyExplorerPanelsState | undefined : T extends typeof ML_NOTIFICATIONS_LAST_CHECKED_AT ? number | undefined + : T extends typeof ML_OVERVIEW_PANELS + ? OverviewPanelsState | undefined : null; export const ML_STORAGE_KEYS = [ @@ -87,4 +97,5 @@ export const ML_STORAGE_KEYS = [ ML_FROZEN_TIER_PREFERENCE, ML_ANOMALY_EXPLORER_PANELS, ML_NOTIFICATIONS_LAST_CHECKED_AT, + ML_OVERVIEW_PANELS, ] as const; diff --git a/x-pack/plugins/ml/public/application/components/collapsible_panel/collapsible_panel.tsx b/x-pack/plugins/ml/public/application/components/collapsible_panel/collapsible_panel.tsx new file mode 100644 index 0000000000000..53ed046423c2f --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/collapsible_panel/collapsible_panel.tsx @@ -0,0 +1,137 @@ +/* + * 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, + EuiButtonIcon, + EuiFlexGroup, + EuiFlexItem, + EuiSplitPanel, + EuiText, + EuiTitle, +} from '@elastic/eui'; +import React, { type FC } from 'react'; +import { css } from '@emotion/react/dist/emotion-react.cjs'; +import { useCurrentThemeVars } from '../../contexts/kibana'; + +export interface CollapsiblePanelProps { + isOpen: boolean; + onToggle: (isOpen: boolean) => void; + + header: React.ReactElement; + headerItems?: React.ReactElement[]; +} + +export const CollapsiblePanel: FC = ({ + isOpen, + onToggle, + children, + header, + headerItems, +}) => { + const { euiTheme } = useCurrentThemeVars(); + + return ( + + + + + + + { + onToggle(!isOpen); + }} + /> + + + +

{header}

+
+
+
+
+ {headerItems ? ( + + + {headerItems.map((item, i) => { + return ( + +
+ {item} +
+
+ ); + })} +
+
+ ) : null} +
+
+ {isOpen ? ( + + {children} + + ) : null} +
+ ); +}; + +export interface StatEntry { + label: string; + value: number; + 'data-test-subj'?: string; +} + +export interface OverviewStatsBarProps { + inputStats: StatEntry[]; + dataTestSub?: string; +} + +export const OverviewStatsBar: FC = ({ inputStats, dataTestSub }) => { + return ( + + {inputStats.map(({ value, label, 'data-test-subj': dataTestSubjValue }) => { + return ( + + + + {label}: + + + {value} + + + + ); + })} + + ); +}; diff --git a/x-pack/plugins/ml/public/application/components/collapsible_panel/index.ts b/x-pack/plugins/ml/public/application/components/collapsible_panel/index.ts new file mode 100644 index 0000000000000..d45a251f69ca9 --- /dev/null +++ b/x-pack/plugins/ml/public/application/components/collapsible_panel/index.ts @@ -0,0 +1,8 @@ +/* + * 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. + */ + +export { CollapsiblePanel } from './collapsible_panel'; diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/empty_prompt/empty_prompt.tsx b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/empty_prompt/empty_prompt.tsx index 23ce92dce1b91..14745812e3045 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/empty_prompt/empty_prompt.tsx +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/components/empty_prompt/empty_prompt.tsx @@ -6,16 +6,7 @@ */ import React, { FC } from 'react'; -import { - EuiButton, - EuiCallOut, - EuiEmptyPrompt, - EuiFlexGroup, - EuiFlexItem, - EuiImage, - EuiLink, - EuiTitle, -} from '@elastic/eui'; +import { EuiButton, EuiEmptyPrompt, EuiImage, EuiLink } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import dfaImage from './data_frame_analytics_kibana.png'; @@ -26,10 +17,7 @@ import { usePermissionCheck } from '../../../../../capabilities/check_capabiliti export const AnalyticsEmptyPrompt: FC = () => { const { - services: { - docLinks, - http: { basePath }, - }, + services: { docLinks }, } = useMlKibana(); const [canCreateDataFrameAnalytics, canStartStopDataFrameAnalytics] = usePermissionCheck([ @@ -40,7 +28,6 @@ export const AnalyticsEmptyPrompt: FC = () => { const disabled = !mlNodesAvailable() || !canCreateDataFrameAnalytics || !canStartStopDataFrameAnalytics; - const transformsLink = `${basePath.get()}/app/management/data/transform`; const navigateToPath = useNavigateToPath(); const navigateToSourceSelection = async () => { @@ -57,16 +44,15 @@ export const AnalyticsEmptyPrompt: FC = () => { size="fullWidth" src={dfaImage} alt={i18n.translate('xpack.ml.dataFrame.analyticsList.emptyPromptTitle', { - defaultMessage: 'Create your first data frame analytics job', + defaultMessage: 'Analyze your data with data frame analytics', })} /> } - color="subdued" title={

} @@ -78,39 +64,6 @@ export const AnalyticsEmptyPrompt: FC = () => { defaultMessage="Train outlier detection, regression, or classification machine learning models using data frame analytics." />

- - - - ), - sourcedata: ( - - - - ), - }} - /> - } - iconType="iInCircle" - /> } actions={[ @@ -118,37 +71,19 @@ export const AnalyticsEmptyPrompt: FC = () => { onClick={navigateToSourceSelection} isDisabled={disabled} color="primary" - iconType="plusInCircle" - fill data-test-subj="mlAnalyticsCreateFirstButton" > {i18n.translate('xpack.ml.dataFrame.analyticsList.emptyPromptButtonText', { - defaultMessage: 'Create job', + defaultMessage: 'Create data frame analytics job', })} , + + + , ]} - footer={ - - - -

- -

-
-
- - - - - -
- } data-test-subj="mlNoDataFrameAnalyticsFound" /> ); diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.test.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.test.ts index 613f9034e2ff4..949c8a47deb32 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.test.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.test.ts @@ -67,7 +67,7 @@ describe('get_analytics', () => { // act and assert expect(getAnalyticsJobsStats(mockResponse)).toEqual({ total: { - label: 'Total analytics jobs', + label: 'Total', value: 2, show: true, }, diff --git a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.ts b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.ts index 170a90b1fcba0..9a3ea0c9bef90 100644 --- a/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.ts +++ b/x-pack/plugins/ml/public/application/data_frame_analytics/pages/analytics_management/services/analytics_service/get_analytics.ts @@ -47,7 +47,7 @@ export function getInitialAnalyticsStats(): AnalyticStatsBarStats { return { total: { label: i18n.translate('xpack.ml.overview.statsBar.totalAnalyticsLabel', { - defaultMessage: 'Total analytics jobs', + defaultMessage: 'Total', }), value: 0, show: true, @@ -97,12 +97,18 @@ export function getAnalyticsJobsStats( ); resultStats.failed.show = resultStats.failed.value > 0; resultStats.total.value = analyticsStats.count; + + if (resultStats.total.value === 0) { + resultStats.started.show = false; + resultStats.stopped.show = false; + } + return resultStats; } export const getAnalyticsFactory = ( setAnalytics: React.Dispatch>, - setAnalyticsStats: React.Dispatch>, + setAnalyticsStats: (update: AnalyticStatsBarStats | undefined) => void, setErrorMessage: React.Dispatch< React.SetStateAction >, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/anomaly_detection_empty_state/anomaly_detection_empty_state.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/anomaly_detection_empty_state/anomaly_detection_empty_state.tsx index b8dee1a7e6f60..171320dd7b781 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/anomaly_detection_empty_state/anomaly_detection_empty_state.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/anomaly_detection_empty_state/anomaly_detection_empty_state.tsx @@ -7,15 +7,7 @@ import React, { FC } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; -import { - EuiButton, - EuiEmptyPrompt, - EuiFlexGroup, - EuiFlexItem, - EuiImage, - EuiLink, - EuiTitle, -} from '@elastic/eui'; +import { EuiButton, EuiEmptyPrompt, EuiImage, EuiLink } from '@elastic/eui'; import adImage from './anomaly_detection_kibana.png'; import { ML_PAGES } from '../../../../../../common/constants/locator'; import { useMlKibana, useMlLocator, useNavigateToPath } from '../../../../contexts/kibana'; @@ -47,12 +39,11 @@ export const AnomalyDetectionEmptyState: FC = () => { hasBorder={false} hasShadow={false} icon={} - color="subdued" title={

} @@ -66,43 +57,25 @@ export const AnomalyDetectionEmptyState: FC = () => {

} - actions={ + actions={[ - - } - footer={ - - - -

- -

-
-
- - - - - -
- } + , + + + , + ]} data-test-subj="mlAnomalyDetectionEmptyState" /> ); diff --git a/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/nodes_list.tsx b/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/nodes_list.tsx index a615e40c9e3ea..a8e5848aaef1a 100644 --- a/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/nodes_list.tsx +++ b/x-pack/plugins/ml/public/application/memory_usage/nodes_overview/nodes_list.tsx @@ -200,15 +200,20 @@ export const NodesList: FC = ({ compactView = false }) => { return (
- - - {nodesStats && ( - - - - )} - - + {nodesStats && !compactView ? ( + <> + + + {nodesStats && ( + + + + )} + + + + ) : null} +
allowNeutralSort={false} diff --git a/x-pack/plugins/ml/public/application/ml_nodes_check/check_ml_nodes.ts b/x-pack/plugins/ml/public/application/ml_nodes_check/check_ml_nodes.ts index ea981a25d7ecb..c44c391a2fcfd 100644 --- a/x-pack/plugins/ml/public/application/ml_nodes_check/check_ml_nodes.ts +++ b/x-pack/plugins/ml/public/application/ml_nodes_check/check_ml_nodes.ts @@ -43,3 +43,7 @@ export function lazyMlNodesAvailable() { export function permissionToViewMlNodeCount() { return userHasPermissionToViewMlNodeCount; } + +export function getMlNodesCount(): number { + return mlNodeCount; +} diff --git a/x-pack/plugins/ml/public/application/overview/components/analytics_panel/analytics_panel.tsx b/x-pack/plugins/ml/public/application/overview/components/analytics_panel/analytics_panel.tsx index b53860d9a3be6..41e9732461bb3 100644 --- a/x-pack/plugins/ml/public/application/overview/components/analytics_panel/analytics_panel.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/analytics_panel/analytics_panel.tsx @@ -5,28 +5,32 @@ * 2.0. */ -import React, { FC, useEffect, useState } from 'react'; -import { - EuiButton, - EuiCallOut, - EuiFlexGroup, - EuiFlexItem, - EuiLoadingSpinner, - EuiPanel, - EuiSpacer, - EuiText, -} from '@elastic/eui'; +import React, { FC, useCallback, useEffect, useState } from 'react'; +import { EuiCallOut, EuiLink, EuiLoadingSpinner } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; +import { useStorage } from '@kbn/ml-local-storage'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { type AnalyticStatsBarStats } from '../../../components/stats_bar'; +import { + OverviewStatsBar, + type StatEntry, +} from '../../../components/collapsible_panel/collapsible_panel'; +import { + ML_OVERVIEW_PANELS, + MlStorageKey, + TMlStorageMapped, +} from '../../../../../common/types/storage'; import { AnalyticsTable } from './table'; import { getAnalyticsFactory } from '../../../data_frame_analytics/pages/analytics_management/services/analytics_service'; import { DataFrameAnalyticsListRow } from '../../../data_frame_analytics/pages/analytics_management/components/analytics_list/common'; -import { AnalyticStatsBarStats, StatsBar } from '../../../components/stats_bar'; import { useMlLink } from '../../../contexts/kibana'; import { ML_PAGES } from '../../../../../common/constants/locator'; import { useRefresh } from '../../../routing/use_refresh'; import type { GetDataFrameAnalyticsStatsResponseError } from '../../../services/ml_api_service/data_frame_analytics'; import { AnalyticsEmptyPrompt } from '../../../data_frame_analytics/pages/analytics_management/components/empty_prompt'; +import { overviewPanelDefaultState } from '../../overview_page'; +import { CollapsiblePanel } from '../../../components/collapsible_panel'; interface Props { setLazyJobCount: React.Dispatch>; @@ -35,9 +39,7 @@ export const AnalyticsPanel: FC = ({ setLazyJobCount }) => { const refresh = useRefresh(); const [analytics, setAnalytics] = useState([]); - const [analyticsStats, setAnalyticsStats] = useState( - undefined - ); + const [analyticsStats, setAnalyticsStats] = useState(undefined); const [errorMessage, setErrorMessage] = useState(); const [isInitialized, setIsInitialized] = useState(false); @@ -45,9 +47,24 @@ export const AnalyticsPanel: FC = ({ setLazyJobCount }) => { page: ML_PAGES.DATA_FRAME_ANALYTICS_JOBS_MANAGE, }); + const [panelsState, setPanelsState] = useStorage< + MlStorageKey, + TMlStorageMapped + >(ML_OVERVIEW_PANELS, overviewPanelDefaultState); + + const setAnalyticsStatsCustom = useCallback((stats: AnalyticStatsBarStats | undefined) => { + if (!stats) return; + + const result = Object.entries(stats) + .filter(([k, v]) => v.show) + .map(([k, v]) => v); + + setAnalyticsStats(result); + }, []); + const getAnalytics = getAnalyticsFactory( setAnalytics, - setAnalyticsStats, + setAnalyticsStatsCustom, setErrorMessage, setIsInitialized, setLazyJobCount, @@ -78,58 +95,40 @@ export const AnalyticsPanel: FC = ({ setLazyJobCount }) => { const noDFAJobs = errorMessage === undefined && isInitialized === true && analytics.length === 0; return ( - <> - {noDFAJobs ? ( - - ) : ( - - {typeof errorMessage !== 'undefined' ? errorDisplay : null} - {isInitialized === false && ( - - )} - - {isInitialized === true && analytics.length > 0 && ( - <> - - - -

- {i18n.translate('xpack.ml.overview.analyticsList.PanelTitle', { - defaultMessage: 'Analytics', - })} -

-
-
- - - {analyticsStats !== undefined ? ( - - - - ) : null} - - - {i18n.translate('xpack.ml.overview.analyticsList.manageJobsButtonText', { - defaultMessage: 'Manage jobs', - })} - - - - -
- - - - )} -
- )} - + { + setPanelsState({ ...panelsState, dfaJobs: update }); + }} + header={ + + } + headerItems={[ + ...(analyticsStats + ? [ + , + ] + : []), + + {i18n.translate('xpack.ml.overview.analyticsList.manageJobsButtonText', { + defaultMessage: 'Manage jobs', + })} + , + ]} + > + {noDFAJobs ? : null} + + {typeof errorMessage !== 'undefined' ? errorDisplay : null} + + {isInitialized === false && } + + {isInitialized === true && analytics.length > 0 ? : null} + ); }; diff --git a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx index 1773531cb9aa2..30aa0f6d22dfb 100644 --- a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/anomaly_detection_panel.tsx @@ -6,10 +6,20 @@ */ import React, { FC, Fragment, useEffect, useState } from 'react'; -import { EuiCallOut, EuiLoadingSpinner, EuiPanel } from '@elastic/eui'; +import { EuiCallOut, EuiLink, EuiLoadingSpinner } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { zipObject } from 'lodash'; -import { useMlKibana } from '../../../contexts/kibana'; +import { zipObject, groupBy } from 'lodash'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { useStorage } from '@kbn/ml-local-storage'; +import { + ML_OVERVIEW_PANELS, + MlStorageKey, + TMlStorageMapped, +} from '../../../../../common/types/storage'; +import { ML_PAGES } from '../../../../../common/constants/locator'; +import { OverviewStatsBar } from '../../../components/collapsible_panel/collapsible_panel'; +import { CollapsiblePanel } from '../../../components/collapsible_panel'; +import { useMlKibana, useMlLink } from '../../../contexts/kibana'; import { AnomalyDetectionTable } from './table'; import { ml } from '../../../services/ml_api_service'; import { getGroupsFromJobs, getStatsBarData } from './utils'; @@ -19,8 +29,8 @@ import { useRefresh } from '../../../routing/use_refresh'; import { useToastNotificationService } from '../../../services/toast_notification_service'; import { AnomalyTimelineService } from '../../../services/anomaly_timeline_service'; import type { OverallSwimlaneData } from '../../../explorer/explorer_utils'; -import { JobStatsBarStats } from '../../../components/stats_bar'; import { AnomalyDetectionEmptyState } from '../../../jobs/jobs_list/components/anomaly_detection_empty_state'; +import { overviewPanelDefaultState } from '../../overview_page'; export type GroupsDictionary = Dictionary; @@ -50,10 +60,21 @@ export const AnomalyDetectionPanel: FC = ({ anomalyTimelineService, setLa const refresh = useRefresh(); + const [panelsState, setPanelsState] = useStorage< + MlStorageKey, + TMlStorageMapped + >(ML_OVERVIEW_PANELS, overviewPanelDefaultState); + + const manageJobsLink = useMlLink({ + page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE, + }); + const [isLoading, setIsLoading] = useState(false); const [groups, setGroups] = useState({}); const [groupsCount, setGroupsCount] = useState(0); - const [statsBarData, setStatsBarData] = useState(); + const [statsBarData, setStatsBarData] = useState>(); + const [restStatsBarData, setRestStatsBarData] = + useState>(); const [errorMessage, setErrorMessage] = useState(); const loadJobs = async () => { @@ -71,9 +92,20 @@ export const AnomalyDetectionPanel: FC = ({ anomalyTimelineService, setLa }); const { groups: jobsGroups, count } = getGroupsFromJobs(jobsSummaryList); const stats = getStatsBarData(jobsSummaryList); + + const statGroups = groupBy( + Object.entries(stats) + .filter(([k, v]) => v.show) + .map(([k, v]) => v), + 'group' + ); + setIsLoading(false); setErrorMessage(undefined); - setStatsBarData(stats); + + setStatsBarData(statGroups[0]); + setRestStatsBarData(statGroups[1]); + setGroupsCount(count); setGroups(jobsGroups); loadOverallSwimLanes(jobsGroups); @@ -138,30 +170,52 @@ export const AnomalyDetectionPanel: FC = ({ anomalyTimelineService, setLa ); - const panelClass = isLoading ? 'mlOverviewPanel__isLoading' : 'mlOverviewPanel'; - const noAdJobs = !errorMessage && isLoading === false && typeof errorMessage === 'undefined' && groupsCount === 0; - if (noAdJobs) { - return ; - } - return ( - + { + setPanelsState({ ...panelsState, adJobs: update }); + }} + header={ + + } + headerItems={[ + ...(statsBarData + ? [] + : []), + ...(restStatsBarData + ? [ + , + ] + : []), + + {i18n.translate('xpack.ml.overview.anomalyDetection.manageJobsButtonText', { + defaultMessage: 'Manage jobs', + })} + , + ]} + > + {noAdJobs ? : null} + {typeof errorMessage !== 'undefined' && errorDisplay} - {isLoading && } + + {isLoading ? : null} {isLoading === false && typeof errorMessage === 'undefined' && groupsCount > 0 ? ( - + ) : null} - + ); }; diff --git a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/table.tsx b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/table.tsx index 016261be7997e..de4a05c638ce2 100644 --- a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/table.tsx +++ b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/table.tsx @@ -9,13 +9,8 @@ import React, { FC, useState } from 'react'; import { Direction, EuiBasicTableColumn, - EuiButton, - EuiFlexGroup, - EuiFlexItem, EuiIcon, EuiInMemoryTable, - EuiSpacer, - EuiText, EuiToolTip, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; @@ -24,13 +19,10 @@ import { ChartsPluginStart } from '@kbn/charts-plugin/public'; import { formatHumanReadableDateTime } from '@kbn/ml-date-utils'; import { useGroupActions } from './actions'; import { Group, GroupsDictionary } from './anomaly_detection_panel'; -import { JobStatsBarStats, StatsBar } from '../../../components/stats_bar'; import { JobSelectorBadge } from '../../../components/job_selector/job_selector_badge'; import { toLocaleString } from '../../../util/string_utils'; import { SwimlaneContainer } from '../../../explorer/swimlane_container'; import { useTimeBuckets } from '../../../components/custom_hooks/use_time_buckets'; -import { ML_PAGES } from '../../../../../common/constants/locator'; -import { useMlLink } from '../../../contexts/kibana'; export enum AnomalyDetectionListColumns { id = 'id', @@ -44,11 +36,10 @@ export enum AnomalyDetectionListColumns { interface Props { items: GroupsDictionary; - statsBarData: JobStatsBarStats; chartsService: ChartsPluginStart; } -export const AnomalyDetectionTable: FC = ({ items, statsBarData, chartsService }) => { +export const AnomalyDetectionTable: FC = ({ items, chartsService }) => { const groupsList = Object.values(items); const [pageIndex, setPageIndex] = useState(0); const [pageSize, setPageSize] = useState(10); @@ -58,10 +49,6 @@ export const AnomalyDetectionTable: FC = ({ items, statsBarData, chartsSe const timeBuckets = useTimeBuckets(); - const manageJobsLink = useMlLink({ - page: ML_PAGES.ANOMALY_DETECTION_JOBS_MANAGE, - }); - const columns: Array> = [ { field: AnomalyDetectionListColumns.id, @@ -195,47 +182,19 @@ export const AnomalyDetectionTable: FC = ({ items, statsBarData, chartsSe }; return ( - <> - - - -

- {i18n.translate('xpack.ml.overview.anomalyDetection.panelTitle', { - defaultMessage: 'Anomaly Detection', - })} -

-
-
- - - - - - - - {i18n.translate('xpack.ml.overview.anomalyDetection.manageJobsButtonText', { - defaultMessage: 'Manage jobs', - })} - - - - -
- - - allowNeutralSort={false} - className="mlAnomalyDetectionTable" - columns={columns} - hasActions={true} - isExpandable={false} - isSelectable={false} - items={groupsList} - itemId={AnomalyDetectionListColumns.id} - onTableChange={onTableChange} - pagination={pagination} - sorting={sorting} - data-test-subj="mlOverviewTableAnomalyDetection" - /> - + + allowNeutralSort={false} + className="mlAnomalyDetectionTable" + columns={columns} + hasActions={true} + isExpandable={false} + isSelectable={false} + items={groupsList} + itemId={AnomalyDetectionListColumns.id} + onTableChange={onTableChange} + pagination={pagination} + sorting={sorting} + data-test-subj="mlOverviewTableAnomalyDetection" + /> ); }; diff --git a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts index fa2e83151b80f..a5a864e139eae 100644 --- a/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts +++ b/x-pack/plugins/ml/public/application/overview/components/anomaly_detection_panel/utils.ts @@ -76,40 +76,45 @@ export function getGroupsFromJobs(jobs: MlSummaryJobs): { export function getStatsBarData(jobsList: any) { const jobStats = { - activeNodes: { - label: i18n.translate('xpack.ml.overviewJobsList.statsBar.activeMLNodesLabel', { - defaultMessage: 'Active ML nodes', - }), - value: 0, - show: true, - }, total: { label: i18n.translate('xpack.ml.overviewJobsList.statsBar.totalJobsLabel', { - defaultMessage: 'Total jobs', + defaultMessage: 'Total', }), value: 0, show: true, + group: 0, }, open: { label: i18n.translate('xpack.ml.overviewJobsList.statsBar.openJobsLabel', { - defaultMessage: 'Open jobs', + defaultMessage: 'Open', }), value: 0, show: true, + group: 0, }, closed: { label: i18n.translate('xpack.ml.overviewJobsList.statsBar.closedJobsLabel', { - defaultMessage: 'Closed jobs', + defaultMessage: 'Closed', }), value: 0, show: true, + group: 0, }, failed: { label: i18n.translate('xpack.ml.overviewJobsList.statsBar.failedJobsLabel', { - defaultMessage: 'Failed jobs', + defaultMessage: 'Failed', }), value: 0, show: false, + group: 0, + }, + activeNodes: { + label: i18n.translate('xpack.ml.overviewJobsList.statsBar.activeMLNodesLabel', { + defaultMessage: 'Active ML nodes', + }), + value: 0, + show: true, + group: 1, }, activeDatafeeds: { label: i18n.translate('xpack.ml.jobsList.statsBar.activeDatafeedsLabel', { @@ -117,6 +122,7 @@ export function getStatsBarData(jobsList: any) { }), value: 0, show: true, + group: 1, }, }; @@ -158,5 +164,13 @@ export function getStatsBarData(jobsList: any) { jobStats.activeNodes.value = Object.keys(mlNodes).length; + if (jobStats.total.value === 0) { + for (const [statKey, val] of Object.entries(jobStats)) { + if (statKey !== 'total') { + val.show = false; + } + } + } + return jobStats; } diff --git a/x-pack/plugins/ml/public/application/overview/overview_page.tsx b/x-pack/plugins/ml/public/application/overview/overview_page.tsx index 4f7244c37f298..6772125bb9532 100644 --- a/x-pack/plugins/ml/public/application/overview/overview_page.tsx +++ b/x-pack/plugins/ml/public/application/overview/overview_page.tsx @@ -6,9 +6,15 @@ */ import React, { FC, useState } from 'react'; -import { EuiPanel, EuiSpacer } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n-react'; +import { EuiLink, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { mlTimefilterRefresh$, useTimefilter } from '@kbn/ml-date-picker'; +import { useStorage } from '@kbn/ml-local-storage'; +import { OverviewStatsBar } from '../components/collapsible_panel/collapsible_panel'; +import { ML_PAGES } from '../../../common/constants/locator'; +import { ML_OVERVIEW_PANELS, MlStorageKey, TMlStorageMapped } from '../../../common/types/storage'; +import { CollapsiblePanel } from '../components/collapsible_panel'; import { usePermissionCheck } from '../capabilities/check_capabilities'; import { mlNodesAvailable } from '../ml_nodes_check'; import { OverviewContent } from './components/content'; @@ -17,11 +23,18 @@ import { JobsAwaitingNodeWarning } from '../components/jobs_awaiting_node_warnin import { SavedObjectsWarning } from '../components/saved_objects_warning'; import { UpgradeWarning } from '../components/upgrade'; import { HelpMenu } from '../components/help_menu'; -import { useMlKibana } from '../contexts/kibana'; +import { useMlKibana, useMlLink } from '../contexts/kibana'; import { NodesList } from '../memory_usage/nodes_overview'; import { MlPageHeader } from '../components/page_header'; import { PageTitle } from '../components/page_title'; import { useIsServerless } from '../contexts/kibana/use_is_serverless'; +import { getMlNodesCount } from '../ml_nodes_check/check_ml_nodes'; + +export const overviewPanelDefaultState = Object.freeze({ + nodes: true, + adJobs: true, + dfaJobs: true, +}); export const OverviewPage: FC = () => { const serverless = useIsServerless(); @@ -33,11 +46,20 @@ export const OverviewPage: FC = () => { } = useMlKibana(); const helpLink = docLinks.links.ml.guide; + const viewNodesLink = useMlLink({ + page: ML_PAGES.MEMORY_USAGE, + }); + const timefilter = useTimefilter({ timeRangeSelector: true, autoRefreshSelector: true }); const [adLazyJobCount, setAdLazyJobCount] = useState(0); const [dfaLazyJobCount, setDfaLazyJobCount] = useState(0); + const [panelsState, setPanelsState] = useStorage< + MlStorageKey, + TMlStorageMapped + >(ML_OVERVIEW_PANELS, overviewPanelDefaultState); + return (
@@ -63,9 +85,36 @@ export const OverviewPage: FC = () => { {canViewMlNodes && serverless === false ? ( <> - + { + setPanelsState({ ...panelsState, nodes: update }); + }} + header={ + + } + headerItems={[ + , + + {i18n.translate('xpack.ml.overview.nodesPanel.viewNodeLink', { + defaultMessage: 'View nodes', + })} + , + ]} + > - + ) : null} diff --git a/x-pack/plugins/ml/public/application/routing/use_resolver.tsx b/x-pack/plugins/ml/public/application/routing/use_resolver.tsx index eb3586c15c05f..5c67ed9769426 100644 --- a/x-pack/plugins/ml/public/application/routing/use_resolver.tsx +++ b/x-pack/plugins/ml/public/application/routing/use_resolver.tsx @@ -35,7 +35,6 @@ export const useRouteResolver = ( ): { context: RouteResolverContext; results: ResolverResults; - component?: React.Component; } => { const requiredCapabilitiesRef = useRef(requiredCapabilities); const customResolversRef = useRef(customResolvers); diff --git a/x-pack/plugins/translations/translations/fr-FR.json b/x-pack/plugins/translations/translations/fr-FR.json index 62a79c1cff886..51c9841978fc7 100644 --- a/x-pack/plugins/translations/translations/fr-FR.json +++ b/x-pack/plugins/translations/translations/fr-FR.json @@ -22521,7 +22521,6 @@ "xpack.ml.notifications.newNotificationsMessage": "Il y a eu {newNotificationsCount, plural, one {# notification} many {# notifications} other {# notifications}} depuis {sinceDate}. Actualisez la page pour afficher les mises à jour.", "xpack.ml.notificationsIndicator.errorsAndWarningLabel": "Il y a eu {count, plural, one {# notification} many {# notifications} other {# notifications}} avec un niveau d'avertissement ou d'erreur depuis {lastCheckedAt}", "xpack.ml.notificationsIndicator.unreadLabel": "Vous avez des notifications non lues depuis {lastCheckedAt}", - "xpack.ml.overview.analyticsList.emptyPromptHelperText": "Avant de créer une tâche d'analyse du cadre de données, utilisez des {transforms} pour créer une {sourcedata}.", "xpack.ml.previewAlert.otherValuesLabel": "et {count, plural, one {# autre} many {# autres} other {# autres}}", "xpack.ml.previewAlert.previewMessage": "{alertsCount, plural, one {# anomalie a été trouvée} many {# anomalies ont été trouvées} other {# anomalies ont été trouvées}} au cours des dernières {interval}.", "xpack.ml.privilege.pleaseContactAdministratorTooltip": "{message} Veuillez contacter votre administrateur.", @@ -22898,7 +22897,6 @@ "xpack.ml.cases.anomalySwimLane.embeddableAddedEvent": "couloir d'anomalie ajouté", "xpack.ml.changePointDetection.pageHeader": "Modifier la détection du point", "xpack.ml.chrome.help.appName": "Machine Learning", - "xpack.ml.common.learnMoreQuestion": "Envie d'en savoir plus ?", "xpack.ml.common.readDocumentationLink": "Lire la documentation", "xpack.ml.components.colorRangeLegend.blueColorRangeLabel": "Bleu", "xpack.ml.components.colorRangeLegend.greenRedColorRangeLabel": "Vert – Rouge", @@ -24525,7 +24523,6 @@ "xpack.ml.overview.anomalyDetection.noAnomaliesFoundMessage": "Aucune anomalie n'a été trouvée", "xpack.ml.overview.anomalyDetection.noResultsFoundMessage": "Résultat introuvable", "xpack.ml.overview.anomalyDetection.overallScore": "Score général", - "xpack.ml.overview.anomalyDetection.panelTitle": "Détection des anomalies", "xpack.ml.overview.anomalyDetection.resultActions.openInJobManagementText": "Afficher les tâches", "xpack.ml.overview.anomalyDetection.resultActions.openJobsInAnomalyExplorerText": "Afficher dans l’Explorateur d'anomalies", "xpack.ml.overview.anomalyDetection.tableActionLabel": "Actions", @@ -24539,8 +24536,6 @@ "xpack.ml.overview.anomalyDetection.tableTypicalTooltip": "Valeurs typiques dans les résultats d'enregistrement des anomalies.", "xpack.ml.overview.anomalyDetection.viewJobsActionName": "Afficher les tâches", "xpack.ml.overview.anomalyDetection.viewResultsActionName": "Afficher dans l’Explorateur d'anomalies", - "xpack.ml.overview.gettingStartedSectionSourceData": "ensemble de données source centré sur les entités", - "xpack.ml.overview.gettingStartedSectionTransforms": "transformations", "xpack.ml.overview.notificationsLabel": "Notifications", "xpack.ml.overview.overviewLabel": "Aperçu", "xpack.ml.overview.statsBar.failedAnalyticsLabel": "Échoué", diff --git a/x-pack/plugins/translations/translations/ja-JP.json b/x-pack/plugins/translations/translations/ja-JP.json index fbda4b3dfe09c..81cdbbd9ca8e6 100644 --- a/x-pack/plugins/translations/translations/ja-JP.json +++ b/x-pack/plugins/translations/translations/ja-JP.json @@ -22512,7 +22512,6 @@ "xpack.ml.notifications.newNotificationsMessage": "{sinceDate}以降に{newNotificationsCount, plural, other {#件の通知があります}}。更新を表示するには、ページを更新してください。", "xpack.ml.notificationsIndicator.errorsAndWarningLabel": "{lastCheckedAt}以降にエラーまたは警告レベルの{count, plural, other {#件の通知があります}}", "xpack.ml.notificationsIndicator.unreadLabel": "{lastCheckedAt}以降に未読の通知があります", - "xpack.ml.overview.analyticsList.emptyPromptHelperText": "データフレーム分析ジョブを構築する前に、{transforms}を使用して{sourcedata}を作成してください。", "xpack.ml.previewAlert.otherValuesLabel": "および{count, plural, other {#個のその他}}", "xpack.ml.previewAlert.previewMessage": "過去{interval}に{alertsCount, plural, other {#個の異常}}が見つかりました。", "xpack.ml.privilege.pleaseContactAdministratorTooltip": "{message} 管理者にお問い合わせください。", @@ -22884,7 +22883,6 @@ "xpack.ml.cases.anomalySwimLane.embeddableAddedEvent": "追加された異常スイムレーン", "xpack.ml.changePointDetection.pageHeader": "変化点検出", "xpack.ml.chrome.help.appName": "機械学習", - "xpack.ml.common.learnMoreQuestion": "詳細について", "xpack.ml.common.readDocumentationLink": "ドキュメンテーションを表示", "xpack.ml.components.colorRangeLegend.blueColorRangeLabel": "青", "xpack.ml.components.colorRangeLegend.greenRedColorRangeLabel": "緑 - 赤", @@ -24511,7 +24509,6 @@ "xpack.ml.overview.anomalyDetection.noAnomaliesFoundMessage": "異常値が見つかりませんでした", "xpack.ml.overview.anomalyDetection.noResultsFoundMessage": "結果が見つかりませんでした", "xpack.ml.overview.anomalyDetection.overallScore": "全体スコア", - "xpack.ml.overview.anomalyDetection.panelTitle": "異常検知", "xpack.ml.overview.anomalyDetection.resultActions.openInJobManagementText": "ジョブを表示", "xpack.ml.overview.anomalyDetection.resultActions.openJobsInAnomalyExplorerText": "異常エクスプローラーで表示", "xpack.ml.overview.anomalyDetection.tableActionLabel": "アクション", @@ -24525,8 +24522,6 @@ "xpack.ml.overview.anomalyDetection.tableTypicalTooltip": "異常レコード結果の標準的な値。", "xpack.ml.overview.anomalyDetection.viewJobsActionName": "ジョブを表示", "xpack.ml.overview.anomalyDetection.viewResultsActionName": "異常エクスプローラーで表示", - "xpack.ml.overview.gettingStartedSectionSourceData": "エンティティ中心のソースデータセット", - "xpack.ml.overview.gettingStartedSectionTransforms": "トランスフォーム", "xpack.ml.overview.notificationsLabel": "通知", "xpack.ml.overview.overviewLabel": "概要", "xpack.ml.overview.statsBar.failedAnalyticsLabel": "失敗", diff --git a/x-pack/plugins/translations/translations/zh-CN.json b/x-pack/plugins/translations/translations/zh-CN.json index 306f183b72169..ac0efe7717c0b 100644 --- a/x-pack/plugins/translations/translations/zh-CN.json +++ b/x-pack/plugins/translations/translations/zh-CN.json @@ -22511,7 +22511,6 @@ "xpack.ml.notifications.newNotificationsMessage": "自 {sinceDate}以来有 {newNotificationsCount, plural, other {# 个通知}}。刷新页面以查看更新。", "xpack.ml.notificationsIndicator.errorsAndWarningLabel": "自 {lastCheckedAt}以来有 {count, plural, other {# 个通知}}包含错误或警告级别", "xpack.ml.notificationsIndicator.unreadLabel": "自 {lastCheckedAt}以来您有未计通知", - "xpack.ml.overview.analyticsList.emptyPromptHelperText": "构建数据帧分析作业之前,请使用 {transforms} 构造一个 {sourcedata}。", "xpack.ml.previewAlert.otherValuesLabel": "和{count, plural, other {另外 # 个}}", "xpack.ml.previewAlert.previewMessage": "在过去 {interval}找到 {alertsCount, plural, other {# 个异常}}。", "xpack.ml.privilege.pleaseContactAdministratorTooltip": "{message}请联系您的管理员。", @@ -22883,7 +22882,6 @@ "xpack.ml.cases.anomalySwimLane.embeddableAddedEvent": "已添加异常泳道", "xpack.ml.changePointDetection.pageHeader": "更改点检测", "xpack.ml.chrome.help.appName": "Machine Learning", - "xpack.ml.common.learnMoreQuestion": "希望了解详情?", "xpack.ml.common.readDocumentationLink": "阅读文档", "xpack.ml.components.colorRangeLegend.blueColorRangeLabel": "蓝", "xpack.ml.components.colorRangeLegend.greenRedColorRangeLabel": "绿 - 红", @@ -24510,7 +24508,6 @@ "xpack.ml.overview.anomalyDetection.noAnomaliesFoundMessage": "找不到异常", "xpack.ml.overview.anomalyDetection.noResultsFoundMessage": "找不到结果", "xpack.ml.overview.anomalyDetection.overallScore": "总分", - "xpack.ml.overview.anomalyDetection.panelTitle": "异常检测", "xpack.ml.overview.anomalyDetection.resultActions.openInJobManagementText": "查看作业", "xpack.ml.overview.anomalyDetection.resultActions.openJobsInAnomalyExplorerText": "在 Anomaly Explorer 中查看", "xpack.ml.overview.anomalyDetection.tableActionLabel": "操作", @@ -24524,8 +24521,6 @@ "xpack.ml.overview.anomalyDetection.tableTypicalTooltip": "异常记录结果中的典型值。", "xpack.ml.overview.anomalyDetection.viewJobsActionName": "查看作业", "xpack.ml.overview.anomalyDetection.viewResultsActionName": "在 Anomaly Explorer 中查看", - "xpack.ml.overview.gettingStartedSectionSourceData": "实体中心型源数据集", - "xpack.ml.overview.gettingStartedSectionTransforms": "转换", "xpack.ml.overview.notificationsLabel": "通知", "xpack.ml.overview.overviewLabel": "概览", "xpack.ml.overview.statsBar.failedAnalyticsLabel": "失败",