import React , { useState , useEffect , useCallback } from 'react' ; import { EuiButtonEmpty , EuiFlexItem , EuiFlexGroup , EuiFlyout } from '@elastic/eui' ; import { i18n } from '@kbn/i18n' ; import './_index.scss' ; import { Dictionary } from '../../../../common/types/common' ; import { useUrlState } from '../../util/url_state' ; import { IdBadges } from './id_badges/index' ; import { BADGE_LIMIT , JobSelectorFlyoutContent , JobSelectorFlyoutProps , } from './job_selector_flyout' ; import { MlJobWithTimeRange } from '../../../../common/types/anomaly_detection_jobs' ; import { useStorage } from '../../contexts/ml/use_storage' ; import { ApplyTimeRangeConfig , ML_APPLY_TIME_RANGE_CONFIG } from '../../../../common/types/storage' ; interface GroupObj { groupId : string ; jobIds : string [ ] ; } function mergeSelection ( jobIds , groupObjs , singleSelection ) { if ( singleSelection ) { return jobIds ; } const selectedIds = [ ] ; const alreadySelected = [ ] ; groupObjs . forEach ( ( group ) => { selectedIds . push ( group . groupId ) ; alreadySelected . push ( ... group . jobIds ) ; } ) ; jobIds . forEach ( ( jobId ) => { if ( alreadySelected . includes ( jobId ) === false ) { selectedIds . push ( jobId ) ; } } ) ; return selectedIds ; } type GroupsMap = Dictionary < string [ ] > ; export function getInitialGroupsMap ( selectedGroups ) { const map = { } ; if ( selectedGroups . length ) { selectedGroups . forEach ( ( group ) => { map [ group . groupId ] = group . jobIds ; } ) ; } return map ; } interface JobSelectorProps { dateFormatTz : string ; singleSelection : boolean ; timeseriesOnly : boolean ; } export interface JobSelectionMaps { jobsMap : Dictionary < MlJobWithTimeRange > ; groupsMap : Dictionary < string [ ] > ; } export function JobSelector ( { dateFormatTz , singleSelection , timeseriesOnly } ) { const [ globalState , setGlobalState ] = useUrlState ( '_g' ) ; const [ applyTimeRangeConfig , setApplyTimeRangeConfig ] = useStorage < ApplyTimeRangeConfig > ( ML_APPLY_TIME_RANGE_CONFIG , true ) ; const selectedJobIds = globalState ?. ml ?. jobIds ?? [ ] ; const selectedGroups = globalState ?. ml ?. groups ?? [ ] ; const [ maps , setMaps ] = useState < JobSelectionMaps > ( { groupsMap : getInitialGroupsMap ( selectedGroups ) , jobsMap : { } , } ) ; const [ selectedIds , setSelectedIds ] = useState ( mergeSelection ( selectedJobIds , selectedGroups , singleSelection ) ) ; const [ showAllBarBadges , setShowAllBarBadges ] = useState ( false ) ; const [ isFlyoutVisible , setIsFlyoutVisible ] = useState ( false ) ; useEffect ( ( ) => { setSelectedIds ( mergeSelection ( selectedJobIds , selectedGroups , singleSelection ) ) ; } , [ JSON . stringify ( [ selectedJobIds , selectedGroups ] ) ] ) ; function closeFlyout ( ) { setIsFlyoutVisible ( false ) ; } function showFlyout ( ) { setIsFlyoutVisible ( true ) ; } function handleJobSelectionClick ( ) { showFlyout ( ) ; } const applySelection = useCallback ( ( { newSelection , jobIds , groups : newGroups , time } ) => { setSelectedIds ( newSelection ) ; setGlobalState ( { ml : { jobIds , groups : newGroups , } , ... ( time !== undefined ? { time } : { } ) , } ) ; closeFlyout ( ) ; } , [ setGlobalState , setSelectedIds ] ) ; function renderJobSelectionBar ( ) { return ( < EuiFlexGroup responsive = { false } gutterSize = "xs" alignItems = "center" > < EuiFlexItem grow = { false } > < EuiFlexGroup wrap responsive = { false } gutterSize = "xs" alignItems = "center" data - test - subj = "mlJobSelectionBadges" > < IdBadges limit = { BADGE_LIMIT } maps = { maps } onLinkClick = { ( ) => setShowAllBarBadges ( ! showAllBarBadges ) } selectedIds = { selectedIds } showAllBarBadges = { showAllBarBadges } / > < /EuiFlexGroup> < /EuiFlexItem> < EuiFlexItem grow = { false } > < EuiButtonEmpty size = "xs" iconType = "pencil" onClick = { handleJobSelectionClick } data - test - subj = "mlButtonEditJobSelection" > { i18n . translate ( 'xpack.ml.jobSelector.jobSelectionButton' , { defaultMessage : 'Edit job selection' , } ) } < /EuiButtonEmpty> < /EuiFlexItem> < /EuiFlexGroup>); } function renderFlyout ( ) { if ( isFlyoutVisible ) { return ( < EuiFlyout onClose = { closeFlyout } data - test - subj = "mlFlyoutJobSelector" aria - labelledby = "jobSelectorFlyout" > < JobSelectorFlyoutContent dateFormatTz = { dateFormatTz } timeseriesOnly = { timeseriesOnly } singleSelection = { singleSelection } selectedIds = { selectedIds } onSelectionConfirmed = { applySelection } onJobsFetched = { setMaps } onFlyoutClose = { closeFlyout } maps = { maps } applyTimeRangeConfig = { applyTimeRangeConfig } onTimeRangeConfigChange = { setApplyTimeRangeConfig } / > < /EuiFlyout>); } } return ( < div className = "mlJobSelectorBar" > { selectedIds . length > 0 && renderJobSelectionBar ( ) } { renderFlyout ( ) } < /div>); }