55 */
66
77import React , { FC , useCallback , useState , useEffect } from 'react' ;
8-
98import { i18n } from '@kbn/i18n' ;
10-
119import {
12- Direction ,
13- EuiButton ,
1410 EuiCallOut ,
15- EuiEmptyPrompt ,
1611 EuiFlexGroup ,
1712 EuiFlexItem ,
18- EuiInMemoryTable ,
13+ EuiBasicTable ,
14+ EuiSearchBar ,
1915 EuiSearchBarProps ,
2016 EuiSpacer ,
2117} from '@elastic/eui' ;
@@ -43,6 +39,39 @@ import {
4339 getGroupQueryText ,
4440} from '../../../../../jobs/jobs_list/components/utils' ;
4541import { SourceSelection } from '../source_selection' ;
42+ import { filterAnalytics , AnalyticsSearchBar } from '../analytics_search_bar' ;
43+ import { AnalyticsEmptyPrompt } from './empty_prompt' ;
44+ import { useTableSettings } from './use_table_settings' ;
45+ import { RefreshAnalyticsListButton } from '../refresh_analytics_list_button' ;
46+
47+ const filters : EuiSearchBarProps [ 'filters' ] = [
48+ {
49+ type : 'field_value_selection' ,
50+ field : 'job_type' ,
51+ name : i18n . translate ( 'xpack.ml.dataframe.analyticsList.typeFilter' , {
52+ defaultMessage : 'Type' ,
53+ } ) ,
54+ multiSelect : 'or' ,
55+ options : Object . values ( ANALYSIS_CONFIG_TYPE ) . map ( ( val ) => ( {
56+ value : val ,
57+ name : val ,
58+ view : getJobTypeBadge ( val ) ,
59+ } ) ) ,
60+ } ,
61+ {
62+ type : 'field_value_selection' ,
63+ field : 'state' ,
64+ name : i18n . translate ( 'xpack.ml.dataframe.analyticsList.statusFilter' , {
65+ defaultMessage : 'Status' ,
66+ } ) ,
67+ multiSelect : 'or' ,
68+ options : Object . values ( DATA_FRAME_TASK_STATE ) . map ( ( val ) => ( {
69+ value : val ,
70+ name : val ,
71+ view : getTaskStateBadge ( val ) ,
72+ } ) ) ,
73+ } ,
74+ ] ;
4675
4776function getItemIdToExpandedRowMap (
4877 itemIds : DataFrameAnalyticsId [ ] ,
@@ -70,23 +99,23 @@ export const DataFrameAnalyticsList: FC<Props> = ({
7099 const [ isInitialized , setIsInitialized ] = useState ( false ) ;
71100 const [ isSourceIndexModalVisible , setIsSourceIndexModalVisible ] = useState ( false ) ;
72101 const [ isLoading , setIsLoading ] = useState ( false ) ;
73-
102+ const [ filteredAnalytics , setFilteredAnalytics ] = useState < {
103+ active : boolean ;
104+ items : DataFrameAnalyticsListRow [ ] ;
105+ } > ( {
106+ active : false ,
107+ items : [ ] ,
108+ } ) ;
74109 const [ searchQueryText , setSearchQueryText ] = useState ( '' ) ;
75-
76110 const [ analytics , setAnalytics ] = useState < DataFrameAnalyticsListRow [ ] > ( [ ] ) ;
77111 const [ analyticsStats , setAnalyticsStats ] = useState < AnalyticStatsBarStats | undefined > (
78112 undefined
79113 ) ;
80114 const [ expandedRowItemIds , setExpandedRowItemIds ] = useState < DataFrameAnalyticsId [ ] > ( [ ] ) ;
81-
82115 const [ errorMessage , setErrorMessage ] = useState < any > ( undefined ) ;
83- const [ searchError , setSearchError ] = useState < any > ( undefined ) ;
84-
85- const [ pageIndex , setPageIndex ] = useState ( 0 ) ;
86- const [ pageSize , setPageSize ] = useState ( 10 ) ;
87-
88- const [ sortField , setSortField ] = useState < string > ( DataFrameAnalyticsListColumn . id ) ;
89- const [ sortDirection , setSortDirection ] = useState < Direction > ( 'asc' ) ;
116+ // Query text/job_id based on url but only after getAnalytics is done first
117+ // selectedJobIdFromUrlInitialized makes sure the query is only run once since analytics is being refreshed constantly
118+ const [ selectedIdFromUrlInitialized , setSelectedIdFromUrlInitialized ] = useState ( false ) ;
90119
91120 const disabled =
92121 ! checkPermission ( 'canCreateDataFrameAnalytics' ) ||
@@ -100,9 +129,29 @@ export const DataFrameAnalyticsList: FC<Props> = ({
100129 blockRefresh
101130 ) ;
102131
103- // Query text/job_id based on url but only after getAnalytics is done first
104- // selectedJobIdFromUrlInitialized makes sure the query is only run once since analytics is being refreshed constantly
105- const [ selectedIdFromUrlInitialized , setSelectedIdFromUrlInitialized ] = useState ( false ) ;
132+ const setQueryClauses = ( queryClauses : any ) => {
133+ if ( queryClauses . length ) {
134+ const filtered = filterAnalytics ( analytics , queryClauses ) ;
135+ setFilteredAnalytics ( { active : true , items : filtered } ) ;
136+ } else {
137+ setFilteredAnalytics ( { active : false , items : [ ] } ) ;
138+ }
139+ } ;
140+
141+ const filterList = ( ) => {
142+ if ( searchQueryText !== '' && selectedIdFromUrlInitialized === true ) {
143+ // trigger table filtering with query for job id to trigger table filter
144+ const query = EuiSearchBar . Query . parse ( searchQueryText ) ;
145+ let clauses : any = [ ] ;
146+ if ( query && query . ast !== undefined && query . ast . clauses !== undefined ) {
147+ clauses = query . ast . clauses ;
148+ }
149+ setQueryClauses ( clauses ) ;
150+ } else {
151+ setQueryClauses ( [ ] ) ;
152+ }
153+ } ;
154+
106155 useEffect ( ( ) => {
107156 if ( selectedIdFromUrlInitialized === false && analytics . length > 0 ) {
108157 const { jobId, groupIds } = getSelectedIdFromUrl ( window . location . href ) ;
@@ -116,9 +165,15 @@ export const DataFrameAnalyticsList: FC<Props> = ({
116165
117166 setSelectedIdFromUrlInitialized ( true ) ;
118167 setSearchQueryText ( queryText ) ;
168+ } else {
169+ filterList ( ) ;
119170 }
120171 } , [ selectedIdFromUrlInitialized , analytics ] ) ;
121172
173+ useEffect ( ( ) => {
174+ filterList ( ) ;
175+ } , [ selectedIdFromUrlInitialized , searchQueryText ] ) ;
176+
122177 const getAnalyticsCallback = useCallback ( ( ) => getAnalytics ( true ) , [ ] ) ;
123178
124179 // Subscribe to the refresh observable to trigger reloading the analytics list.
@@ -137,6 +192,10 @@ export const DataFrameAnalyticsList: FC<Props> = ({
137192 isMlEnabledInSpace
138193 ) ;
139194
195+ const { onTableChange, pageOfItems, pagination, sorting } = useTableSettings (
196+ filteredAnalytics . active ? filteredAnalytics . items : analytics
197+ ) ;
198+
140199 // Before the analytics have been loaded for the first time, display the loading indicator only.
141200 // Otherwise a user would see 'No data frame analytics found' during the initial loading.
142201 if ( ! isInitialized ) {
@@ -160,34 +219,10 @@ export const DataFrameAnalyticsList: FC<Props> = ({
160219 if ( analytics . length === 0 ) {
161220 return (
162221 < >
163- < EuiEmptyPrompt
164- iconType = "createAdvancedJob"
165- title = {
166- < h2 >
167- { i18n . translate ( 'xpack.ml.dataFrame.analyticsList.emptyPromptTitle' , {
168- defaultMessage : 'Create your first data frame analytics job' ,
169- } ) }
170- </ h2 >
171- }
172- actions = {
173- ! isManagementTable
174- ? [
175- < EuiButton
176- onClick = { ( ) => setIsSourceIndexModalVisible ( true ) }
177- isDisabled = { disabled }
178- color = "primary"
179- iconType = "plusInCircle"
180- fill
181- data-test-subj = "mlAnalyticsCreateFirstButton"
182- >
183- { i18n . translate ( 'xpack.ml.dataFrame.analyticsList.emptyPromptButtonText' , {
184- defaultMessage : 'Create job' ,
185- } ) }
186- </ EuiButton > ,
187- ]
188- : [ ]
189- }
190- data-test-subj = "mlNoDataFrameAnalyticsFound"
222+ < AnalyticsEmptyPrompt
223+ isManagementTable = { isManagementTable }
224+ disabled = { disabled }
225+ onCreateFirstJobClick = { ( ) => setIsSourceIndexModalVisible ( true ) }
191226 />
192227 { isSourceIndexModalVisible === true && (
193228 < SourceSelection onClose = { ( ) => setIsSourceIndexModalVisible ( false ) } />
@@ -196,95 +231,32 @@ export const DataFrameAnalyticsList: FC<Props> = ({
196231 ) ;
197232 }
198233
199- const sorting = {
200- sort : {
201- field : sortField ,
202- direction : sortDirection ,
203- } ,
204- } ;
205-
206234 const itemIdToExpandedRowMap = getItemIdToExpandedRowMap ( expandedRowItemIds , analytics ) ;
207235
208- const pagination = {
209- initialPageIndex : pageIndex ,
210- initialPageSize : pageSize ,
211- totalItemCount : analytics . length ,
212- pageSizeOptions : [ 10 , 20 , 50 ] ,
213- hidePerPageOptions : false ,
214- } ;
215-
216- const handleSearchOnChange : EuiSearchBarProps [ 'onChange' ] = ( search ) => {
217- if ( search . error !== null ) {
218- setSearchError ( search . error . message ) ;
219- return false ;
220- }
221-
222- setSearchError ( undefined ) ;
223- setSearchQueryText ( search . queryText ) ;
224- return true ;
225- } ;
226-
227- const search : EuiSearchBarProps = {
228- query : searchQueryText ,
229- onChange : handleSearchOnChange ,
230- box : {
231- incremental : true ,
232- } ,
233- filters : [
234- {
235- type : 'field_value_selection' ,
236- field : 'job_type' ,
237- name : i18n . translate ( 'xpack.ml.dataframe.analyticsList.typeFilter' , {
238- defaultMessage : 'Type' ,
239- } ) ,
240- multiSelect : 'or' ,
241- options : Object . values ( ANALYSIS_CONFIG_TYPE ) . map ( ( val ) => ( {
242- value : val ,
243- name : val ,
244- view : getJobTypeBadge ( val ) ,
245- } ) ) ,
246- } ,
247- {
248- type : 'field_value_selection' ,
249- field : 'state' ,
250- name : i18n . translate ( 'xpack.ml.dataframe.analyticsList.statusFilter' , {
251- defaultMessage : 'Status' ,
252- } ) ,
253- multiSelect : 'or' ,
254- options : Object . values ( DATA_FRAME_TASK_STATE ) . map ( ( val ) => ( {
255- value : val ,
256- name : val ,
257- view : getTaskStateBadge ( val ) ,
258- } ) ) ,
259- } ,
260- ] ,
261- } ;
262-
263- const onTableChange : EuiInMemoryTable < DataFrameAnalyticsListRow > [ 'onTableChange' ] = ( {
264- page = { index : 0 , size : 10 } ,
265- sort = { field : DataFrameAnalyticsListColumn . id , direction : 'asc' } ,
266- } ) => {
267- const { index, size } = page ;
268- setPageIndex ( index ) ;
269- setPageSize ( size ) ;
236+ const stats = analyticsStats && (
237+ < EuiFlexItem grow = { false } >
238+ < StatsBar stats = { analyticsStats } dataTestSub = { 'mlAnalyticsStatsBar' } />
239+ </ EuiFlexItem >
240+ ) ;
270241
271- const { field, direction } = sort ;
272- setSortField ( field ) ;
273- setSortDirection ( direction ) ;
274- } ;
242+ const managementStats = (
243+ < EuiFlexItem >
244+ < EuiFlexGroup justifyContent = "spaceBetween" >
245+ { stats }
246+ < EuiFlexItem grow = { false } >
247+ < RefreshAnalyticsListButton />
248+ </ EuiFlexItem >
249+ </ EuiFlexGroup >
250+ </ EuiFlexItem >
251+ ) ;
275252
276253 return (
277254 < >
278255 { modals }
279- < EuiSpacer size = "m" />
256+ { ! isManagementTable && < EuiSpacer size = "m" /> }
280257 < EuiFlexGroup justifyContent = "spaceBetween" >
281- < EuiFlexItem grow = { false } >
282- { analyticsStats && (
283- < EuiFlexItem grow = { false } >
284- < StatsBar stats = { analyticsStats } dataTestSub = { 'mlAnalyticsStatsBar' } />
285- </ EuiFlexItem >
286- ) }
287- </ EuiFlexItem >
258+ { ! isManagementTable && stats }
259+ { isManagementTable && managementStats }
288260 < EuiFlexItem grow = { false } >
289261 < EuiFlexGroup alignItems = "center" gutterSize = "s" >
290262 { ! isManagementTable && (
@@ -300,22 +272,25 @@ export const DataFrameAnalyticsList: FC<Props> = ({
300272 </ EuiFlexGroup >
301273 < EuiSpacer size = "m" />
302274 < div data-test-subj = "mlAnalyticsTableContainer" >
303- < EuiInMemoryTable
304- allowNeutralSort = { false }
275+ < AnalyticsSearchBar
276+ filters = { filters }
277+ searchQueryText = { searchQueryText }
278+ setSearchQueryText = { setSearchQueryText }
279+ />
280+ < EuiSpacer size = "l" />
281+ < EuiBasicTable < DataFrameAnalyticsListRow >
305282 className = "mlAnalyticsTable"
306283 columns = { columns }
307- error = { searchError }
308284 hasActions = { false }
309285 isExpandable = { true }
310286 isSelectable = { false }
311- items = { analytics }
287+ items = { pageOfItems }
312288 itemId = { DataFrameAnalyticsListColumn . id }
313289 itemIdToExpandedRowMap = { itemIdToExpandedRowMap }
314290 loading = { isLoading }
315- onTableChange = { onTableChange }
316- pagination = { pagination }
291+ onChange = { onTableChange }
292+ pagination = { pagination ! }
317293 sorting = { sorting }
318- search = { search }
319294 data-test-subj = { isLoading ? 'mlAnalyticsTable loading' : 'mlAnalyticsTable loaded' }
320295 rowProps = { ( item ) => ( {
321296 'data-test-subj' : `mlAnalyticsTableRow row-${ item . id } ` ,
0 commit comments