diff --git a/x-pack/legacy/plugins/ml/public/components/stats_bar/_index.scss b/x-pack/legacy/plugins/ml/public/components/stats_bar/_index.scss new file mode 100644 index 0000000000000..e8d8e85763eff --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/components/stats_bar/_index.scss @@ -0,0 +1,2 @@ +@import 'stat'; +@import 'stats_bar'; diff --git a/x-pack/legacy/plugins/ml/public/components/stats_bar/_stat.scss b/x-pack/legacy/plugins/ml/public/components/stats_bar/_stat.scss new file mode 100644 index 0000000000000..d05c1f7195587 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/components/stats_bar/_stat.scss @@ -0,0 +1,7 @@ +.stat { + margin-right: $euiSizeS; + + .stat-value { + font-weight: bold + } +} diff --git a/x-pack/legacy/plugins/ml/public/components/stats_bar/_stats_bar.scss b/x-pack/legacy/plugins/ml/public/components/stats_bar/_stats_bar.scss new file mode 100644 index 0000000000000..c433b53789573 --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/components/stats_bar/_stats_bar.scss @@ -0,0 +1,6 @@ +.mlStatsBar { + // SASSTODO: proper calcs + height: 42px; + padding: 14px; + background-color: $euiColorLightestShade; +} diff --git a/x-pack/legacy/plugins/ml/public/components/stats_bar/index.ts b/x-pack/legacy/plugins/ml/public/components/stats_bar/index.ts new file mode 100644 index 0000000000000..4c781afe0a64c --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/components/stats_bar/index.ts @@ -0,0 +1,7 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { StatsBar, TransformStatsBarStats } from './stats_bar'; diff --git a/x-pack/legacy/plugins/ml/public/components/stats_bar/stat.tsx b/x-pack/legacy/plugins/ml/public/components/stats_bar/stat.tsx new file mode 100644 index 0000000000000..55fa902fe41ed --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/components/stats_bar/stat.tsx @@ -0,0 +1,24 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC } from 'react'; + +export interface StatsBarStat { + label: string; + value: string | number; + show?: boolean; +} +interface StatProps { + stat: StatsBarStat; +} + +export const Stat: FC = ({ stat }) => { + return ( + + {stat.label}: {stat.value} + + ); +}; diff --git a/x-pack/legacy/plugins/ml/public/components/stats_bar/stats_bar.tsx b/x-pack/legacy/plugins/ml/public/components/stats_bar/stats_bar.tsx new file mode 100644 index 0000000000000..0995b1e70df5d --- /dev/null +++ b/x-pack/legacy/plugins/ml/public/components/stats_bar/stats_bar.tsx @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { FC } from 'react'; +import { Stat, StatsBarStat } from './stat'; + +interface JobStatsBarStats { + activeNodes: StatsBarStat; + total: StatsBarStat; + open: StatsBarStat; + failed: StatsBarStat; + closed: StatsBarStat; + activeDatafeeds: StatsBarStat; +} + +export interface TransformStatsBarStats { + total: StatsBarStat; + batch: StatsBarStat; + continuous: StatsBarStat; + failed: StatsBarStat; + started: StatsBarStat; +} + +type StatsBarStats = TransformStatsBarStats | JobStatsBarStats; +type StatsKey = keyof StatsBarStats; + +interface StatsBarProps { + stats: StatsBarStats; + dataTestSub: string; +} + +export const StatsBar: FC = ({ stats, dataTestSub }) => { + const statsList = Object.keys(stats).map(k => stats[k as StatsKey]); + return ( +
+ {statsList + .filter((s: StatsBarStat) => s.show) + .map((s: StatsBarStat) => ( + + ))} +
+ ); +}; diff --git a/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/__snapshots__/page.test.tsx.snap b/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/__snapshots__/page.test.tsx.snap index af70dccfad236..dc16e6843ba91 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/__snapshots__/page.test.tsx.snap +++ b/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/__snapshots__/page.test.tsx.snap @@ -5,6 +5,9 @@ exports[`Data Frame: Job List Minimal initialization 1`] = ` + Minimal initialization 1`] = ` hasShadow={false} paddingSize="m" > - + diff --git a/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/__snapshots__/transform_list.test.tsx.snap b/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/__snapshots__/transform_list.test.tsx.snap index d7ccbb57382f4..dd3763d054b14 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/__snapshots__/transform_list.test.tsx.snap +++ b/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/__snapshots__/transform_list.test.tsx.snap @@ -1,7 +1,31 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Data Frame: Transform List Minimal initialization 1`] = ` - + + + + Create your first data frame transform + , + ] + } + data-test-subj="mlNoDataFrameTransformsFound" + iconColor="subdued" + title={ +

+ No data frame transforms found +

+ } + /> +
`; diff --git a/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/transform_list.test.tsx b/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/transform_list.test.tsx index e4e7537aae6ea..522f17c3382d2 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/transform_list.test.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/transform_list.test.tsx @@ -12,7 +12,14 @@ import { DataFrameTransformList } from './transform_list'; describe('Data Frame: Transform List ', () => { test('Minimal initialization', () => { - const wrapper = shallow(); + const wrapper = shallow( + + ); expect(wrapper).toMatchSnapshot(); }); diff --git a/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/transform_list.tsx b/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/transform_list.tsx index 449753365a4ae..d42cf6c55bf43 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/transform_list.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/components/transform_list/transform_list.tsx @@ -19,11 +19,7 @@ import { SortDirection, } from '@elastic/eui'; -import { - DataFrameTransformId, - moveToDataFrameWizard, - useRefreshTransformList, -} from '../../../../common'; +import { DataFrameTransformId, moveToDataFrameWizard } from '../../../../common'; import { checkPermission } from '../../../../../privilege/check_privilege'; import { getTaskStateBadge } from './columns'; import { DeleteAction } from './action_delete'; @@ -39,11 +35,9 @@ import { Query, Clause, } from './common'; -import { getTransformsFactory } from '../../services/transform_service'; import { getColumns } from './columns'; import { ExpandedRow } from './expanded_row'; import { ProgressBar, TransformTable } from './transform_table'; -import { useRefreshInterval } from './use_refresh_interval'; function getItemIdToExpandedRowMap( itemIds: DataFrameTransformId[], @@ -69,20 +63,28 @@ function stringMatch(str: string | undefined, substr: string) { ); } -export const DataFrameTransformList: SFC = () => { - const [isInitialized, setIsInitialized] = useState(false); +interface Props { + isInitialized: boolean; + transforms: DataFrameTransformListRow[]; + errorMessage: any; + transformsLoading: boolean; +} + +export const DataFrameTransformList: SFC = ({ + isInitialized, + transforms, + errorMessage, + transformsLoading, +}) => { const [isLoading, setIsLoading] = useState(false); - const [blockRefresh, setBlockRefresh] = useState(false); const [filterActive, setFilterActive] = useState(false); - const [transforms, setTransforms] = useState([]); const [filteredTransforms, setFilteredTransforms] = useState([]); const [expandedRowItemIds, setExpandedRowItemIds] = useState([]); const [transformSelection, setTransformSelection] = useState([]); const [isActionsMenuOpen, setIsActionsMenuOpen] = useState(false); - const [errorMessage, setErrorMessage] = useState(undefined); const [searchError, setSearchError] = useState(undefined); const [pageIndex, setPageIndex] = useState(0); @@ -96,20 +98,6 @@ export const DataFrameTransformList: SFC = () => { !checkPermission('canPreviewDataFrame') || !checkPermission('canStartStopDataFrame'); - const getTransforms = getTransformsFactory( - setTransforms, - setErrorMessage, - setIsInitialized, - blockRefresh - ); - // Subscribe to the refresh observable to trigger reloading the transform list. - useRefreshTransformList({ - isLoading: setIsLoading, - onRefresh: () => getTransforms(true), - }); - // Call useRefreshInterval() after the subscription above is set up. - useRefreshInterval(setBlockRefresh); - const onQueryChange = ({ query, error }: { query: Query; error: any }) => { if (error) { setSearchError(error.message); @@ -188,13 +176,13 @@ export const DataFrameTransformList: SFC = () => { // Before the transforms have been loaded for the first time, display the loading indicator only. // Otherwise a user would see 'No data frame transforms found' during the initial loading. if (!isInitialized) { - return ; + return ; } if (typeof errorMessage !== 'undefined') { return ( - + { if (transforms.length === 0) { return ( - + @@ -368,7 +356,7 @@ export const DataFrameTransformList: SFC = () => { return ( - + { + if (transform.mode === DATA_FRAME_MODE.CONTINUOUS) { + transformStats.continuous.value++; + } else if (transform.mode === DATA_FRAME_MODE.BATCH) { + transformStats.batch.value++; + } else if (transform.stats.state === DATA_FRAME_TRANSFORM_STATE.FAILED) { + failedTransforms++; + } else if (transform.stats.state === DATA_FRAME_TRANSFORM_STATE.STARTED) { + startedTransforms++; + } + }); + + transformStats.total.value = transformsList.length; + transformStats.started.value = startedTransforms; + + if (failedTransforms !== 0) { + transformStats.failed.value = failedTransforms; + transformStats.failed.show = true; + } else { + transformStats.failed.show = false; + } + + return transformStats; +} + +interface Props { + transformsList: DataFrameTransformListRow[]; +} + +export const TransformStatsBar: FC = ({ transformsList }) => { + const transformStats: TransformStatsBarStats = createTranformStats(transformsList); + + return ; +}; diff --git a/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/page.tsx b/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/page.tsx index 576e5f9a42637..3f00a37484808 100644 --- a/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/page.tsx +++ b/x-pack/legacy/plugins/ml/public/data_frame/pages/transform_management/page.tsx @@ -25,17 +25,42 @@ import { import { NavigationMenu } from '../../../components/navigation_menu/navigation_menu'; import { useRefreshTransformList } from '../../common'; +import { DataFrameTransformListRow } from './components/transform_list/common'; import { CreateTransformButton } from './components/create_transform_button'; import { DataFrameTransformList } from './components/transform_list'; import { RefreshTransformListButton } from './components/refresh_transform_list_button'; +import { TransformStatsBar } from '../transform_management/components/transform_list/transforms_stats_bar'; +import { getTransformsFactory } from './services/transform_service'; +import { useRefreshInterval } from './components/transform_list/use_refresh_interval'; export const Page: FC = () => { const [isLoading, setIsLoading] = useState(false); + const [transformsLoading, setTransformsLoading] = useState(false); + const [isInitialized, setIsInitialized] = useState(false); + const [blockRefresh, setBlockRefresh] = useState(false); + const [transforms, setTransforms] = useState([]); + const [errorMessage, setErrorMessage] = useState(undefined); const { refresh } = useRefreshTransformList({ isLoading: setIsLoading }); + const getTransforms = getTransformsFactory( + setTransforms, + setErrorMessage, + setIsInitialized, + blockRefresh + ); + + // Subscribe to the refresh observable to trigger reloading the transform list. + useRefreshTransformList({ + isLoading: setTransformsLoading, + onRefresh: () => getTransforms(true), + }); + // Call useRefreshInterval() after the subscription above is set up. + useRefreshInterval(setBlockRefresh); + return ( + @@ -77,7 +102,12 @@ export const Page: FC = () => { - + diff --git a/x-pack/legacy/plugins/ml/public/index.scss b/x-pack/legacy/plugins/ml/public/index.scss index 0b7811cb0504b..2a751aea1d831 100644 --- a/x-pack/legacy/plugins/ml/public/index.scss +++ b/x-pack/legacy/plugins/ml/public/index.scss @@ -50,6 +50,7 @@ @import 'components/messagebar/index'; @import 'components/navigation_menu/index'; @import 'components/rule_editor/index'; // SASSTODO: This file overwrites EUI directly + @import 'components/stats_bar/index'; // Hacks are last so they can overwrite anything above if needed @import 'hacks'; diff --git a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/_index.scss b/x-pack/legacy/plugins/ml/public/jobs/jobs_list/_index.scss index a215ff2d1a835..2d26cd644eca2 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/_index.scss +++ b/x-pack/legacy/plugins/ml/public/jobs/jobs_list/_index.scss @@ -6,6 +6,5 @@ @import 'components/job_group/index'; @import 'components/jobs_list/index'; // SASSTODO: Dangerous EUI overwrites @import 'components/jobs_list_view/index'; -@import 'components/jobs_stats_bar/index'; @import 'components/multi_job_actions/index'; // SASSTODO: Dangerous EUI overwrites -@import 'components/start_datafeed_modal/index'; // SASSTODO: Needs a rewrite \ No newline at end of file +@import 'components/start_datafeed_modal/index'; // SASSTODO: Needs a rewrite diff --git a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/_index.scss b/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/_index.scss deleted file mode 100644 index 995478bc0966c..0000000000000 --- a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/_index.scss +++ /dev/null @@ -1 +0,0 @@ -@import 'jobs_stats_bar'; \ No newline at end of file diff --git a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/_jobs_stats_bar.scss b/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/_jobs_stats_bar.scss deleted file mode 100644 index 63a1bc01c94ae..0000000000000 --- a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/_jobs_stats_bar.scss +++ /dev/null @@ -1,14 +0,0 @@ -.jobs-stats-bar { - // SASSTODO: proper calcs - height: 42px; - padding: 14px; - background-color: $euiColorLightestShade; - - .stat { - margin-right: $euiSizeS; - - .stat-value { - font-weight: bold - } - } -} diff --git a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js b/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js index f82b3f5b07b05..83116579a2adb 100644 --- a/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js +++ b/x-pack/legacy/plugins/ml/public/jobs/jobs_list/components/jobs_stats_bar/jobs_stats_bar.js @@ -6,6 +6,7 @@ import { JOB_STATE, DATAFEED_STATE } from 'plugins/ml/../common/constants/states'; +import { StatsBar } from '../../../../components/stats_bar'; import PropTypes from 'prop-types'; import React from 'react'; @@ -99,27 +100,11 @@ function createJobStats(jobsSummaryList) { return jobStats; } -function Stat({ stat }) { - return ( - - {stat.label}: {stat.value} - - ); -} -Stat.propTypes = { - stat: PropTypes.object.isRequired, -}; - export const JobStatsBar = ({ jobsSummaryList }) => { const jobStats = createJobStats(jobsSummaryList); - const stats = Object.keys(jobStats).map(k => jobStats[k]); return ( -
- { - stats.filter(s => (s.show)).map(s => ) - } -
+ ); };