diff --git a/x-pack/legacy/plugins/cross_cluster_replication/public/np_ready/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js b/x-pack/legacy/plugins/cross_cluster_replication/public/np_ready/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js index 60feac5ec8c6edc..5bf0d31146f7910 100644 --- a/x-pack/legacy/plugins/cross_cluster_replication/public/np_ready/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js +++ b/x-pack/legacy/plugins/cross_cluster_replication/public/np_ready/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js @@ -55,11 +55,11 @@ export class FollowerIndicesTable extends PureComponent { if (queryText) { return followerIndices.filter(followerIndex => { - const { name, shards } = followerIndex; + const { name, remoteCluster, leaderIndex } = followerIndex; const inName = name.toLowerCase().includes(queryText); - const inRemoteCluster = shards[0].remoteCluster.toLowerCase().includes(queryText); - const inLeaderIndex = shards[0].leaderIndex.toLowerCase().includes(queryText); + const inRemoteCluster = remoteCluster.toLowerCase().includes(queryText); + const inLeaderIndex = leaderIndex.toLowerCase().includes(queryText); return inName || inRemoteCluster || inLeaderIndex; }); diff --git a/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js new file mode 100644 index 000000000000000..ef4a511f276bdb3 --- /dev/null +++ b/x-pack/plugins/cross_cluster_replication/public/app/sections/home/follower_indices_list/components/follower_indices_table/follower_indices_table.js @@ -0,0 +1,314 @@ +/* + * 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, { PureComponent, Fragment } from 'react'; +import PropTypes from 'prop-types'; +import { i18n } from '@kbn/i18n'; +import { FormattedMessage } from '@kbn/i18n/react'; +import { + EuiIcon, + EuiHealth, + EuiInMemoryTable, + EuiLink, + EuiLoadingKibana, + EuiOverlayMask, +} from '@elastic/eui'; +import { API_STATUS, UIM_FOLLOWER_INDEX_SHOW_DETAILS_CLICK } from '../../../../../constants'; +import { + FollowerIndexPauseProvider, + FollowerIndexResumeProvider, + FollowerIndexUnfollowProvider, +} from '../../../../../components'; +import { routing } from '../../../../../services/routing'; +import { trackUiMetric } from '../../../../../services/track_ui_metric'; +import { ContextMenu } from '../context_menu'; + +export class FollowerIndicesTable extends PureComponent { + static propTypes = { + followerIndices: PropTypes.array, + selectFollowerIndex: PropTypes.func.isRequired, + }; + + state = { + selectedItems: [], + }; + + onSearch = ({ query }) => { + const { text } = query; + const normalizedSearchText = text.toLowerCase(); + this.setState({ + queryText: normalizedSearchText, + }); + }; + + editFollowerIndex = id => { + const uri = routing.getFollowerIndexPath(id, '/edit', false); + routing.navigate(uri); + }; + + getFilteredIndices = () => { + const { followerIndices } = this.props; + const { queryText } = this.state; + + if (queryText) { + return followerIndices.filter(followerIndex => { + const { name, remoteCluster, leaderIndex } = followerIndex; + + const inName = name.toLowerCase().includes(queryText); + const inRemoteCluster = remoteCluster.toLowerCase().includes(queryText); + const inLeaderIndex = leaderIndex.toLowerCase().includes(queryText); + + return inName || inRemoteCluster || inLeaderIndex; + }); + } + + return followerIndices.slice(0); + }; + + getTableColumns() { + const { selectFollowerIndex } = this.props; + + const actions = [ + /* Pause or resume follower index */ + { + render: followerIndex => { + const { name, isPaused } = followerIndex; + const label = isPaused + ? i18n.translate( + 'xpack.crossClusterReplication.followerIndexList.table.actionResumeDescription', + { + defaultMessage: 'Resume replication', + } + ) + : i18n.translate( + 'xpack.crossClusterReplication.followerIndexList.table.actionPauseDescription', + { + defaultMessage: 'Pause replication', + } + ); + + return isPaused ? ( + + {resumeFollowerIndex => ( + resumeFollowerIndex(name)} data-test-subj="resumeButton"> + + {label} + + )} + + ) : ( + + {pauseFollowerIndex => ( + pauseFollowerIndex(followerIndex)} + data-test-subj="pauseButton" + > + + {label} + + )} + + ); + }, + }, + /* Edit follower index */ + { + render: ({ name }) => { + const label = i18n.translate( + 'xpack.crossClusterReplication.followerIndexList.table.actionEditDescription', + { + defaultMessage: 'Edit follower index', + } + ); + + return ( + this.editFollowerIndex(name)} data-test-subj="editButton"> + + {label} + + ); + }, + }, + /* Unfollow leader index */ + { + render: ({ name }) => { + const label = i18n.translate( + 'xpack.crossClusterReplication.followerIndexList.table.actionUnfollowDescription', + { + defaultMessage: 'Unfollow leader index', + } + ); + + return ( + + {unfollowLeaderIndex => ( + unfollowLeaderIndex(name)} data-test-subj="unfollowButton"> + + {label} + + )} + + ); + }, + }, + ]; + + return [ + { + field: 'name', + name: i18n.translate( + 'xpack.crossClusterReplication.followerIndexList.table.nameColumnTitle', + { + defaultMessage: 'Name', + } + ), + sortable: true, + truncateText: false, + render: name => { + return ( + { + trackUiMetric('click', UIM_FOLLOWER_INDEX_SHOW_DETAILS_CLICK); + selectFollowerIndex(name); + }} + data-test-subj="followerIndexLink" + > + {name} + + ); + }, + }, + { + field: 'isPaused', + name: i18n.translate( + 'xpack.crossClusterReplication.followerIndexList.table.statusColumnTitle', + { + defaultMessage: 'Status', + } + ), + truncateText: true, + sortable: true, + render: isPaused => { + return isPaused ? ( + + + + ) : ( + + + + ); + }, + }, + { + field: 'remoteCluster', + name: i18n.translate( + 'xpack.crossClusterReplication.followerIndexList.table.clusterColumnTitle', + { + defaultMessage: 'Remote cluster', + } + ), + truncateText: true, + sortable: true, + }, + { + field: 'leaderIndex', + name: i18n.translate( + 'xpack.crossClusterReplication.followerIndexList.table.leaderIndexColumnTitle', + { + defaultMessage: 'Leader index', + } + ), + truncateText: true, + sortable: true, + }, + { + name: i18n.translate( + 'xpack.crossClusterReplication.followerIndexList.table.actionsColumnTitle', + { + defaultMessage: 'Actions', + } + ), + actions, + width: '100px', + }, + ]; + } + + renderLoading = () => { + const { apiStatusDelete } = this.props; + + if (apiStatusDelete === API_STATUS.DELETING) { + return ( + + + + ); + } + return null; + }; + + render() { + const { selectedItems } = this.state; + + const sorting = { + sort: { + field: 'name', + direction: 'asc', + }, + }; + + const pagination = { + initialPageSize: 20, + pageSizeOptions: [10, 20, 50], + }; + + const selection = { + onSelectionChange: newSelectedItems => this.setState({ selectedItems: newSelectedItems }), + }; + + const search = { + toolsLeft: selectedItems.length ? ( + + ) : ( + undefined + ), + onChange: this.onSearch, + box: { + incremental: true, + }, + }; + + return ( + + ({ + 'data-test-subj': 'row', + })} + cellProps={(item, column) => ({ + 'data-test-subj': `cell-${column.field}`, + })} + data-test-subj="followerIndexListTable" + /> + {this.renderLoading()} + + ); + } +}