AMBARI-22682 Ambari 3.0 Admin View: Add visual-search box to all pages. (atkach)


Project: http://git-wip-us.apache.org/repos/asf/ambari/repo
Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/81c04545
Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/81c04545
Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/81c04545

Branch: refs/heads/branch-feature-AMBARI-21674
Commit: 81c045452e37adc6e68379a125026ac98f8ed103
Parents: 9ea3fa1
Author: Andrii Tkach <atk...@apache.org>
Authored: Thu Dec 21 15:24:55 2017 +0200
Committer: Andrii Tkach <atk...@apache.org>
Committed: Thu Dec 21 15:24:55 2017 +0200

----------------------------------------------------------------------
 .../main/resources/ui/admin-web/app/index.html  |   2 +
 .../app/scripts/controllers/SideNavCtrl.js      |   6 +-
 .../controllers/ambariViews/ViewsListCtrl.js    | 184 +++++------
 .../remoteClusters/RemoteClustersListCtrl.js    | 109 +++----
 .../stackVersions/StackVersionsEditCtrl.js      |   2 +-
 .../stackVersions/StackVersionsListCtrl.js      | 191 +++++-------
 .../userManagement/GroupsListCtrl.js            |  86 ++----
 .../controllers/userManagement/UsersListCtrl.js | 132 ++++----
 .../app/scripts/directives/comboSearch.js       |  39 ++-
 .../admin-web/app/scripts/services/Cluster.js   |   3 +-
 .../admin-web/app/scripts/services/Filters.js   |  84 +++++
 .../ui/admin-web/app/scripts/services/Group.js  |  11 +-
 .../app/scripts/services/Pagination.js          |  59 ++++
 .../app/scripts/services/RemoteCluster.js       |  10 +-
 .../ui/admin-web/app/scripts/services/Stack.js  |  32 +-
 .../ui/admin-web/app/scripts/services/User.js   |  13 +-
 .../ui/admin-web/app/styles/user-management.css |   4 +
 .../app/views/ambariViews/viewsList.html        |   5 +-
 .../app/views/remoteClusters/list.html          |  33 +-
 .../admin-web/app/views/stackVersions/list.html |  34 +--
 .../app/views/userManagement/groupsList.html    |  51 ++--
 .../app/views/userManagement/usersList.html     |  61 ++--
 .../ambariViews/ViewsListCtrl_test.js           |  66 +---
 .../userManagement/GroupsListCtrl_test.js       | 100 +-----
 .../userManagement/UsersListCtrl_test.js        | 306 -------------------
 .../test/unit/directives/comboSearch_test.js    |  13 -
 .../test/unit/services/Filters_test.js          | 161 ++++++++++
 .../test/unit/services/Pagination_test.js       |  72 +++++
 28 files changed, 805 insertions(+), 1064 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/index.html
----------------------------------------------------------------------
diff --git a/ambari-admin/src/main/resources/ui/admin-web/app/index.html 
b/ambari-admin/src/main/resources/ui/admin-web/app/index.html
index a1346ed..a9c6984 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/index.html
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/index.html
@@ -170,6 +170,8 @@
 <script src="scripts/services/AddRepositoryModal.js"></script>
 <script src="scripts/services/AddVersionModal.js"></script>
 <script src="scripts/services/RoleDetailsModal.js"></script>
+<script src="scripts/services/Pagination.js"></script>
+<script src="scripts/services/Filters.js"></script>
 <!-- endbuild -->
 </body>
 </html>

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/SideNavCtrl.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/SideNavCtrl.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/SideNavCtrl.js
index 558d110..6bf356d 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/SideNavCtrl.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/SideNavCtrl.js
@@ -29,11 +29,7 @@ angular.module('ambariAdminConsole')
   }, true);
 
   function loadRepos() {
-    Stack.allRepos({version: '',
-      cluster: {
-        options: [],
-        current: null
-      }}, {}).then(function (repos) {
+    Stack.allRepos().then(function (repos) {
       $scope.totalRepos = repos.itemTotal;
     });
   }

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsListCtrl.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsListCtrl.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsListCtrl.js
index 8c61a25..f536e50 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsListCtrl.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/ambariViews/ViewsListCtrl.js
@@ -18,7 +18,9 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-.controller('ViewsListCtrl',['$scope', 'View','$modal', 'Alert', 
'ConfirmationModal', '$translate', 'Settings', function($scope, View, $modal, 
Alert, ConfirmationModal, $translate, Settings) {
+.controller('ViewsListCtrl',
+['$scope', 'View','$modal', 'Alert', 'ConfirmationModal', '$translate', 
'Settings', 'Pagination', 'Filters',
+function($scope, View, $modal, Alert, ConfirmationModal, $translate, Settings, 
Pagination, Filters) {
   var $t = $translate.instant;
   var VIEWS_VERSION_STATUS_TIMEOUT = 5000;
   $scope.isLoading = false;
@@ -33,6 +35,9 @@ angular.module('ambariAdminConsole')
     {
       key: 'url',
       label: $t('urls.url'),
+      customValueConverter: function(item) {
+        return '/main/view/' + item.view_name + '/' + item.short_url;
+      },
       options: []
     },
     {
@@ -46,136 +51,32 @@ angular.module('ambariAdminConsole')
       options: []
     }
   ];
-
-  function checkViewVersionStatus(view, versionObj, versionNumber) {
-    var deferred = View.checkViewVersionStatus(view.view_name, versionNumber);
-
-    deferred.promise.then(function (status) {
-      if (versionNeedStatusUpdate(status)) {
-        setTimeout(function() {
-          checkViewVersionStatus(view, versionObj, versionNumber);
-        }, VIEWS_VERSION_STATUS_TIMEOUT);
-      } else {
-        versionObj.status = status;
-        angular.forEach(view.versions, function (version) {
-          if (version.status === 'DEPLOYED') {
-            view.canCreateInstance = true;
-          }
-        })
-      }
-    });
-  }
-
-  function versionNeedStatusUpdate(status) {
-    return status !== 'DEPLOYED' && status !== 'ERROR';
-  }
-
-  function loadViews() {
-    $scope.isLoading = true;
-    View.all().then(function (views) {
-      $scope.isLoading = false;
-      $scope.views = views;
-      $scope.instances = [];
-      angular.forEach(views, function (view) {
-        angular.forEach(view.versions, function (versionObj, versionNumber) {
-          if (versionNeedStatusUpdate(versionObj.status)) {
-            checkViewVersionStatus(view, versionObj, versionNumber);
-          }
-        });
-        angular.forEach(view.instances, function (instance) {
-          instance.ViewInstanceInfo.short_url_name = 
instance.ViewInstanceInfo.short_url_name || '';
-          instance.ViewInstanceInfo.short_url = 
instance.ViewInstanceInfo.short_url || '';
-          instance.ViewInstanceInfo.versionObj = 
view.versions[instance.ViewInstanceInfo.version] || {};
-          $scope.instances.push(instance.ViewInstanceInfo);
-        });
-      });
-      $scope.initFilterOptions();
-      $scope.filterInstances();
-    }).catch(function (data) {
-      Alert.error($t('views.alerts.cannotLoadViews'), data.data.message);
-    });
-  }
-
-  function showInstancesOnPage() {
-    var startIndex = ($scope.currentPage - 1) * $scope.instancesPerPage + 1;
-    var endIndex = $scope.currentPage * $scope.instancesPerPage;
-    var showedCount = 0;
-    var filteredCount = 0;
-
-    angular.forEach($scope.instances, function(instance) {
-      instance.isShowed = false;
-      if (instance.isFiltered) {
-        filteredCount++;
-        if (filteredCount >= startIndex && filteredCount <= endIndex) {
-          instance.isShowed = true;
-          showedCount++;
-        }
-      }
-    });
-    $scope.tableInfo.showed = showedCount;
-  }
-
   $scope.views = [];
   $scope.instances = [];
-  $scope.instancesPerPage = 10;
-  $scope.currentPage = 1;
-  $scope.maxVisiblePages = 10;
   $scope.tableInfo = {
     filtered: 0,
-    showed: 0
+    showed: 0,
+    total: 0
   };
+  $scope.pagination = Pagination.create();
 
-  loadViews();
+  $scope.resetPagination = function() {
+    $scope.pagination.resetPagination($scope.instances, $scope.tableInfo);
+  };
 
-  $scope.initFilterOptions = function() {
-    $scope.filters.forEach(function(filter) {
-      filter.options = $.unique($scope.instances.map(function(instance) {
-        if (filter.key === 'url') {
-          return '/main/view/' + instance.view_name + '/' + instance.short_url;
-        }
-        return instance[filter.key];
-      })).map(function(item) {
-        return {
-          key: item,
-          label: item
-        }
-      });
-    });
+  $scope.pageChanged = function() {
+    $scope.pagination.pageChanged($scope.instances, $scope.tableInfo);
   };
 
   $scope.filterInstances = function(appliedFilters) {
-    var filteredCount = 0;
-    angular.forEach($scope.instances, function(instance) {
-      instance.isFiltered = !(appliedFilters && appliedFilters.length > 0 && 
appliedFilters.some(function(filter) {
-        if (filter.key === 'url') {
-          return filter.values.every(function(value) {
-            return ('/main/view/' + instance.view_name + '/' + 
instance.short_url).indexOf(value) === -1;
-          });
-        }
-        return filter.values.every(function(value) {
-          return instance[filter.key].indexOf(value) === -1;
-        });
-      }));
-
-      filteredCount += ~~instance.isFiltered;
-    });
-    $scope.tableInfo.filtered = filteredCount;
-    $scope.resetPagination();
+    $scope.tableInfo.filtered = Filters.filterItems(appliedFilters, 
$scope.instances, $scope.filters);
+    $scope.pagination.resetPagination($scope.instances, $scope.tableInfo);
   };
 
   $scope.toggleSearchBox = function() {
     $('.search-box-button .popup-arrow-up, 
.search-box-row').toggleClass('hide');
   };
 
-  $scope.pageChanged = function() {
-    showInstancesOnPage();
-  };
-
-  $scope.resetPagination = function() {
-    $scope.currentPage = 1;
-    showInstancesOnPage();
-  };
-
   $scope.cloneInstance = function(instanceClone) {
     $scope.createInstance(instanceClone);
   };
@@ -217,4 +118,57 @@ angular.module('ambariAdminConsole')
         });
     });
   };
+
+  loadViews();
+
+  function checkViewVersionStatus(view, versionObj, versionNumber) {
+    var deferred = View.checkViewVersionStatus(view.view_name, versionNumber);
+
+    deferred.promise.then(function (status) {
+      if (versionNeedStatusUpdate(status)) {
+        setTimeout(function() {
+          checkViewVersionStatus(view, versionObj, versionNumber);
+        }, VIEWS_VERSION_STATUS_TIMEOUT);
+      } else {
+        versionObj.status = status;
+        angular.forEach(view.versions, function (version) {
+          if (version.status === 'DEPLOYED') {
+            view.canCreateInstance = true;
+          }
+        })
+      }
+    });
+  }
+
+  function versionNeedStatusUpdate(status) {
+    return status !== 'DEPLOYED' && status !== 'ERROR';
+  }
+
+  function loadViews() {
+    $scope.isLoading = true;
+    View.all().then(function (views) {
+      $scope.isLoading = false;
+      $scope.views = views;
+      $scope.instances = [];
+      angular.forEach(views, function (view) {
+        angular.forEach(view.versions, function (versionObj, versionNumber) {
+          if (versionNeedStatusUpdate(versionObj.status)) {
+            checkViewVersionStatus(view, versionObj, versionNumber);
+          }
+        });
+        angular.forEach(view.instances, function (instance) {
+          instance.ViewInstanceInfo.short_url_name = 
instance.ViewInstanceInfo.short_url_name || '';
+          instance.ViewInstanceInfo.short_url = 
instance.ViewInstanceInfo.short_url || '';
+          instance.ViewInstanceInfo.versionObj = 
view.versions[instance.ViewInstanceInfo.version] || {};
+          $scope.instances.push(instance.ViewInstanceInfo);
+        });
+      });
+      $scope.tableInfo.total = $scope.instances.length;
+      Filters.initFilterOptions($scope.filters, $scope.instances);
+      $scope.filterInstances();
+    }).catch(function (data) {
+      Alert.error($t('views.alerts.cannotLoadViews'), data.data.message);
+    });
+  }
+
 }]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersListCtrl.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersListCtrl.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersListCtrl.js
index 4726357..5944d20 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersListCtrl.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/remoteClusters/RemoteClustersListCtrl.js
@@ -18,93 +18,74 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-.controller('RemoteClustersListCtrl', ['$scope', '$routeParams', '$translate', 
'RemoteCluster', 'Settings', function ($scope, $routeParams, $translate, 
RemoteCluster, Settings) {
+.controller('RemoteClustersListCtrl',
+['$scope', '$routeParams', '$translate', 'RemoteCluster', 'Settings', 
'Pagination', 'Filters',
+function ($scope, $routeParams, $translate, RemoteCluster, Settings, 
Pagination, Filters) {
   var $t = $translate.instant;
 
   $scope.minInstanceForPagination = Settings.minRowsToShowPagination;
   $scope.clusterName = $routeParams.clusterName;
   $scope.isLoading = false;
-
   $scope.constants = {
     groups: $t('common.clusters').toLowerCase()
   };
-
-  $scope.groupsPerPage = 10;
-  $scope.currentPage = 1;
-  $scope.totalGroups = 1;
-  $scope.currentNameFilter = '';
-  $scope.maxVisiblePages=20;
   $scope.tableInfo = {
+    filtered: 0,
     total: 0,
     showed: 0
   };
+  $scope.pagination = Pagination.create();
+  $scope.filters = [
+    {
+      key: 'clusterName',
+      label: $t('views.clusterName'),
+      options: []
+    },
+    {
+      key: 'service',
+      label: $t('common.services'),
+      customValueConverter: function (item) {
+        return item.ClusterInfo.services;
+      },
+      isMultiple: true,
+      options: []
+    }
+  ];
 
-  $scope.isNotEmptyFilter = true;
-
-  $scope.pageChanged = function() {
-    loadRemoteClusters();
-  };
-  $scope.groupsPerPageChanges = function() {
-    loadRemoteClusters();
+  $scope.toggleSearchBox = function () {
+    $('.search-box-button .popup-arrow-up, 
.search-box-row').toggleClass('hide');
   };
 
-  $scope.resetPagination = function() {
-    $scope.currentPage = 1;
-    loadRemoteClusters();
+  $scope.filterClusters = function (appliedFilters) {
+    $scope.tableInfo.filtered = Filters.filterItems(appliedFilters, 
$scope.remoteClusters, $scope.filters);
+    $scope.pagination.resetPagination($scope.remoteClusters, $scope.tableInfo);
   };
 
-  $scope.typeFilterOptions = [
-    $t('common.any')
-  ];
-
-  $scope.currentTypeFilter = $scope.typeFilterOptions[0];
-
-  $scope.clearFilters = function () {
-    $scope.currentNameFilter = '';
-    $scope.currentTypeFilter = $scope.typeFilterOptions[0];
-    $scope.resetPagination();
+  $scope.pageChanged = function () {
+    $scope.pagination.pageChanged($scope.remoteClusters, $scope.tableInfo);
   };
 
-  function loadRemoteClusters(){
-      $scope.isLoading = true;
-      RemoteCluster.all({
-        currentPage: $scope.currentPage,
-        groupsPerPage: $scope.groupsPerPage,
-        searchString: $scope.currentNameFilter,
-        service: $scope.currentTypeFilter
-      }).then(function(remoteclusters) {
-        $scope.isLoading = false;
-
-        $scope.totalGroups = remoteclusters.itemTotal;
-        $scope.tableInfo.total = remoteclusters.itemTotal;
-        $scope.tableInfo.showed = remoteclusters.items.length;
-
-        $scope.remoteClusters = remoteclusters.items;
-
-        remoteclusters.items.map(function(clusteritem){
-          clusteritem.ClusterInfo.services.map(function(service){
-            var serviceIndex = $scope.typeFilterOptions.indexOf(service);
-            if(serviceIndex == -1){
-              $scope.typeFilterOptions.push(service);
-            }
-          })
-        })
+  $scope.resetPagination = function () {
+    $scope.pagination.resetPagination($scope.remoteClusters, $scope.tableInfo);
+  };
 
-      })
-      .catch(function(data) {
+  function loadRemoteClusters() {
+    $scope.isLoading = true;
+    RemoteCluster.all().then(function (remoteclusters) {
+      $scope.isLoading = false;
+      $scope.remoteClusters = remoteclusters.items.map(function (item) {
+        item.clusterName = item.ClusterInfo.name;
+        return item;
+      });
+      $scope.tableInfo.total = $scope.remoteClusters.length;
+      $scope.filterClusters();
+      Filters.initFilterOptions($scope.filters, $scope.remoteClusters);
+    })
+      .catch(function (data) {
         console.error($t('remoteClusters.alerts.fetchError'), data);
       });
-  };
+  }
 
   loadRemoteClusters();
 
-  $scope.$watch(
-    function (scope) {
-      return Boolean(scope.currentNameFilter || (scope.currentTypeFilter));
-    },
-    function (newValue, oldValue, scope) {
-      scope.isNotEmptyFilter = newValue;
-    }
-  );
-
 }]);

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsEditCtrl.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsEditCtrl.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsEditCtrl.js
index a4b121c..3edaf07 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsEditCtrl.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsEditCtrl.js
@@ -136,7 +136,7 @@ angular.module('ambariAdminConsole')
   };
 
   $scope.isDeletable = function() {
-    return !($scope.repoStatus == 'current' || $scope.repoStatus == 
'installed');
+    return !($scope.repoStatus === 'CURRENT' || $scope.repoStatus === 
'INSTALLED');
   };
 
   $scope.disableUnusedOS = function() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsListCtrl.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsListCtrl.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsListCtrl.js
index ae00978..ea7a480 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsListCtrl.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/stackVersions/StackVersionsListCtrl.js
@@ -18,7 +18,9 @@
 'use strict';
 
 angular.module('ambariAdminConsole')
-  .controller('StackVersionsListCtrl', ['$scope', 'Cluster', 'Stack', 
'$routeParams', '$translate', 'Settings', function ($scope, Cluster, Stack, 
$routeParams, $translate, Settings) {
+  .controller('StackVersionsListCtrl',
+  ['$scope', 'Cluster', 'Stack', '$routeParams', '$translate', 'Settings', 
'Pagination', '$q', 'Filters',
+  function ($scope, Cluster, Stack, $routeParams, $translate, Settings, 
Pagination, $q, Filters) {
     var $t = $translate.instant;
     $scope.getConstant = function (key) {
       return $t(key).toLowerCase();
@@ -26,86 +28,86 @@ angular.module('ambariAdminConsole')
     $scope.minInstanceForPagination = Settings.minRowsToShowPagination;
     $scope.isLoading = false;
     $scope.clusterName = $routeParams.clusterName;
-    $scope.filter = {
-      name: '',
-      version: '',
-      type: '',
-      cluster: {
-        options: [],
-        current: null
-      },
-      stack: {
-        options: [],
-        current: null
-      }
-    };
-    $scope.isNotEmptyFilter = true;
-
-    $scope.pagination = {
-      totalRepos: 10,
-      maxVisiblePages: 20,
-      itemsPerPage: 10,
-      currentPage: 1
-    };
-
     $scope.tableInfo = {
       total: 0,
       showed: 0,
       filtered: 0
     };
-
     $scope.repos = [];
     $scope.dropDownClusters = [];
     $scope.selectedCluster = $scope.dropDownClusters[0];
+    $scope.filters = [
+      {
+        key: 'stack',
+        label: $t('common.stack'),
+        customValueConverter: function(item) {
+          return item.stack_name + '-' + item.stack_version;
+        },
+        options: []
+      },
+      {
+        key: 'display_name',
+        label: $t('common.name'),
+        options: []
+      },
+      {
+        key: 'type',
+        label: $t('common.type'),
+        options: []
+      },
+      {
+        key: 'repository_version',
+        label: $t('common.version'),
+        options: []
+      },
+      {
+        key: 'cluster',
+        label: $t('common.cluster'),
+        options: []
+      }
+    ];
+    $scope.pagination = Pagination.create();
 
-    $scope.resetPagination = function () {
-      $scope.pagination.currentPage = 1;
-      $scope.loadAllData();
+    $scope.resetPagination = function() {
+      $scope.pagination.resetPagination($scope.repos, $scope.tableInfo);
     };
 
-    $scope.pageChanged = function () {
-      $scope.loadAllData();
+    $scope.pageChanged = function() {
+      $scope.pagination.pageChanged($scope.repos, $scope.tableInfo);
     };
 
-    $scope.goToCluster = function() {
-      window.location.replace(Settings.siteRoot + 
'#/main/admin/stack/versions');
+    $scope.filterRepos = function (appliedFilters) {
+      $scope.tableInfo.filtered = Filters.filterItems(appliedFilters, 
$scope.repos, $scope.filters);
+      $scope.pagination.resetPagination($scope.repos, $scope.tableInfo);
+    };
+
+    $scope.toggleSearchBox = function() {
+      $('.search-box-button .popup-arrow-up, 
.search-box-row').toggleClass('hide');
     };
 
-    $scope.clearFilters = function () {
-      $scope.filter.name = '';
-      $scope.filter.version = '';
-      $scope.filter.cluster.current = $scope.filter.cluster.options[0];
-      $scope.filter.stack.current = $scope.filter.stack.options[0];
-      $scope.resetPagination();
+    $scope.goToCluster = function() {
+      window.location.replace(Settings.siteRoot + 
'#/main/admin/stack/versions');
     };
 
     $scope.fetchRepoClusterStatus = function (allRepos) {
+      var calls = [];
       if (allRepos && allRepos.length) {
-        var clusterName = ($scope.clusters && $scope.clusters.length > 0) ? 
$scope.clusters[0].Clusters.cluster_name : null, // only support one cluster at 
the moment
-          repos = [],
-          processedRepos = 0;
+        // only support one cluster at the moment
+        var clusterName = $scope.cluster && 
$scope.cluster.Clusters.cluster_name;
         if (clusterName) {
-          angular.forEach(allRepos, function (repo) {
-            Cluster.getRepoVersionStatus(clusterName, repo.id).then(function 
(response) {
-              repo.cluster = (response.status == 'current' || response.status 
== 'installed') ? clusterName : '';
-              if (!$scope.filter.cluster.current.value || repo.cluster) {
+          $scope.repos = allRepos;
+          $scope.tableInfo.total = allRepos.length;
+          angular.forEach($scope.repos, function (repo) {
+            calls.push(Cluster.getRepoVersionStatus(clusterName, 
repo.id).then(function (response) {
+              repo.cluster = (response.status === 'CURRENT' || response.status 
=== 'INSTALLED') ? clusterName : '';
+              if (repo.cluster) {
                 repo.status = response.status;
                 repo.totalHosts = response.totalHosts;
                 repo.currentHosts = response.currentHosts;
                 repo.installedHosts = response.installedHosts;
                 repo.stackVersionId = response.stackVersionId;
-                repos.push(repo);
               }
-              processedRepos++;
-              if (processedRepos === allRepos.length) {
-                var from = ($scope.pagination.currentPage - 1) * 
$scope.pagination.itemsPerPage;
-                var to = (repos.length - from > 
$scope.pagination.itemsPerPage) ? from + $scope.pagination.itemsPerPage : 
repos.length;
-                $scope.repos = repos.slice(from, to);
-                $scope.tableInfo.total = repos.length;
-                $scope.pagination.totalRepos = repos.length;
-                $scope.tableInfo.showed = to - from;
-              }
-            });
+            }));
           });
         }
       } else {
@@ -114,101 +116,52 @@ angular.module('ambariAdminConsole')
         $scope.pagination.totalRepos = 0;
         $scope.tableInfo.showed = 0;
       }
+      $scope.tableInfo.total = $scope.repos.length;
+      return $q.all(calls);
     };
 
     $scope.fetchRepos = function () {
-      return Stack.allRepos($scope.filter).then(function (repos) {
+      return Stack.allRepos().then(function (repos) {
         $scope.isLoading = false;
         return repos.items;
       });
     };
 
-    $scope.fillClusters = function (clusters) {
-      $scope.dropDownClusters = [].concat(clusters);
-      var options = [{label: $t('common.all'), value: ''}];
-      angular.forEach(clusters, function (cluster) {
-        options.push({
-          label: cluster.Clusters.cluster_name,
-          value: cluster.Clusters.cluster_name
-        });
-      });
-      $scope.filter.cluster.options = options;
-      if (!$scope.filter.cluster.current) {
-        $scope.filter.cluster.current = options[0];
-      }
-    };
-
     $scope.fetchClusters = function () {
       return Cluster.getAllClusters().then(function (clusters) {
         if (clusters && clusters.length > 0) {
-          $scope.clusters = clusters;
-          $scope.fillClusters(clusters);
-        }
-      });
-    };
-
-    $scope.fetchStacks = function () {
-      return Stack.allStackVersions().then(function (clusters) {
-        if (clusters && clusters.length > 0) {
-          $scope.stacks = clusters;
-          $scope.fillStacks(clusters);
-        }
-      });
-    };
-
-    $scope.fillStacks = function() {
-      var options = [{label: $t('common.all'), value: ''}];
-      angular.forEach($scope.stacks, function (stack) {
-        if (stack.active) {
-          options.push({
-            label: stack.displayName,
-            value: stack.displayName
-          });
+          $scope.dropDownClusters = clusters;
         }
       });
-      $scope.filter.stack.options = options;
-      if (!$scope.filter.stack.current) {
-        $scope.filter.stack.current = options[0];
-      }
     };
 
     $scope.loadAllData = function () {
       $scope.isLoading = true;
-      $scope.fetchStacks()
-        .then(function () {
-          return $scope.fetchClusters();
-        })
-        .then(function () {
-          return $scope.fetchRepos();
-        })
+      $scope.fetchRepos()
         .then(function (repos) {
-          $scope.fetchRepoClusterStatus(repos);
+          $scope.fetchClusters();
+          $scope.fetchRepoClusterStatus(repos).then(function() {
+            Filters.initFilterOptions($scope.filters, $scope.repos);
+          });
+          $scope.filterRepos();
         });
     };
 
     $scope.loadAllData();
 
-    $scope.$watch('filter', function (filter) {
-      $scope.isNotEmptyFilter = Boolean(filter.name
-        || filter.version
-        || filter.type
-        || (filter.cluster.current && filter.cluster.current.value)
-        || (filter.stack.current && filter.stack.current.value));
-    }, true);
-
     $scope.toggleVisibility = function (repo) {
       repo.isProccessing = true;
       var payload = {
-        RepositoryVersions:{
+        RepositoryVersions: {
           hidden: repo.hidden
         }
-      }
-      Stack.updateRepo(repo.stack_name, repo.stack_version, repo.id, 
payload).then( null, function () {
+      };
+      Stack.updateRepo(repo.stack_name, repo.stack_version, repo.id, 
payload).then(null, function () {
         repo.hidden = !repo.hidden;
-      }).finally( function () {
+      }).finally(function () {
         delete repo.isProccessing;
       });
-    }
+    };
 
     $scope.isHideCheckBoxEnabled = function ( repo ) {
       return !repo.isProccessing && ( !repo.cluster || repo.isPatch && ( 
repo.status === 'installed' || repo.status === 'install_failed') );

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupsListCtrl.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupsListCtrl.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupsListCtrl.js
index 61b5282..f2a6f67 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupsListCtrl.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/GroupsListCtrl.js
@@ -19,8 +19,8 @@
 
 angular.module('ambariAdminConsole')
 .controller('GroupsListCtrl',
-['$scope', 'Group', '$modal', 'ConfirmationModal', '$rootScope', '$translate', 
'Settings', 'Cluster', 'View', 'Alert',
-function($scope, Group, $modal, ConfirmationModal, $rootScope, $translate, 
Settings, Cluster, View, Alert) {
+['$scope', 'Group', '$modal', 'ConfirmationModal', '$rootScope', '$translate', 
'Settings', 'Cluster', 'View', 'Alert', 'Pagination', 'Filters',
+function($scope, Group, $modal, ConfirmationModal, $rootScope, $translate, 
Settings, Cluster, View, Alert, Pagination, Filters) {
   var $t = $translate.instant;
   $scope.constants = {
     groups: $t('common.groups').toLowerCase()
@@ -28,77 +28,57 @@ function($scope, Group, $modal, ConfirmationModal, 
$rootScope, $translate, Setti
   $scope.minRowsToShowPagination = Settings.minRowsToShowPagination;
   $scope.isLoading = false;
   $scope.groups = [];
-
-  $scope.groupsPerPage = 10;
-  $scope.currentPage = 1;
-  $scope.totalGroups = 0;
-  $scope.filter = {
-    name: '',
-    type: null
-  };
-  $scope.maxVisiblePages=20;
   $scope.tableInfo = {
+    filtered: 0,
     total: 0,
     showed: 0
   };
-  $scope.isNotEmptyFilter = true;
+  $scope.pagination = Pagination.create();
+  $scope.filters = [
+    {
+      key: 'group_name',
+      label: $t('groups.name'),
+      options: []
+    },
+    {
+      key: 'groupTypeName',
+      label: $t('common.type'),
+      options: []
+    }
+  ];
+
+  $scope.resetPagination = function() {
+    $scope.pagination.resetPagination($scope.groups, $scope.tableInfo);
+  };
 
   $scope.pageChanged = function() {
-    loadGroups();
+    $scope.pagination.pageChanged($scope.groups, $scope.tableInfo);
   };
-  $scope.groupsPerPageChanges = function() {
-    loadGroups();
+
+  $scope.filterGroups = function(appliedFilters) {
+    $scope.tableInfo.filtered = Filters.filterItems(appliedFilters, 
$scope.groups, $scope.filters);
+    $scope.pagination.resetPagination($scope.groups, $scope.tableInfo);
   };
 
-  $scope.resetPagination = function() {
-    $scope.currentPage = 1;
-    loadGroups();
+  $scope.toggleSearchBox = function() {
+    $('.search-box-button .popup-arrow-up, 
.search-box-row').toggleClass('hide');
   };
 
-  function loadGroups(){
+  $scope.loadGroups = function() {
     $scope.isLoading = true;
-    Group.all({
-      currentPage: $scope.currentPage, 
-      groupsPerPage: $scope.groupsPerPage, 
-      searchString: $scope.filter.name,
-      group_type: $scope.filter.type.value
-    }).then(function(groups) {
+    Group.all().then(function(groups) {
       $scope.isLoading = false;
-      $scope.totalGroups = groups.itemTotal;
       $scope.groups = groups.map(Group.makeGroup);
-      $scope.tableInfo.total = groups.itemTotal;
-      $scope.tableInfo.showed = groups.length;
+      $scope.tableInfo.total = $scope.groups.length;
+      Filters.initFilterOptions($scope.filters, $scope.groups);
+      $scope.filterGroups();
     })
     .catch(function(data) {
       Alert.error($t('groups.alerts.getGroupsListError'), data.data.message);
     });
-  }
-
-  $scope.typeFilterOptions = [{ label: $t('common.all'), value: '*'}]
-    .concat(Object.keys(Group.getTypes()).map(function(key) {
-      return {
-        label: $t(Group.getTypes()[key].LABEL_KEY),
-        value: Group.getTypes()[key].VALUE
-      };
-  }));
-  $scope.filter.type = $scope.typeFilterOptions[0];
-
-  $scope.clearFilters = function () {
-    $scope.filter.name = '';
-    $scope.filter.type = $scope.typeFilterOptions[0];
-    $scope.resetPagination();
   };
-  
-  loadGroups();
 
-  $scope.$watch(
-    function (scope) {
-      return Boolean(scope.filter.name || (scope.filter.type && 
scope.filter.type.value !== '*'));
-    },
-    function (newValue, oldValue, scope) {
-      scope.isNotEmptyFilter = newValue;
-    }
-  );
+  $scope.loadGroups();
 
   $rootScope.$watch(function(scope) {
     return scope.LDAPSynced;

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UsersListCtrl.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UsersListCtrl.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UsersListCtrl.js
index 00bf9c3..7982f0b 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UsersListCtrl.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/controllers/userManagement/UsersListCtrl.js
@@ -19,95 +19,105 @@
 
 angular.module('ambariAdminConsole')
 .controller('UsersListCtrl',
-['$scope', 'User', '$modal', '$rootScope', 'UserConstants', '$translate', 
'Cluster', 'View', 'ConfirmationModal', 'Settings',
-function($scope, User, $modal, $rootScope, UserConstants, $translate, Cluster, 
View, ConfirmationModal, Settings) {
+['$scope', 'User', '$modal', '$rootScope', 'UserConstants', '$translate', 
'Cluster', 'View', 'ConfirmationModal', 'Settings', 'Pagination', 'Filters',
+function($scope, User, $modal, $rootScope, UserConstants, $translate, Cluster, 
View, ConfirmationModal, Settings, Pagination, Filters) {
   var $t = $translate.instant;
   $scope.constants = {
     admin: $t('users.ambariAdmin'),
     users: $t('common.users').toLowerCase()
   };
+  $scope.users = [];
   $scope.minRowsToShowPagination = Settings.minRowsToShowPagination;
   $scope.isLoading = false;
-  $scope.users = [];
-  $scope.usersPerPage = 10;
-  $scope.currentPage = 1;
-  $scope.totalUsers = 0;
-  $scope.filters = {
-    name: '',
-    status: null,
-    type: null
-  };
-  $scope.maxVisiblePages = 20;
+  $scope.pagination = Pagination.create();
   $scope.tableInfo = {
+    filtered: 0,
     total: 0,
     showed: 0
   };
-  $scope.isNotEmptyFilter = true;
+  $scope.filters = [
+    {
+      key: 'user_name',
+      label: $t('users.username'),
+      customValueConverter: function(item) {
+        return item.Users.user_name;
+      },
+      options: []
+    },
+    {
+      key: 'role',
+      label: $t('clusters.role'),
+      customValueConverter: function(item) {
+        return item.Users.roles[0] ? item.Users.roles[0].permission_label : '';
+      },
+      options: []
+    },
+    {
+      key: 'status',
+      label: $t('users.status'),
+      isStatic: true,
+      customValueConverter: function(item) {
+        return item.Users.active ? $t('users.active') : $t('users.inactive');
+      },
+      options: [
+        {
+          key: $t('users.active'),
+          label: $t('users.active')
+        },
+        {
+          key: $t('users.inactive'),
+          label: $t('users.inactive')
+        }
+      ]
+    },
+    {
+      key: 'type',
+      label: $t('common.type'),
+      customValueConverter: function(item) {
+        return item.Users.userTypeName;
+      },
+      options: []
+    },
+    {
+      key: 'group',
+      label: $t('common.group'),
+      isMultiple: true,
+      customValueConverter: function(item) {
+        return item.Users.groups;
+      },
+      options: []
+    }
+  ];
 
   function loadUsers() {
     $scope.isLoading = true;
-    User.list({
-      currentPage: $scope.currentPage,
-      usersPerPage: $scope.usersPerPage,
-      searchString: $scope.filters.name,
-      user_type: $scope.filters.type.value,
-      active: $scope.filters.status.value
-    }).then(function (data) {
-      $scope.totalUsers = data.data.itemTotal;
+    User.list().then(function (data) {
       $scope.users = data.data.items.map(User.makeUser);
-      $scope.tableInfo.showed = data.data.items.length;
-      $scope.tableInfo.total = data.data.itemTotal;
+      $scope.tableInfo.total = $scope.users.length;
+      $scope.filterUsers();
+      Filters.initFilterOptions($scope.filters, $scope.users);
     }).finally(function () {
       $scope.isLoading = false;
     });
   }
 
-  $scope.pageChanged = function () {
-    loadUsers();
+  $scope.toggleSearchBox = function() {
+    $('.search-box-button .popup-arrow-up, 
.search-box-row').toggleClass('hide');
   };
-  $scope.usersPerPageChanges = function () {
-    $scope.resetPagination();
+
+  $scope.pageChanged = function () {
+    $scope.pagination.pageChanged($scope.users, $scope.tableInfo);
   };
 
   $scope.resetPagination = function () {
-    $scope.currentPage = 1;
-    loadUsers();
+    $scope.pagination.resetPagination($scope.users, $scope.tableInfo);
   };
 
-  $scope.activeFilterOptions = [
-    {label: $t('common.all'), value: '*'},
-    {label: $t('users.active'), value: true},
-    {label: $t('users.inactive'), value: false}
-  ];
-  $scope.filters.status = $scope.activeFilterOptions[0];
-
-  $scope.typeFilterOptions = [{label: $t('common.all'), value: '*'}]
-    .concat(Object.keys(UserConstants.TYPES).map(function (key) {
-      return {
-        label: $t(UserConstants.TYPES[key].LABEL_KEY),
-        value: UserConstants.TYPES[key].VALUE
-      };
-    }));
-
-  $scope.filters.type = $scope.typeFilterOptions[0];
-
-  $scope.clearFilters = function () {
-    $scope.filters.name = '';
-    $scope.filters.type = $scope.typeFilterOptions[0];
-    $scope.filters.status = $scope.activeFilterOptions[0];
-    $scope.resetPagination();
+  $scope.filterUsers = function(appliedFilters) {
+    $scope.tableInfo.filtered = Filters.filterItems(appliedFilters, 
$scope.users, $scope.filters);
+    $scope.pagination.resetPagination($scope.users, $scope.tableInfo);
   };
 
-  $scope.$watch(
-    function (scope) {
-      return Boolean(scope.filters.name || (scope.filters.status && 
scope.filters.status.value !== '*')
-        || (scope.filters.type && scope.filters.type.value !== '*'));
-    },
-    function (newValue, oldValue, scope) {
-      scope.isNotEmptyFilter = newValue;
-    }
-  );
-
   $rootScope.$watch(function (scope) {
     return scope.LDAPSynced;
   }, function (LDAPSynced) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/directives/comboSearch.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/directives/comboSearch.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/directives/comboSearch.js
index af25167..fc58eae 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/directives/comboSearch.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/directives/comboSearch.js
@@ -169,10 +169,8 @@ angular.module('ambariAdminConsole')
           return !(option.key === '' || option.key === undefined || 
appliedOptions[option.key])
             && (!filter.searchOptionInput || 
option.label.toLowerCase().indexOf(filter.searchOptionInput.toLowerCase()) !== 
-1);
         });
+        resetActive(filter.filteredOptions);
         filter.showAutoComplete = filter.filteredOptions.length > 0;
-        if (filter.filteredOptions.length > 0) {
-          $scope.makeActive(filter.filteredOptions[0], filter.filteredOptions);
-        }
       };
 
       $scope.extractFilters = function(filters) {
@@ -247,7 +245,13 @@ angular.module('ambariAdminConsole')
             return i;
           }
         }
-        return 0;
+        return -1;
+      }
+
+      function resetActive(array) {
+        array.forEach(function(item) {
+          item.active = false;
+        });
       }
 
       function focusInput(filter) {
@@ -282,6 +286,10 @@ angular.module('ambariAdminConsole')
             leftArrowKeyHandler();
             $scope.$apply();
           }
+          if (event.which === 27) { // "Escape" key
+            $scope.showAutoComplete = false;
+            $scope.$apply();
+          }
         });
       }
 
@@ -341,7 +349,14 @@ angular.module('ambariAdminConsole')
           if (activeAppliedFilters.length > 0) {
             var filteredOptions = activeAppliedFilters[0].filteredOptions;
             activeIndex = findActiveByProperty(filteredOptions);
-            nextIndex = (activeIndex < filteredOptions.length - 1) ? 
activeIndex + 1 : 0;
+            if (activeIndex < filteredOptions.length - 1) {
+              nextIndex = activeIndex + 1;
+            } else {
+              //switch to input of option
+              nextIndex = null;
+              resetActive(filteredOptions);
+              focusInput(activeAppliedFilters[0]);
+            }
           }
           if (nextIndex !== null) {
             $scope.makeActive(filteredOptions[nextIndex], filteredOptions);
@@ -374,7 +389,16 @@ angular.module('ambariAdminConsole')
           if (activeAppliedFilters.length > 0) {
             var filteredOptions = activeAppliedFilters[0].filteredOptions;
             activeIndex = findActiveByProperty(filteredOptions);
-            nextIndex = (activeIndex > 0) ?  activeIndex - 1 : 
filteredOptions.length - 1;
+            if (activeIndex > 0) {
+              nextIndex = activeIndex - 1;
+            } else if (activeIndex === 0) {
+              //switch to input of option
+              nextIndex = null;
+              resetActive(filteredOptions);
+              focusInput(activeAppliedFilters[0]);
+            } else {
+              nextIndex = filteredOptions.length - 1;
+            }
           }
           if (nextIndex !== null) {
             $scope.makeActive(filteredOptions[nextIndex], filteredOptions);
@@ -401,7 +425,8 @@ angular.module('ambariAdminConsole')
             if (activeOptions.length > 0) {
               $scope.selectOption(null, activeOptions[0], 
activeAppliedFilters[0]);
             }
-          } else {
+          }
+          if (activeAppliedFilters.length === 0 || activeOptions.length === 0) 
{
             $scope.appliedFilters.filter(function(item) {
               return !item.currentOption;
             }).forEach(function(item) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Cluster.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Cluster.js 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Cluster.js
index 30ef91a..1a99a65 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Cluster.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Cluster.js
@@ -292,8 +292,7 @@ angular.module('ambariAdminConsole')
           angular.forEach(hostStatus, function(status) {
             totalHosts += status.length;
           });
-          response.status = currentHosts > 0? 'current' :
-                            installedHosts > 0? 'installed' : '';
+          response.status = data[0].ClusterStackVersions.state;
           response.currentHosts = currentHosts;
           response.installedHosts = installedHosts;
           response.totalHosts = totalHosts;

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Filters.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Filters.js 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Filters.js
new file mode 100644
index 0000000..7c6815f
--- /dev/null
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Filters.js
@@ -0,0 +1,84 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+'use strict';
+
+angular.module('ambariAdminConsole')
+.factory('Filters', function() {
+
+  function initFilterOptions(filters, items) {
+    filters.filter(function(filter) {
+      return !filter.isStatic;
+    }).forEach(function(filter) {
+      var preOptions = [];
+      if (filter.isMultiple) {
+        items.forEach(function(item) {
+          if (typeof filter.customValueConverter === 'function') {
+            preOptions = preOptions.concat(filter.customValueConverter(item));
+          } else {
+            preOptions = preOptions.concat(item[filter.key]);
+          }
+        });
+      } else {
+        preOptions = items.map(function(item) {
+          if (typeof filter.customValueConverter === 'function') {
+            return filter.customValueConverter(item);
+          }
+          return item[filter.key];
+        });
+      }
+      filter.options = $.unique(preOptions).filter(function(item) {
+        return item !== undefined && item !== null;
+      }).map(function(item) {
+        return {
+          key: item,
+          label: item
+        }
+      });
+    });
+  }
+
+  function filterItems(appliedFilters, items, filterDefinitions) {
+    var filteredCount = 0;
+    angular.forEach(items, function(item) {
+      item.isFiltered = !(appliedFilters && appliedFilters.length > 0 && 
appliedFilters.some(function(filter) {
+        var customValueFilter = 
filterDefinitions.filter(function(filterDefinition) {
+          return filterDefinition.key === filter.key && typeof 
filterDefinition.customValueConverter === 'function';
+        })[0];
+        if (customValueFilter) {
+          return filter.values.every(function(value) {
+            var itemValue = customValueFilter.customValueConverter(item);
+            return String(Array.isArray(itemValue) ? itemValue.join() : 
itemValue).indexOf(value) === -1;
+          });
+        }
+        return filter.values.every(function(value) {
+          var itemValue = item[filter.key];
+          return String(Array.isArray(itemValue) ? itemValue.join() : 
itemValue).indexOf(value) === -1;
+
+        });
+      }));
+
+      filteredCount += ~~item.isFiltered;
+    });
+    return filteredCount;
+  }
+
+  return {
+    initFilterOptions: initFilterOptions,
+    filterItems: filterItems
+  };
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Group.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Group.js 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Group.js
index 0509e11..dc6d351 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Group.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Group.js
@@ -100,18 +100,11 @@ angular.module('ambariAdminConsole')
     return $http.post(Settings.baseUrl + '/groups/' + groupName + 
'/members/'+memberName);
   };
 
-  Group.all = function(params) {
+  Group.all = function() {
     var deferred = $q.defer();
 
-    $http.get(Settings.baseUrl + '/groups?'
-      + 'Groups/group_name.matches(.*'+params.searchString+'.*)'
-      + '&fields=*'
-      + '&from='+ (params.currentPage-1)*params.groupsPerPage
-      + '&page_size=' + params.groupsPerPage
-      + (params.group_type === '*' ? '' : '&Groups/group_type=' + 
params.group_type)
-    )
+    $http.get(Settings.baseUrl + '/groups?fields=*')
     .success(function(data) {
-      data.items.itemTotal = data.itemTotal;
       deferred.resolve(data.items);
     })
     .error(function(data) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Pagination.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Pagination.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Pagination.js
new file mode 100644
index 0000000..ac0a8bc
--- /dev/null
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Pagination.js
@@ -0,0 +1,59 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+'use strict';
+
+angular.module('ambariAdminConsole')
+.factory('Pagination', function() {
+
+  function showItemsOnPage(items, tableInfo) {
+    var startIndex = (this.currentPage - 1) * this.itemsPerPage + 1;
+    var endIndex = this.currentPage * this.itemsPerPage;
+    var showedCount = 0;
+    var filteredCount = 0;
+
+    angular.forEach(items, function (item) {
+      item.isShowed = false;
+      if (item.isFiltered) {
+        filteredCount++;
+        if (filteredCount >= startIndex && filteredCount <= endIndex) {
+          item.isShowed = true;
+          showedCount++;
+        }
+      }
+    });
+    tableInfo.showed = showedCount;
+  }
+
+  return {
+    create: function(options) {
+      options = options || {};
+      return {
+        itemsPerPage: options.itemsPerPage || 10,
+        currentPage: options.currentPage || 1,
+        maxVisiblePages: options.maxVisiblePages || 10,
+        pageChanged: function(items, tableInfo) {
+          showItemsOnPage.call(this, items, tableInfo);
+        },
+        resetPagination: function(items, tableInfo) {
+          this.currentPage = 1;
+          showItemsOnPage.call(this, items, tableInfo);
+        }
+      }
+    }
+  };
+});

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/RemoteCluster.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/RemoteCluster.js
 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/RemoteCluster.js
index 49c6abc..86407fd 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/RemoteCluster.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/RemoteCluster.js
@@ -80,16 +80,10 @@ angular.module('ambariAdminConsole')
         return deferred.promise;
     }
 
-    RemoteCluster.all = function(params) {
+    RemoteCluster.all = function() {
       var deferred = $q.defer();
 
-      $http.get(Settings.baseUrl + "/remoteclusters?"
-          + 'ClusterInfo/name.matches(.*'+params.searchString+'.*)'
-          + '&fields=*'
-          + '&from='+ (params.currentPage-1)*params.groupsPerPage
-          + '&page_size=' + params.groupsPerPage
-          + (params.service === 'Any' ? '' : 
'&ClusterInfo/services.matches(.*'+params.service+'.*)')
-        )
+      $http.get(Settings.baseUrl + "/remoteclusters")
         .success(function(response) {
           deferred.resolve(response);
         })

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Stack.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Stack.js 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Stack.js
index f52e687..4f111fe 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Stack.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/Stack.js
@@ -189,28 +189,8 @@ angular.module('ambariAdminConsole')
       }
     },
 
-    allRepos: function (filter, pagination) {
-      var versionFilter = filter.version;
-      var nameFilter = filter.name;
-      var typeFilter = filter.type;
-      var stackFilter = filter.stack && filter.stack.current && 
filter.stack.current.value;
+    allRepos: function () {
       var url = 
'/stacks?fields=versions/repository_versions/RepositoryVersions';
-      if (versionFilter) {
-        url += 
'&versions/repository_versions/RepositoryVersions/repository_version.matches(.*'
 + versionFilter + '.*)';
-      }
-      if (nameFilter) {
-        url += 
'&versions/repository_versions/RepositoryVersions/display_name.matches(.*' + 
nameFilter + '.*)';
-      }
-      if (typeFilter){
-        url += 
'&versions/repository_versions/RepositoryVersions/type.matches(.*' + 
typeFilter.toUpperCase() + '.*)';
-      }
-      if (stackFilter) {
-        var stack = filter.stack.current.value.split('-'),
-          stackNameFilter = stack[0],
-          stackVersionFilter = stack[1];
-        url += '&versions/repository_versions/RepositoryVersions/stack_name=' 
+ stackNameFilter;
-        url += 
'&versions/repository_versions/RepositoryVersions/stack_version=' + 
stackVersionFilter;
-      }
       var deferred = $q.defer();
       $http.get(Settings.baseUrl + url, {mock: 'version/versions.json'})
       .success(function (data) {
@@ -230,16 +210,8 @@ angular.module('ambariAdminConsole')
         });
         // prepare response data with client side pagination
         var response = {};
+        response.items = repos;
         response.itemTotal = repos.length;
-        if (pagination) {
-          var from = (pagination.currentPage - 1) * pagination.itemsPerPage;
-          var to = (repos.length - from > pagination.itemsPerPage)? from + 
pagination.itemsPerPage : repos.length;
-          response.items = repos.slice(from, to);
-          response.showed = to - from;
-        } else {
-          response.items = repos;
-          response.showed = repos.length;
-        }
         deferred.resolve(response);
       })
       .error(function (data) {

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js 
b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js
index 7932d9b..5ed692a 100644
--- a/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js
+++ b/ambari-admin/src/main/resources/ui/admin-web/app/scripts/services/User.js
@@ -33,17 +33,8 @@ angular.module('ambariAdminConsole')
   var $t = $translate.instant;
 
   return {
-    list: function(params) {
-      return $http.get(
-        Settings.baseUrl + '/users/?'
-        + 'Users/user_name.matches(.*'+params.searchString+'.*)'
-        + '&fields=privileges/PrivilegeInfo/*,Users'
-        + '&from=' + (params.currentPage-1)*params.usersPerPage
-        + '&page_size=' + params.usersPerPage
-        + (params.user_type === '*' ? '' : '&Users/user_type=' + 
params.user_type)
-        + (params.active === '*' ? '' : '&Users/active=' + params.active)
-        + (params.admin ? '&Users/admin=true' : '')
-      );
+    list: function() {
+      return $http.get(Settings.baseUrl + 
'/users?fields=Users/*,privileges/*');
     },
     listByName: function(name) {
       return $http.get(

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/styles/user-management.css
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/styles/user-management.css 
b/ambari-admin/src/main/resources/ui/admin-web/app/styles/user-management.css
index 3c9756e..bca53ca 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/styles/user-management.css
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/styles/user-management.css
@@ -20,6 +20,10 @@
   vertical-align: baseline;
 }
 
+#user-management .search-box-row {
+  margin-top: -1px;
+}
+
 #user-management .nav.nav-tabs {
   margin-bottom: 0;
 }

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/viewsList.html
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/viewsList.html
 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/viewsList.html
index 9e9cb55..48f445b 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/viewsList.html
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/ambariViews/viewsList.html
@@ -115,13 +115,12 @@
     <div class="col-sm-12 table-bar" ng-show="instances.length >= 
minInstanceForPagination">
         <div class="pull-left filtered-info">
             <span>{{'common.filterInfo' | translate:{showed: tableInfo.showed, 
total: tableInfo.filtered, term: urs.urls} }}</span>
-            <span ng-show="isNotEmptyFilter">- <a href 
ng-click="clearFilters()">{{'common.controls.clearFilters' | 
translate}}</a></span>
         </div>
         <div class="pull-right left-margin">
-            <pagination class="paginator" total-items="tableInfo.filtered" 
max-size="maxVisiblePages" items-per-page="instancesPerPage" 
ng-model="currentPage" ng-change="pageChanged()"></pagination>
+            <pagination class="paginator" total-items="tableInfo.filtered" 
max-size="pagination.maxVisiblePages" items-per-page="pagination.itemsPerPage" 
ng-model="pagination.currentPage" ng-change="pageChanged()"></pagination>
         </div>
         <div class="pull-right">
-            <select class="form-control" ng-model="instancesPerPage" 
ng-change="resetPagination()" ng-options="currOption for currOption in [10, 25, 
50, 100]"></select>
+            <select class="form-control" ng-model="pagination.itemsPerPage" 
ng-change="resetPagination()" ng-options="currOption for currOption in [10, 25, 
50, 100]"></select>
         </div>
     </div>
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/list.html
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/list.html
 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/list.html
index 7a8e6f4..5f8a0be 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/list.html
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/remoteClusters/list.html
@@ -23,30 +23,34 @@
         {{'views.registerRemoteCluster' | translate}}
       </a>
     </div>
+    <div class="search-box-button pull-right">
+      <button class="btn btn-default" ng-click="toggleSearchBox()">
+        <i class="fa fa-filter" aria-hidden="true"></i>
+      </button>
+      <div class="popup-arrow-up hide"></div>
+    </div>
+  </div>
+
+  <div class="search-box-row hide">
+    <combo-search suggestions="filters" filter-change="filterClusters" 
placeholder="Search"></combo-search>
   </div>
+
   <table class="table table-striped table-hover">
     <thead>
       <tr>
         <th class="col-sm-3">
           <div class="search-container">
-            <label for="">{{'views.clusterName' | translate}}</label>
-            <input type="text" class="form-control namefilter" 
placeholder="{{'common.any' | translate}}" ng-model="currentNameFilter" 
ng-change="resetPagination()">
-            <button type="button" class="close" ng-show="currentNameFilter" 
ng-click="currentNameFilter=''; resetPagination()"><span 
aria-hidden="true">&times;</span><span 
class="sr-only">{{'common.controls.close' | translate}}</span></button>
+            <label>{{'views.clusterName' | translate}}</label>
           </div>
         </th>
         <th class="col-sm-9">
-          <label for="">{{'common.services' | translate}}</label>
-          <select class="form-control typefilter"
-                  ng-model="currentTypeFilter"
-                  ng-options="item for item in typeFilterOptions"
-                  ng-change="resetPagination();">
-          </select>
+          <label>{{'common.services' | translate}}</label>
         </th>
       </tr>
     </thead>
     <tbody>
-    <tr ng-repeat="remoteCluster in remoteClusters" >
-      <td class="col-sm-3"><a 
href="#/remoteClusters/{{remoteCluster.ClusterInfo.name}}/edit">{{ 
remoteCluster.ClusterInfo.name }}</a></td>
+    <tr ng-repeat="remoteCluster in remoteClusters  | filter : { isShowed: 
true }">
+      <td class="col-sm-3"><a 
href="#/remoteClusters/{{remoteCluster.ClusterInfo.name}}/edit">{{ 
remoteCluster.clusterName }}</a></td>
       <td class="col-sm-9">
         <span ng-repeat="service in remoteCluster.ClusterInfo.services" 
ng-if="remoteCluster.ClusterInfo.services.length > 0">{{ service }}{{$last ? '' 
: ','}} </span>
         <span ng-if="remoteCluster.ClusterInfo.services.length == 0">--</span>
@@ -58,19 +62,18 @@
   <div ng-if="isLoading" class="spinner-container">
     <i class="fa fa-2x fa-spinner fa-spin" aria-hidden="true"></i>
   </div>
-  <div class="alert empty-table-alert col-sm-12" 
ng-show="!remoteClusters.length && !isLoading">
+  <div class="alert empty-table-alert col-sm-12" ng-show="!tableInfo.showed && 
!isLoading">
     {{'common.alerts.noRemoteClusterDisplay' | translate}}
   </div>
   <div class="col-sm-12 table-bar" ng-show="tableInfo.total >= 
minInstanceForPagination">
     <div class="pull-left filtered-info">
       <span>{{'common.filterInfo' | translate:{showed: tableInfo.showed, 
total: tableInfo.total, term: constants.groups} }}</span>
-      <span ng-show="isNotEmptyFilter">- <a href 
ng-click="clearFilters()">{{'common.controls.clearFilters' | 
translate}}</a></span>
     </div>
     <div class="pull-right left-margin">
-      <pagination class="paginator" total-items="totalGroups" 
max-size="maxVisiblePages" items-per-page="groupsPerPage" 
ng-model="currentPage" ng-change="pageChanged()"></pagination>
+      <pagination class="paginator" total-items="tableInfo.filtered" 
max-size="pagination.maxVisiblePages" items-per-page="groupsPerPage" 
ng-model="pagination.currentPage" ng-change="pageChanged()"></pagination>
     </div>
     <div class="pull-right">
-      <select class="form-control" ng-model="groupsPerPage" 
ng-change="groupsPerPageChanges()" ng-options="currOption for currOption in 
[10, 25, 50, 100]"></select>
+      <select class="form-control" ng-model="pagination.itemsPerPage" 
ng-change="resetPagination()" ng-options="currOption for currOption in [10, 25, 
50, 100]"></select>
     </div>
   </div>
 

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/views/stackVersions/list.html
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/stackVersions/list.html
 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/stackVersions/list.html
index 9d81543..697b3b2 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/stackVersions/list.html
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/stackVersions/list.html
@@ -23,47 +23,42 @@
         {{'versions.register.title' | translate}}
       </a>
     </div>
+    <div class="search-box-button pull-right">
+      <button class="btn btn-default" ng-click="toggleSearchBox()">
+        <i class="fa fa-filter" aria-hidden="true"></i>
+      </button>
+      <div class="popup-arrow-up hide"></div>
+    </div>
+  </div>
+
+  <div class="search-box-row hide">
+    <combo-search suggestions="filters" filter-change="filterRepos" 
placeholder="Search"></combo-search>
   </div>
+
   <table class="table table-striped table-hover">
     <thead>
     <tr>
       <th class="col-small">
         <label>{{'common.stack' | translate}}</label>
-        <select class="form-control"
-                ng-change="resetPagination()"
-                ng-model="filter.stack.current"
-                ng-options="item.label for item in filter.stack.options track 
by item.value"
-          ></select>
       </th>
       <th class="col-medium text-search-container">
         <label>{{'common.name' | translate}}</label>
-        <input type="text" class="form-control" ng-change="resetPagination()" 
ng-model="filter.name" placeholder="{{'common.any' | translate}}">
-        <button type="button" class="close clearfilter" ng-show="filter.name" 
ng-click="filter.name=''; resetPagination()"><span 
aria-hidden="true">&times;</span><span 
class="sr-only">{{'common.controls.close' | translate}}</span></button>
       </th>
       <th class="col-medium text-search-container">
         <label>{{'common.type' | translate}}</label>
-        <input type="text" class="form-control" ng-change="resetPagination()" 
ng-model="filter.type" placeholder="{{'common.any' | translate}}">
-        <button type="button" class="close clearfilter" ng-show="filter.type" 
ng-click="filter.type=''; resetPagination()"><span 
aria-hidden="true">&times;</span><span 
class="sr-only">{{'common.controls.close' | translate}}</span></button>
       </th>
       <th class="col-medium text-search-container">
         <label>{{'common.version' | translate}}</label>
-        <input type="text" class="form-control" ng-change="resetPagination()" 
ng-model="filter.version" placeholder="{{'common.any' | translate}}">
-        <button type="button" class="close clearfilter" 
ng-show="filter.version" ng-click="filter.version=''; resetPagination()"><span 
aria-hidden="true">&times;</span><span 
class="sr-only">{{'common.controls.close' | translate}}</span></button>
       </th>
       <th class="col-small">
         <label>{{'common.cluster' | translate}}</label>
-        <select class="form-control"
-                ng-change="resetPagination()"
-                ng-model="filter.cluster.current"
-                ng-options="item.label for item in filter.cluster.options 
track by item.value"
-          ></select>
       </th>
       <th class="col-small"></th>
       <th class="col-small text-center vertical-top">{{'common.hidden' | 
translate}}</th>
     </tr>
     </thead>
     <tbody>
-    <tr ng-repeat="repo in repos">
+    <tr ng-repeat="repo in repos | filter : { isShowed: true }">
       <td class="col-small">
         <span>{{repo.stack_name}}-{{repo.stack_version}}</span>
       </td>
@@ -120,17 +115,16 @@
   <div ng-if="isLoading" class="spinner-container">
     <i class="fa fa-2x fa-spinner fa-spin" aria-hidden="true"></i>
   </div>
-  <div class="alert empty-table-alert col-sm-12" ng-show="!repos.length && 
!isLoading">
+  <div class="alert empty-table-alert col-sm-12" ng-show="!tableInfo.showed && 
!isLoading">
     {{'common.alerts.nothingToDisplay' | translate:{term: 
getConstant("common.version")} }}
   </div>
   <div class="col-sm-12 table-bar" ng-show="tableInfo.total >= 
minInstanceForPagination">
     <div class="pull-left filtered-info">
       <span>{{'common.filterInfo' | translate:{showed: tableInfo.showed, 
total: tableInfo.total, term: getConstant("common.versions")} }}</span>
-      <span ng-show="isNotEmptyFilter">- <a href 
ng-click="clearFilters()">{{'common.controls.clearFilters' | 
translate}}</a></span>
     </div>
     <div class="pull-right left-margin">
       <pagination class="paginator"
-                  total-items="pagination.totalRepos"
+                  total-items="tableInfo.total"
                   max-size="pagination.maxVisiblePages"
                   items-per-page="pagination.itemsPerPage"
                   ng-model="pagination.currentPage"

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupsList.html
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupsList.html
 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupsList.html
index af8303d..4218a65 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupsList.html
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/groupsList.html
@@ -16,11 +16,24 @@
 * limitations under the License.
 -->
 <div class="groups-pane">
-  <div class="clearfix panel">
-    <button class="btn btn-default creategroup-btn pull-right" 
ng-click="createGroup()">
-      {{'groups.createLocal' | translate}}
-    </button>
+  <div class="clearfix">
+    <div class="pull-right">
+      <button class="btn btn-default creategroup-btn pull-right" 
ng-click="createGroup()">
+        {{'groups.createLocal' | translate}}
+      </button>
+    </div>
+    <div class="search-box-button pull-right">
+      <button class="btn btn-default" ng-click="toggleSearchBox()">
+        <i class="fa fa-filter" aria-hidden="true"></i>
+      </button>
+      <div class="popup-arrow-up hide"></div>
+    </div>
   </div>
+
+  <div class="search-box-row hide">
+    <combo-search suggestions="filters" filter-change="filterGroups" 
placeholder="Search"></combo-search>
+  </div>
+
   <table class="table table-striped table-hover col-sm-12">
     <thead>
       <tr>
@@ -37,26 +50,9 @@
           <span>{{'common.actions' | translate}}</span>
         </th>
       </tr>
-      <tr>
-        <th class="col-sm-6">
-          <div class="search-container">
-            <input type="text" class="form-control namefilter" 
placeholder="{{'common.any' | translate}}" ng-model="filter.name" 
ng-change="resetPagination()">
-            <button type="button" class="close" ng-show="filter.name" 
ng-click="filter.name=''; resetPagination()"><span 
aria-hidden="true">&times;</span><span 
class="sr-only">{{'common.controls.close' | translate}}</span></button>
-          </div>
-        </th>
-        <th class="col-sm-2">
-          <select class="form-control typefilter"
-                  ng-model="filter.type"
-                  ng-options="item.label for item in typeFilterOptions"
-                  ng-change="resetPagination();">
-          </select>
-        </th>
-        <th class="col-sm-2"></th>
-        <th class="col-sm-2"></th>
-      </tr>
     </thead>
     <tbody>
-      <tr ng-repeat="group in groups">
+      <tr ng-repeat="group in groups | filter : { isShowed: true }">
         <td class="col-sm-8">
           <span>{{group.group_name}}</span>
         </td>
@@ -76,19 +72,18 @@
   <div ng-if="isLoading" class="spinner-container">
     <i class="fa fa-2x fa-spinner fa-spin" aria-hidden="true"></i>
   </div>
-  <div class="alert empty-table-alert col-sm-12" ng-show="!groups.length && 
!isLoading">
+  <div class="alert empty-table-alert col-sm-12" ng-show="!tableInfo.showed && 
!isLoading">
     {{'common.alerts.nothingToDisplay' | translate:{term: constants.groups} }}
   </div>
-  <div class="col-sm-12 table-bar" ng-show="totalGroups > 
minRowsToShowPagination">
+  <div class="col-sm-12 table-bar" ng-show="tableInfo.total > 
minRowsToShowPagination">
     <div class="pull-left filtered-info">
-      <span>{{'common.filterInfo' | translate:{showed: tableInfo.showed, 
total: tableInfo.total, term: constants.groups} }}</span>
-      <span ng-show="isNotEmptyFilter">- <a href 
ng-click="clearFilters()">{{'common.controls.clearFilters' | 
translate}}</a></span>
+      <span>{{'common.filterInfo' | translate:{showed: tableInfo.showed, 
total: tableInfo.filtered, term: constants.groups} }}</span>
     </div>
     <div class="pull-right left-margin">
-      <pagination class="paginator" total-items="totalGroups" 
max-size="maxVisiblePages" items-per-page="groupsPerPage" 
ng-model="currentPage" ng-change="pageChanged()"></pagination>
+      <pagination class="paginator" total-items="tableInfo.filtered" 
max-size="pagination.maxVisiblePages" items-per-page="pagination.itemsPerPage" 
ng-model="pagination.currentPage" ng-change="pageChanged()"></pagination>
     </div>
     <div class="pull-right">
-      <select class="form-control" ng-model="groupsPerPage" 
ng-change="groupsPerPageChanges()" ng-options="currOption for currOption in 
[10, 25, 50, 100]"></select>
+      <select class="form-control" ng-model="pagination.itemsPerPage" 
ng-change="resetPagination()" ng-options="currOption for currOption in [10, 25, 
50, 100]"></select>
     </div>
   </div>
 </div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/usersList.html
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/usersList.html
 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/usersList.html
index 23e9ddb..2348dad 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/usersList.html
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/app/views/userManagement/usersList.html
@@ -17,14 +17,27 @@
 -->
 
 <div class="users-pane">
-  <div class="clearfix panel">
-    <button class="btn btn-default createuser-btn pull-right" 
ng-click="createUser();">
-      {{'users.create' | translate}}
-    </button>
+  <div class="clearfix">
+    <div class="pull-right">
+      <button class="btn btn-default createuser-btn pull-right" 
ng-click="createUser();">
+        {{'users.create' | translate}}
+      </button>
+    </div>
+    <div class="search-box-button pull-right">
+      <button class="btn btn-default" ng-click="toggleSearchBox()">
+        <i class="fa fa-filter" aria-hidden="true"></i>
+      </button>
+      <div class="popup-arrow-up hide"></div>
+    </div>
   </div>
+
+  <div class="search-box-row hide">
+    <combo-search suggestions="filters" filter-change="filterUsers" 
placeholder="Search"></combo-search>
+  </div>
+
   <table class="table table-striped table-hover">
     <thead>
-      <tr class="fix-bottom">
+      <tr>
         <th>
           <span>{{'users.username' | translate}}</span>
         </th>
@@ -44,36 +57,9 @@
           <span>{{'common.actions' | translate}}</span>
         </th>
       </tr>
-      <tr class="fix-top">
-        <th>
-          <div class="search-container">
-            <input type="text" class="form-control namefilter" 
placeholder="{{'common.any' | translate}}" ng-model="filters.name" 
ng-change="resetPagination()">
-            <button type="button" class="close clearfilter" 
ng-show="filters.name" ng-click="filters.name=''; resetPagination()">
-              <span aria-hidden="true">&times;</span><span 
class="sr-only">{{'common.controls.close' | translate}}</span>
-            </button>
-          </div>
-        </th>
-        <th></th>
-        <th>
-          <select class="form-control statusfilter"
-                  ng-model="filters.status"
-                  ng-options="item.label for item in activeFilterOptions"
-                  ng-change="resetPagination()">
-          </select>
-        </th>
-        <th>
-          <select class="form-control typefilter"
-                  ng-model="filters.type"
-                  ng-options="item.label for item in typeFilterOptions"
-                  ng-change="resetPagination()">
-          </select>
-        </th>
-        <th></th>
-        <th></th>
-      </tr>
     </thead>
     <tbody>
-      <tr ng-repeat="user in users">
+      <tr ng-repeat="user in users | filter : { isShowed: true }">
         <td>
           <span>{{user.Users.user_name}}</span>
         </td>
@@ -101,19 +87,18 @@
   <div ng-if="isLoading" class="spinner-container">
     <i class="fa fa-2x fa-spinner fa-spin" aria-hidden="true"></i>
   </div>
-  <div class="alert empty-table-alert col-sm-12" ng-show="!users.length && 
!isLoading">
+  <div class="alert empty-table-alert col-sm-12" ng-show="!tableInfo.showed && 
!isLoading">
     {{'common.alerts.nothingToDisplay' | translate:{term: constants.users} }}
   </div>
-  <div class="col-sm-12 table-bar" ng-show="totalUsers > 
minRowsToShowPagination">
+  <div class="col-sm-12 table-bar" ng-show="users.length > 
minRowsToShowPagination">
     <div class="pull-left filtered-info">
       <span>{{'common.filterInfo' | translate:{showed: tableInfo.showed, 
total: tableInfo.total, term: constants.users} }}</span>
-      <span ng-show="isNotEmptyFilter">- <a href 
ng-click="clearFilters()">{{'common.controls.clearFilters' | 
translate}}</a></span>
     </div>
     <div class="pull-right left-margin">
-      <pagination class="paginator" total-items="totalUsers" 
max-size="maxVisiblePages" items-per-page="usersPerPage" ng-model="currentPage" 
ng-change="pageChanged()"></pagination>
+      <pagination class="paginator" total-items="tableInfo.filtered" 
max-size="pagination.maxVisiblePages" items-per-page="pagination.itemsPerPage" 
ng-model="pagination.currentPage" ng-change="pageChanged()"></pagination>
     </div>
     <div class="pull-right">
-      <select class="form-control" ng-model="usersPerPage" 
ng-change="usersPerPageChanges()" ng-options="currOption for currOption in [10, 
25, 50, 100]"></select>
+      <select class="form-control" ng-model="pagination.itemsPerPage" 
ng-change="resetPagination()" ng-options="currOption for currOption in [10, 25, 
50, 100]"></select>
     </div>
   </div>
 </div>

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/ambariViews/ViewsListCtrl_test.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/ambariViews/ViewsListCtrl_test.js
 
b/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/ambariViews/ViewsListCtrl_test.js
index 362b94a..ca8205c 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/ambariViews/ViewsListCtrl_test.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/ambariViews/ViewsListCtrl_test.js
@@ -24,6 +24,9 @@ describe('#Cluster', function () {
       module('ambariAdminConsole');
       inject(function($rootScope, $controller) {
         scope = $rootScope.$new();
+        scope.pagination = {
+          resetPagination: angular.noop
+        };
         ctrl = $controller('ViewsListCtrl', {$scope: scope});
       });
       scope.instances = [
@@ -44,68 +47,9 @@ describe('#Cluster', function () {
       ];
     });
 
-    describe('#initFilterOptions()', function () {
-      beforeEach(function() {
-        scope.initFilterOptions();
-      });
-
-      it('should fill short_url_name options', function() {
-        expect(scope.filters[0].options).toEqual([
-          {
-            key: 'sun1',
-            label: 'sun1'
-          },
-          {
-            key: 'sun2',
-            label: 'sun2'
-          }
-        ]);
-      });
-
-      it('should fill url options', function() {
-        expect(scope.filters[1].options).toEqual([
-          {
-            key: '/main/view/vn1/su1',
-            label: '/main/view/vn1/su1'
-          },
-          {
-            key: '/main/view/vn2/su2',
-            label: '/main/view/vn2/su2'
-          }
-        ]);
-      });
-
-      it('should fill view_name options', function() {
-        expect(scope.filters[2].options).toEqual([
-          {
-            key: 'vn1',
-            label: 'vn1'
-          },
-          {
-            key: 'vn2',
-            label: 'vn2'
-          }
-        ]);
-      });
-
-      it('should fill instance_name options', function() {
-        expect(scope.filters[3].options).toEqual([
-          {
-            key: 'in1',
-            label: 'in1'
-          },
-          {
-            key: 'in2',
-            label: 'in2'
-          }
-        ]);
-      });
-    });
-
-
     describe('#filterInstances', function() {
       beforeEach(function() {
-        spyOn(scope, 'resetPagination');
+        spyOn(scope.pagination, 'resetPagination');
       });
 
       it('all should be filtered when filters not applied', function() {
@@ -117,7 +61,7 @@ describe('#Cluster', function () {
 
       it('resetPagination should be called', function() {
         scope.filterInstances();
-        expect(scope.resetPagination).toHaveBeenCalled();
+        expect(scope.pagination.resetPagination).toHaveBeenCalled();
       });
 
       it('one view should be filtered', function() {

http://git-wip-us.apache.org/repos/asf/ambari/blob/81c04545/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/GroupsListCtrl_test.js
----------------------------------------------------------------------
diff --git 
a/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/GroupsListCtrl_test.js
 
b/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/GroupsListCtrl_test.js
index 8d04757..1e76aec 100644
--- 
a/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/GroupsListCtrl_test.js
+++ 
b/ambari-admin/src/main/resources/ui/admin-web/test/unit/controllers/userManagement/GroupsListCtrl_test.js
@@ -20,110 +20,20 @@ describe('#Cluster', function () {
 
   describe('GroupsListCtrl', function() {
 
-    var scope, ctrl, $t, $httpBackend;
+    var $scope, ctrl, $t, $httpBackend, $group;
 
     beforeEach(module('ambariAdminConsole', function () {}));
 
-    beforeEach(inject(function($rootScope, $controller, _$translate_, 
_$httpBackend_) {
-      scope = $rootScope.$new();
+    beforeEach(inject(function($rootScope, $controller, _$translate_, 
_$httpBackend_, _Group_) {
+      $scope = $rootScope.$new();
       $t = _$translate_.instant;
       $httpBackend = _$httpBackend_;
+      $group = _Group_;
       ctrl = $controller('GroupsListCtrl', {
-        $scope: scope
+        $scope: $scope
       });
     }));
 
-    describe('#clearFilters()', function () {
-
-      it('should clear filters and reset pagination', function () {
-        scope.currentPage = 2;
-        scope.filter.name = 'a';
-        scope.filter.type = {
-          label: $t('common.local'),
-          value: false
-        };
-        scope.clearFilters();
-        expect(scope.filter.name).toEqual('');
-        expect(scope.filter.type).toEqual({
-          label: $t('common.all'),
-          value: '*'
-        });
-        expect(scope.currentPage).toEqual(1);
-      });
-
-    });
-
-    describe('#isNotEmptyFilter', function () {
-
-      var cases = [
-        {
-          currentNameFilter: '',
-          currentTypeFilter: null,
-          isNotEmptyFilter: false,
-          title: 'no filters'
-        },
-        {
-          currentNameFilter: '',
-          currentTypeFilter: {
-            value: '*'
-          },
-          isNotEmptyFilter: false,
-          title: 'empty filters'
-        },
-        {
-          currentNameFilter: 'a',
-          currentTypeFilter: {
-            value: '*'
-          },
-          isNotEmptyFilter: true,
-          title: 'name filter'
-        },
-        {
-          currentNameFilter: '0',
-          currentTypeFilter: {
-            value: '*'
-          },
-          isNotEmptyFilter: true,
-          title: 'name filter with "0" as string'
-        },
-        {
-          currentNameFilter: '',
-          currentTypeFilter: {
-            value: false
-          },
-          isNotEmptyFilter: true,
-          title: 'type filter'
-        },
-        {
-          currentNameFilter: 'a',
-          currentTypeFilter: {
-            value: false
-          },
-          isNotEmptyFilter: true,
-          title: 'both filters'
-        },
-        {
-          currentNameFilter: '0',
-          currentTypeFilter: {
-            value: false
-          },
-          isNotEmptyFilter: true,
-          title: 'both filters with "0" as string'
-        }
-      ];
-
-      cases.forEach(function (item) {
-        it(item.title, function () {
-          $httpBackend.expectGET(/\/api\/v1\/groups/).respond(200);
-          scope.filter.name = item.currentNameFilter;
-          scope.filter.type = item.currentTypeFilter;
-          scope.$digest();
-          expect(scope.isNotEmptyFilter).toEqual(item.isNotEmptyFilter);
-        });
-      });
-
-    });
-
   });
 
 });

Reply via email to