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()}
+
+ );
+ }
+}