You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by at...@apache.org on 2018/07/27 17:27:03 UTC

[ambari] branch trunk updated: AMBARI-24372 How alert count is presented for config errors/warnings/suggestions confusing and misleading

This is an automated email from the ASF dual-hosted git repository.

atkach pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 7171b96  AMBARI-24372 How alert count is presented for config errors/warnings/suggestions confusing and misleading
7171b96 is described below

commit 7171b964194263e20309d7c016b096079cc9037f
Author: Andrii Tkach <at...@apache.org>
AuthorDate: Fri Jul 27 17:44:18 2018 +0300

    AMBARI-24372 How alert count is presented for config errors/warnings/suggestions confusing and misleading
---
 .../app/controllers/wizard/step7_controller.js     | 10 ++-
 .../app/mappers/alert_definition_summary_mapper.js | 44 ++++--------
 ambari-web/app/models/host_component.js            | 13 +++-
 ambari-web/app/models/service.js                   | 13 +++-
 ambari-web/app/styles/alert_badge.less             | 81 ++++++++++++++++++++++
 ambari-web/app/styles/alerts.less                  | 34 +--------
 ambari-web/app/styles/wizard.less                  |  7 ++
 ambari-web/app/templates/application.hbs           | 14 +---
 ambari-web/app/templates/common/alert_badge.hbs    | 36 ++++++++++
 .../templates/common/configs/service_config.hbs    | 16 +++--
 .../common/configs/service_config_category.hbs     |  6 +-
 .../common/configs/service_config_layout_tab.hbs   |  6 +-
 .../common/configs/service_config_wizard.hbs       | 12 +++-
 .../templates/common/configs/services_config.hbs   | 23 ++++--
 .../modal_popups/config_recommendation_popup.hbs   | 12 +++-
 .../common/modal_popups/dependent_configs_list.hbs | 14 +++-
 ambari-web/app/templates/main/host.hbs             | 11 +--
 .../app/templates/main/service/info/summary.hbs    |  8 ++-
 .../service/info/summary/master_components.hbs     |  8 ++-
 .../app/templates/main/service/menu_item.hbs       |  7 +-
 .../app/templates/wizard/step7/directories_tab.hbs |  6 +-
 ambari-web/app/views.js                            |  1 +
 ambari-web/app/views/common/alert_badge.js         | 71 +++++++++++++++++++
 ambari-web/app/views/main/menu.js                  |  8 ---
 ambari-web/app/views/main/service/info/summary.js  | 11 ++-
 .../test/views/main/service/info/summary_test.js   |  8 ---
 26 files changed, 343 insertions(+), 137 deletions(-)

diff --git a/ambari-web/app/controllers/wizard/step7_controller.js b/ambari-web/app/controllers/wizard/step7_controller.js
index e93564d..37e7a3c 100644
--- a/ambari-web/app/controllers/wizard/step7_controller.js
+++ b/ambari-web/app/controllers/wizard/step7_controller.js
@@ -150,7 +150,7 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E
    * Total number of recommendation and validation issues
    * @type {number}
    */
-  issuesCounter: 0,
+  issuesCounter: Em.computed.sumProperties('validationsCounter', 'suggestionsCounter'),
 
   /**
    * Number of ui-side validation issues
@@ -159,6 +159,12 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E
   validationsCounter: 0,
 
   /**
+   * Number of ui-side suggestion issues
+   * @type {number}
+   */
+  suggestionsCounter: 0,
+
+  /**
    * Tab objects to represent each config category tab
    */
   tabs: [],
@@ -2108,7 +2114,7 @@ App.WizardStep7Controller = Em.Controller.extend(App.ServerValidatorMixin, App.E
     var recommendations = this.get('changedProperties.length');
     var validations = this.get('stepConfigs').mapProperty('configsWithErrors.length').reduce(Em.sum, 0);
     var configErrorList = this.get('configErrorList');
-    this.set('issuesCounter', recommendations + validations + configErrorList.get('issues.length') + configErrorList.get('criticalIssues.length'));
+    this.set('suggestionsCounter', recommendations + configErrorList.get('issues.length') + configErrorList.get('criticalIssues.length'));
     if (validations !== this.get('validationsCounter')) {
       this.ringBell();
     }
diff --git a/ambari-web/app/mappers/alert_definition_summary_mapper.js b/ambari-web/app/mappers/alert_definition_summary_mapper.js
index 29f286e..3b3b0d0 100644
--- a/ambari-web/app/mappers/alert_definition_summary_mapper.js
+++ b/ambari-web/app/mappers/alert_definition_summary_mapper.js
@@ -73,43 +73,23 @@ App.alertDefinitionSummaryMapper = App.QuickDataMapper.create({
     Object.keys(groupedByServiceName).forEach(function(serviceName) {
       var service = servicesMap[serviceName];
       if (service) {
-        var hasCriticalAlerts = false;
-
-        var alertsCount = groupedByServiceName[serviceName].map(function (alertDefinition) {
-
-          var criticalCount = alertDefinition.getWithDefault('summary.CRITICAL.count', 0);
-          var warningCount = alertDefinition.getWithDefault('summary.WARNING.count', 0);
-
-          if (criticalCount) {
-            hasCriticalAlerts = true;
-          }
-          return criticalCount + warningCount;
-
-        }).reduce(Em.sum, 0);
-
         service.setProperties({
-          alertsCount: alertsCount,
-          hasCriticalAlerts: hasCriticalAlerts
+          criticalCount: groupedByServiceName[serviceName].map((alertDefinition) => {
+            return alertDefinition.getWithDefault('summary.CRITICAL.count', 0);
+          }).reduce(Em.sum, 0),
+          warningCount: groupedByServiceName[serviceName].map((alertDefinition) => {
+            return alertDefinition.getWithDefault('summary.WARNING.count', 0);
+          }).reduce(Em.sum, 0)
         });
 
         service.get('hostComponents').filterProperty('isMaster').forEach(function (master) {
-
-          hasCriticalAlerts = false;
-          alertsCount = (groupedByComponentName[master.get('componentName')] || []).map(function (alertDefinition) {
-
-            var criticalCount = alertDefinition.getWithDefault('summary.CRITICAL.count', 0);
-            var warningCount = alertDefinition.getWithDefault('summary.WARNING.count', 0);
-
-            if (criticalCount) {
-              hasCriticalAlerts = true;
-            }
-            return criticalCount + warningCount;
-
-          }).reduce(Em.sum, 0);
-
           master.setProperties({
-            alertsCount: alertsCount,
-            hasCriticalAlerts: hasCriticalAlerts
+            criticalCount: groupedByServiceName[serviceName].map((alertDefinition) => {
+              return alertDefinition.getWithDefault('summary.CRITICAL.count', 0);
+            }).reduce(Em.sum, 0),
+            warningCount: groupedByServiceName[serviceName].map((alertDefinition) => {
+              return alertDefinition.getWithDefault('summary.WARNING.count', 0);
+            }).reduce(Em.sum, 0)
           });
         });
       }
diff --git a/ambari-web/app/models/host_component.js b/ambari-web/app/models/host_component.js
index 40db612..88b9b88 100644
--- a/ambari-web/app/models/host_component.js
+++ b/ambari-web/app/models/host_component.js
@@ -134,16 +134,25 @@ App.HostComponent = DS.Model.extend({
   }.property('componentName', 'App.components.nonHDP'),
 
   /**
+   * @type {number}
+   */
+  warningCount: 0,
+  /**
+   * @type {number}
+   */
+  criticalCount: 0,
+
+  /**
    * Does component have Critical Alerts
    * @type {boolean}
    */
-  hasCriticalAlerts: false,
+  hasCriticalAlerts: Em.computed.gte('criticalCount', 0),
 
   /**
    * Number of the Critical and Warning alerts for current component
    * @type {number}
    */
-  alertsCount: 0,
+  alertsCount: Em.computed.sumProperties('warningCount', 'criticalCount'),
 
   statusClass: function () {
     return this.get('isActive') ? this.get('workStatus') : 'icon-medkit';
diff --git a/ambari-web/app/models/service.js b/ambari-web/app/models/service.js
index df00049..994f9ce 100644
--- a/ambari-web/app/models/service.js
+++ b/ambari-web/app/models/service.js
@@ -168,16 +168,25 @@ App.Service = DS.Model.extend({
   }.property('restartRequiredHostsAndComponents'),
 
   /**
+   * @type {number}
+   */
+  warningCount: 0,
+  /**
+   * @type {number}
+   */
+  criticalCount: 0,
+
+  /**
    * Does service have Critical Alerts
    * @type {boolean}
    */
-  hasCriticalAlerts: false,
+  hasCriticalAlerts: Em.computed.gte('criticalCount', 0),
 
   /**
    * Number of the Critical and Warning alerts for current service
    * @type {number}
    */
-  alertsCount: 0
+  alertsCount: Em.computed.sumProperties('warningCount', 'criticalCount')
 
 });
 
diff --git a/ambari-web/app/styles/alert_badge.less b/ambari-web/app/styles/alert_badge.less
new file mode 100644
index 0000000..0068066
--- /dev/null
+++ b/ambari-web/app/styles/alert_badge.less
@@ -0,0 +1,81 @@
+/**
+ * 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.
+ */
+
+
+@import 'common.less';
+@default-size: 20px;
+@medium-size: 16px;
+@small-size: 15px;
+
+.alert-badge {
+  display: inline-block;
+  position: relative;
+  min-width: @default-size;
+  height: @default-size;
+  margin: 0 4px;
+  vertical-align: text-top;
+  .counter {
+    font-size: 13px;
+    border-radius: 4px;
+    padding: 0 2px;
+    display: block;
+    height: @default-size;
+    min-width: @default-size;
+    line-height: @default-size;
+  }
+  .position-top {
+    position: absolute;
+    z-index: 3;
+    top: 2px;
+    left: -2px;
+  }
+  .position-middle {
+    position: absolute;
+    z-index: 2;
+    top: 0;
+    left: 0;
+  }
+  .position-bottom {
+    position: absolute;
+    z-index: 1;
+    top: -2px;
+    left: 2px;
+  }
+}
+
+.alert-badge.medium-size {
+  min-width: @medium-size;
+  height: @medium-size;
+  .counter {
+    font-size: 11px;
+    height: @medium-size;
+    min-width: @medium-size;
+    line-height: @medium-size;
+  }
+}
+
+.alert-badge.small-size {
+  min-width: @small-size;
+  height: @small-size;
+  .counter {
+    font-size: 9px;
+    height: @small-size;
+    min-width: @small-size;
+    line-height: @small-size;
+  }
+}
diff --git a/ambari-web/app/styles/alerts.less b/ambari-web/app/styles/alerts.less
index b092adc..0bac6a4 100644
--- a/ambari-web/app/styles/alerts.less
+++ b/ambari-web/app/styles/alerts.less
@@ -366,23 +366,7 @@
 
 }
 
-.alert-mixin {
-  border-radius: 50%;
-  color: #ffffff;
-  text-align: center;
-  display: inline-block;
-  position: relative;
-}
-
 .service-block .summary-box-header {
-  .alerts-crit-count, .alerts-warn-count, .alerts-none-count {
-    .alert-mixin;
-    padding: 3px 5px;
-    font-size: 10px;
-    min-width: 16px;
-    height: 16px;
-    top: -3px;
-  }
   .glyphicon {
     color: #999999;
     cursor: pointer;
@@ -393,25 +377,9 @@
 }
 
 .alerts-block {
-  .alerts-crit-count, .alerts-warn-count, .alerts-none-count {
-    .alert-mixin;
-    padding: 3px 5px;
-    font-size: 10px;
-    min-width: 16px;
-    height: 16px;
-  }
+  margin-right: 6px;
 }
 
-.summary-value {
-  .alerts-crit-count, .alerts-warn-count, .alerts-none-count {
-    .alert-mixin;
-    padding: 3px 0;
-    font-size: 9px;
-    width: 15px;
-    height: 15px;
-    top: -3px;
-  }
-}
 
 .alerts-crit-count, .alerts-warn-count, .alerts-none-count {
   cursor: pointer;
diff --git a/ambari-web/app/styles/wizard.less b/ambari-web/app/styles/wizard.less
index a8ba197..b367b9e 100644
--- a/ambari-web/app/styles/wizard.less
+++ b/ambari-web/app/styles/wizard.less
@@ -1191,6 +1191,13 @@
     -webkit-animation: zoom-in 0.3s ease;
     animation: zoom-in 0.3s ease;
   }
+  #issues-counter {
+    vertical-align: sub;
+    .alert-badge {
+      top: 2px;
+      margin: 0;
+    }
+  }
 
   .service-name {
     position: relative;
diff --git a/ambari-web/app/templates/application.hbs b/ambari-web/app/templates/application.hbs
index 3590e9e..7edbd05 100644
--- a/ambari-web/app/templates/application.hbs
+++ b/ambari-web/app/templates/application.hbs
@@ -146,17 +146,9 @@
                 <span class="alerts-label dropdown-toggle" data-toggle="dropdown" aria-haspopup="true"
                       aria-expanded="false">
                   <span class="glyphicon glyphicon-bell"></span>
-                  {{#if App.router.mainAlertDefinitionsController.unhealthyAlertInstancesCount}}
-                  {{! alerts exist }}
-                    <span {{bindAttr class=":numberCircle App.router.mainAlertDefinitionsController.isCriticalAlerts:alert-crit-count:alert-warn-count"}}>
-                      {{App.router.mainAlertDefinitionsController.unhealthyAlertInstancesCount}}
-                    </span>
-                  {{else}}
-                  {{! no alerts }}
-                    <span {{translateAttr title="titlebar.alerts.noAlerts"}} class="numberCircle alerts-none-count">
-                      {{App.router.mainAlertDefinitionsController.unhealthyAlertInstancesCount}}
-                    </span>
-                  {{/if}}
+                  {{view App.AlertBadgeView
+                    criticalCountBinding="App.router.mainAlertDefinitionsController.criticalAlertInstancesCount"
+                    warningCountBinding="App.router.mainAlertDefinitionsController.warningAlertInstancesCount"}}
                 </span>
                 <div id="notifications-dropdown" class="dropdown-menu row">
                   {{view App.AlertInstancesPopupView}}
diff --git a/ambari-web/app/templates/common/alert_badge.hbs b/ambari-web/app/templates/common/alert_badge.hbs
new file mode 100644
index 0000000..d0ac493
--- /dev/null
+++ b/ambari-web/app/templates/common/alert_badge.hbs
@@ -0,0 +1,36 @@
+{{!
+* 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.
+}}
+
+{{#if view.criticalCount}}
+  <span {{bindAttr class=":counter :label :alerts-crit-count view.isTwoLevelBadge:position-middle:position-top"}}>
+    {{view.criticalCountDisplay}}
+  </span>
+{{/if}}
+{{#if view.warningCount}}
+  <span {{bindAttr class=":counter :label :alerts-warn-count view.isTwoLevelBadge:position-bottom:position-middle"}}>
+    {{view.warningCountDisplay}}
+  </span>
+{{/if}}
+{{#if view.suggestionCount}}
+  <span class="counter label alerts-none-count position-bottom">
+    {{view.suggestionCountDisplay}}
+  </span>
+{{/if}}
+{{#if view.showNoneCount}}
+  <span class="counter label alerts-none-count position-middle">0</span>
+{{/if}}
diff --git a/ambari-web/app/templates/common/configs/service_config.hbs b/ambari-web/app/templates/common/configs/service_config.hbs
index ad74cfd..199def6 100644
--- a/ambari-web/app/templates/common/configs/service_config.hbs
+++ b/ambari-web/app/templates/common/configs/service_config.hbs
@@ -123,13 +123,21 @@
         <li rel='tooltip' {{bindAttr class="tab.isActive:active tab.isHiddenByFilter:disabled" data-original-title="tab.tooltipMsg"}}>
           <a href="#" {{action "setActiveTab" tab target="view"}} {{bindAttr data-target="tab.headingClass"}} data-toggle="tab">
             {{tab.displayName}}
-              {{#if tab.isAdvanced}}
-                {{#if controller.errorsCount}}
-                <span class="badge badge-important">{{controller.errorsCount}}</span>
+            {{#if tab.isAdvanced}}
+              {{#if controller.errorsCount}}
+                <span class="alert-badge" {{QAAttr "error-badge"}}>
+                  <span class="counter label alerts-crit-count">
+                    {{controller.errorsCount}}
+                  </span>
+                </span>
               {{/if}}
             {{else}}
               {{#if tab.errorsCount}}
-                <span class="badge badge-important">{{tab.errorsCount}}</span>
+                <span class="alert-badge" {{QAAttr "error-badge"}}>
+                  <span class="counter label alerts-crit-count">
+                    {{tab.errorsCount}}
+                  </span>
+                </span>
               {{/if}}
             {{/if}}
           </a>
diff --git a/ambari-web/app/templates/common/configs/service_config_category.hbs b/ambari-web/app/templates/common/configs/service_config_category.hbs
index 5f8de30..b2b523f 100644
--- a/ambari-web/app/templates/common/configs/service_config_category.hbs
+++ b/ambari-web/app/templates/common/configs/service_config_category.hbs
@@ -22,7 +22,11 @@
       <a class="panel-toggle category-header" {{QAAttr "category-header"}}>
         <span class="category-name" {{QAAttr "category-name"}}>{{view.category.displayName}}</span>
         {{#if view.category.errorCount}}
-          <span class="badge badge-important">{{view.category.errorCount}}</span>
+          <span class="alert-badge" {{QAAttr "error-badge"}}>
+            <span class="counter label alerts-crit-count position-top">
+              {{view.category.errorCount}}
+            </span>
+          </span>
         {{/if}}
       </a>
     </h3>
diff --git a/ambari-web/app/templates/common/configs/service_config_layout_tab.hbs b/ambari-web/app/templates/common/configs/service_config_layout_tab.hbs
index b50ad57..cad6435 100644
--- a/ambari-web/app/templates/common/configs/service_config_layout_tab.hbs
+++ b/ambari-web/app/templates/common/configs/service_config_layout_tab.hbs
@@ -58,7 +58,11 @@
                                     <a href="#" {{action setActiveSubTab subSectionTab target="view"}}{{bindAttr data-target="subSectionTab.id"}} data-toggle="tab">
                                       {{subSectionTab.displayName}}
                                       {{#if subSectionTab.errorsCount}}
-                                        <span class="badge badge-important">{{subSectionTab.errorsCount}}</span>
+                                        <span class="alert-badge" {{QAAttr "error-badge"}}>
+                                          <span class="counter label alerts-crit-count">
+                                            {{subSectionTab.errorsCount}}
+                                          </span>
+                                        </span>
                                       {{/if}}
                                     </a>
                                   </li>
diff --git a/ambari-web/app/templates/common/configs/service_config_wizard.hbs b/ambari-web/app/templates/common/configs/service_config_wizard.hbs
index 554db23..73bb094 100644
--- a/ambari-web/app/templates/common/configs/service_config_wizard.hbs
+++ b/ambari-web/app/templates/common/configs/service_config_wizard.hbs
@@ -97,11 +97,19 @@
               {{tab.displayName}}
               {{#if tab.isAdvanced}}
                 {{#if controller.errorsCount}}
-                  <span class="badge badge-important">{{controller.errorsCount}}</span>
+                  <span class="alert-badge" {{QAAttr "error-badge"}}>
+                    <span class="counter label alerts-crit-count">
+                      {{controller.errorsCount}}
+                    </span>
+                  </span>
                 {{/if}}
               {{else}}
                 {{#if tab.errorsCount}}
-                  <span class="badge badge-important">{{tab.errorsCount}}</span>
+                  <span class="alert-badge" {{QAAttr "error-badge"}}>
+                    <span class="counter label alerts-crit-count">
+                      {{tab.errorsCount}}
+                    </span>
+                  </span>
                 {{/if}}
               {{/if}}
             </a>
diff --git a/ambari-web/app/templates/common/configs/services_config.hbs b/ambari-web/app/templates/common/configs/services_config.hbs
index 044f32e7..31914a1 100644
--- a/ambari-web/app/templates/common/configs/services_config.hbs
+++ b/ambari-web/app/templates/common/configs/services_config.hbs
@@ -29,7 +29,11 @@
                data-toggle="tab">
               {{formatRole tab.serviceName}}
               {{#if tab.errorsCount}}
-                <span class="badge badge-important">{{tab.errorsCount}}</span>
+                <span class="alert-badge">
+                  <span class="counter label alerts-crit-count">
+                    {{tab.errorsCount}}
+                  </span>
+                </span>
               {{/if}}
             </a>
           </li>
@@ -50,8 +54,15 @@
           {{#view App.ServiceConfigTab}}
             <a href="#{{unbound service.serviceName}}" {{bindAttr class=":active service.selected:new"}}
                data-toggle="tab" {{action selectService service target="controller"}}>
-              {{service.displayName}}{{#if service.errorCount}}<span
-                class="badge badge-important" {{QAAttr "error-badge"}}>{{service.errorCount}}</span>{{/if}}</a>
+              {{service.displayName}}
+              {{#if service.errorCount}}
+                <span class="alert-badge" {{QAAttr "error-badge"}}>
+                  <span class="counter label alerts-crit-count">
+                    {{service.errorCount}}
+                  </span>
+                </span>
+              {{/if}}
+            </a>
           {{/view}}
         {{/if}}
       {{/each}}
@@ -65,8 +76,10 @@
     <div class="notifications-group align-right installer-all-configs-notifications">
       <span class="alerts-label dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
         <span id="issues-bell" {{bindAttr class=":glyphicon :glyphicon-bell controller.hasErrors:important:warning"}}></span>
-        <span id="issues-counter" {{bindAttr class=":badge controller.hasErrors:badge-important:badge-warning"}}>
-          {{issuesCounter}}
+        <span id="issues-counter">
+          {{view App.AlertBadgeView
+                 criticalCountBinding="validationsCounter"
+                 suggestionCountBinding="suggestionsCounter"}}
         </span>
       </span>
       <div id="notifications-dropdown" class="dropdown-menu row">
diff --git a/ambari-web/app/templates/common/modal_popups/config_recommendation_popup.hbs b/ambari-web/app/templates/common/modal_popups/config_recommendation_popup.hbs
index 0f51473..1599f25 100644
--- a/ambari-web/app/templates/common/modal_popups/config_recommendation_popup.hbs
+++ b/ambari-web/app/templates/common/modal_popups/config_recommendation_popup.hbs
@@ -19,7 +19,11 @@
 {{#if view.configErrors.criticalIssues.length}}
   <p>
     {{t installer.step7.popup.validation.criticalIssues.body}}
-    <span class="badge badge-important">{{view.configErrors.criticalIssues.length}}</span>
+    <span class="alert-badge" {{QAAttr "error-badge"}}>
+      <span class="counter label alerts-crit-count">
+        {{view.configErrors.criticalIssues.length}}
+      </span>
+    </span>
   </p>
   <div {{bindAttr class="controller.isInstallWizard::limited-height-2 :config-validation-warnings"}}>
     <table class="table table-hover">
@@ -62,7 +66,11 @@
 {{#if view.configErrors.issues.length}}
   <p>
     {{t installer.step7.popup.validation.issues.title}}
-    <span class="badge badge-warning">{{view.configErrors.issues.length}}</span>
+    <span class="alert-badge" {{QAAttr "error-badge"}}>
+      <span class="counter label alerts-none-count">
+        {{view.configErrors.issues.length}}
+      </span>
+    </span>
   </p>
   <p class="recommendations-message">
     {{t installer.step7.popup.validation.issues.body}}
diff --git a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs
index d12af61..985e99f 100644
--- a/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs
+++ b/ambari-web/app/templates/common/modal_popups/dependent_configs_list.hbs
@@ -19,7 +19,12 @@
 {{#if controller.isInstallWizard}}
   {{#if view.allConfigsWithErrors.length}}
     <h5>
-      {{t installer.step7.requirements.popover.header}} <span class="badge badge-important">{{view.allConfigsWithErrors.length}}</span>
+      {{t installer.step7.requirements.popover.header}}
+      <span class="alert-badge" {{QAAttr "error-badge"}}>
+        <span class="counter label alerts-crit-count">
+          {{view.allConfigsWithErrors.length}}
+        </span>
+      </span>
     </h5>
     <div class="recommendations-message">
       {{t installer.step7.requirements.popover.message}}
@@ -54,7 +59,12 @@
   {{view App.ValidationsView configErrorsBinding="controller.configErrorList"}}
   {{#if controller.hasChangedDependencies}}
     <h5>
-      {{t installer.step7.recommendations.popover.header}} <span class="badge badge-warning">{{view.recommendations.length}}</span>
+      {{t installer.step7.recommendations.popover.header}}
+      <span class="alert-badge" {{QAAttr "error-badge"}}>
+        <span class="counter label alerts-none-count">
+          {{view.recommendations.length}}
+        </span>
+      </span>
     </h5>
     <div class="recommendations-message">
       {{t installer.step7.recommendations.popover.message}}
diff --git a/ambari-web/app/templates/main/host.hbs b/ambari-web/app/templates/main/host.hbs
index 4b832f0..a83316a 100644
--- a/ambari-web/app/templates/main/host.hbs
+++ b/ambari-web/app/templates/main/host.hbs
@@ -77,11 +77,12 @@
               <a  rel="UsageTooltip" data-original-title="{{unbound host.hostName}}" href="#" {{action "showDetails" host}}>{{unbound host.hostName}}</a>
             </span>
             {{#if host.criticalWarningAlertsCount}}
-              {{#if host.alertsSummary.CRITICAL}}
-                <span class="label alerts-crit-count" {{action "goToHostAlerts" host target="controller"}}>{{host.criticalWarningAlertsCount}}</span>
-              {{else}}
-                <span class="label alerts-warn-count" {{action "goToHostAlerts" host target="controller"}}>{{host.criticalWarningAlertsCount}}</span>
-              {{/if}}
+              <span {{action "goToHostAlerts" host target="controller"}}>
+                {{view App.AlertBadgeView
+                       classNames="medium-size"
+                       criticalCountBinding="host.alertsSummary.CRITICAL"
+                       warningCountBinding="host.alertsSummary.WARNING"}}
+              </span>
             {{/if}}
           </td>
           <td class="restart">
diff --git a/ambari-web/app/templates/main/service/info/summary.hbs b/ambari-web/app/templates/main/service/info/summary.hbs
index c100fca..9dead92 100644
--- a/ambari-web/app/templates/main/service/info/summary.hbs
+++ b/ambari-web/app/templates/main/service/info/summary.hbs
@@ -58,9 +58,11 @@
             {{#if view.hasAlertDefinitions}}
               <span {{action "showServiceAlertsPopup" controller.content target="controller"}} class="pull-right">
                 <i class="glyphicon glyphicon-bell"></i>
-                <span {{bindAttr class=":label view.hasCriticalAlerts:alerts-crit-count view.hasNonCriticalAlertsOnly:alerts-warn-count view.hasNoAlerts:alerts-none-count"}}>
-                  {{view.alertsCount}}
-                </span>
+                {{view App.AlertBadgeView
+                       classNames="medium-size"
+                       criticalCountBinding="view.controller.content.criticalCount"
+                       warningCountBinding="view.controller.content.warningCount"
+                       shouldShowNoneCountBinding="view.shouldShowNoneCount"}}
               </span>
             {{/if}}
           </div>
diff --git a/ambari-web/app/templates/main/service/info/summary/master_components.hbs b/ambari-web/app/templates/main/service/info/summary/master_components.hbs
index 6c672e7..b70375e 100644
--- a/ambari-web/app/templates/main/service/info/summary/master_components.hbs
+++ b/ambari-web/app/templates/main/service/info/summary/master_components.hbs
@@ -37,9 +37,11 @@
                   data-original-title="comp.passiveTooltip"}}></span>
               <span class="main-info">{{comp.componentTextStatus}}</span>
               {{#if comp.alertsCount}}
-                <span {{action "showServiceAlertsPopup" comp target="controller"}}
-                  {{bindAttr class=":label comp.hasCriticalAlerts:alerts-crit-count:alerts-warn-count"}}>
-                  {{comp.alertsCount}}
+                <span {{action "showServiceAlertsPopup" comp target="controller"}}>
+                {{view App.AlertBadgeView
+                       classNames="small-size"
+                       criticalCountBinding="comp.criticalCount"
+                       warningCountBinding="comp.warningCount"}}
                 </span>
               {{/if}}
             </div>
diff --git a/ambari-web/app/templates/main/service/menu_item.hbs b/ambari-web/app/templates/main/service/menu_item.hbs
index ee75581..8fb389d 100644
--- a/ambari-web/app/templates/main/service/menu_item.hbs
+++ b/ambari-web/app/templates/main/service/menu_item.hbs
@@ -18,9 +18,10 @@
 
 <a class="services-menu-blocks" {{bindAttr href="view.link" data-href="view.dataHref"}}>
   <span class="pull-right alerts-block">
-    <span {{bindAttr class=":label view.noAlerts:hidden view.hasCriticalAlerts:alerts-crit-count:alerts-warn-count"}}>
-      {{view.alertsCountDisplay}}
-    </span>
+    {{view App.AlertBadgeView
+           classNames="medium-size"
+           criticalCountBinding="view.content.criticalCount"
+           warningCountBinding="view.content.warningCount"}}
   </span>
   <span class="pull-right">
     <i rel="tooltip" {{action goToConfigs target="view"}} {{bindAttr class=":glyphicon :glyphicon-refresh :restart-required-service view.content.isRestartRequired::hidden" data-original-title="view.restartRequiredMessage"}}></i>
diff --git a/ambari-web/app/templates/wizard/step7/directories_tab.hbs b/ambari-web/app/templates/wizard/step7/directories_tab.hbs
index d224a34..99a5fd8 100644
--- a/ambari-web/app/templates/wizard/step7/directories_tab.hbs
+++ b/ambari-web/app/templates/wizard/step7/directories_tab.hbs
@@ -28,7 +28,11 @@
              data-toggle="tab">
             {{formatRole tab.serviceName}}
             {{#if tab.errorsCount}}
-              <span class="badge badge-important">{{tab.errorsCount}}</span>
+              <span class="alert-badge" {{QAAttr "error-badge"}}>
+                <span class="counter label alerts-crit-count">
+                  {{tab.errorsCount}}
+                </span>
+              </span>
             {{/if}}
           </a>
         </li>
diff --git a/ambari-web/app/views.js b/ambari-web/app/views.js
index 2d03460..715c8bb 100644
--- a/ambari-web/app/views.js
+++ b/ambari-web/app/views.js
@@ -23,6 +23,7 @@ require('views/application');
 require('views/common/breadcrumbs_view');
 require('views/common/clock_view');
 require('views/common/checkbox_view');
+require('views/common/alert_badge');
 require('views/common/log_search_ui_link_view');
 require('views/common/log_file_search_view');
 require('views/common/log_tail_view');
diff --git a/ambari-web/app/views/common/alert_badge.js b/ambari-web/app/views/common/alert_badge.js
new file mode 100644
index 0000000..c67499f
--- /dev/null
+++ b/ambari-web/app/views/common/alert_badge.js
@@ -0,0 +1,71 @@
+/**
+ * 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.
+ */
+
+var App = require('app');
+
+App.AlertBadgeView = Em.View.extend({
+  layoutName: require('templates/common/alert_badge'),
+  classNames: ['alert-badge'],
+
+  /**
+   * if true - supports only CRITICAL and WARNING alerts
+   * if false - supports CRITICAL, WARNING and SUGGESTION alerts
+   * @type {boolean}
+   */
+  isTwoLevelBadge: true,
+
+  /**
+   * @type {boolean}
+   */
+  shouldShowNoneCount: false,
+
+  /**
+   * @type {number}
+   */
+  criticalCount: 0,
+  /**
+   * @type {number}
+   */
+  warningCount: 0,
+  /**
+   * @type {number}
+   */
+  suggestionCount: 0,
+  /**
+   * @type {string}
+   */
+  criticalCountDisplay: function() {
+    return this.get('criticalCount') > 99 ? "99+" : String(this.get('criticalCount'));
+  }.property('criticalCount'),
+  /**
+   * @type {string}
+   */
+  warningCountDisplay: function() {
+    return this.get('warningCount') > 99 ? "99+" : String(this.get('warningCount'));
+  }.property('warningCount'),
+  /**
+   * @type {string}
+   */
+  suggestionCountDisplay: function() {
+    return this.get('suggestionCount') > 99 ? "99+" : String(this.get('suggestionCount'));
+  }.property('suggestionCount'),
+  /**
+   * @type {boolean}
+   */
+  showNoneCount: Em.computed.and('!criticalCount', '!warningCount', '!suggestionCount', 'shouldShowNoneCount')
+});
diff --git a/ambari-web/app/views/main/menu.js b/ambari-web/app/views/main/menu.js
index dae885f..9de9df8 100644
--- a/ambari-web/app/views/main/menu.js
+++ b/ambari-web/app/views/main/menu.js
@@ -212,14 +212,6 @@ App.SideNavServiceMenuView = Em.CollectionView.extend({
 
     shouldBeRestarted: Em.computed.someBy('content.hostComponents', 'staleConfigs', true),
 
-    alertsCountDisplay: function () {
-      return this.get('content.alertsCount') > 99 ? "99+" : this.get('content.alertsCount');
-    }.property('content.alertsCount'),
-
-    noAlerts: Em.computed.equal('content.alertsCount', 0),
-
-    hasCriticalAlerts: Em.computed.alias('content.hasCriticalAlerts'),
-
     isMasterDown: function() {
       return this.get('content.hostComponents').filterProperty('isMaster').some((component) => {
         return !component.get('isRunning');
diff --git a/ambari-web/app/views/main/service/info/summary.js b/ambari-web/app/views/main/service/info/summary.js
index d7c7a9f..1b11063 100644
--- a/ambari-web/app/views/main/service/info/summary.js
+++ b/ambari-web/app/views/main/service/info/summary.js
@@ -351,13 +351,10 @@ App.MainServiceInfoSummaryView = Em.View.extend({
   componentsCount: null,
   hostsCount: null,
 
-  alertsCount: Em.computed.alias('controller.content.alertsCount'),
-
-  hasNoAlerts: Em.computed.equal('alertsCount', 0),
-
-  hasCriticalAlerts: Em.computed.alias('controller.content.hasCriticalAlerts'),
-
-  hasNonCriticalAlertsOnly: Em.computed.and('!hasNoAlerts', '!hasCriticalAlerts'),
+  /**
+   * @type {boolean}
+   */
+  shouldShowNoneCount: true,
 
   /**
    * Define if service has alert definitions defined
diff --git a/ambari-web/test/views/main/service/info/summary_test.js b/ambari-web/test/views/main/service/info/summary_test.js
index 03ec08b..3c05d0d 100644
--- a/ambari-web/test/views/main/service/info/summary_test.js
+++ b/ambari-web/test/views/main/service/info/summary_test.js
@@ -40,14 +40,6 @@ describe('App.MainServiceInfoSummaryView', function() {
 
   App.TestAliases.testAsComputedAlias(view, 'serviceName', 'service.serviceName', 'string');
 
-  App.TestAliases.testAsComputedAlias(view, 'alertsCount', 'controller.content.alertsCount', 'number');
-
-  App.TestAliases.testAsComputedAlias(view, 'hasCriticalAlerts', 'controller.content.hasCriticalAlerts', 'boolean');
-
-  App.TestAliases.testAsComputedEqual(view, 'hasNoAlerts', 'alertsCount', 0);
-
-  App.TestAliases.testAsComputedAnd(view, 'hasNonCriticalAlertsOnly', ['!hasNoAlerts', '!hasCriticalAlerts']);
-
   describe('#servers', function () {
     it('services shouldn\'t have servers except FLUME and ZOOKEEPER', function () {
       expect(view.get('servers')).to.be.empty;