Parcourir la source

AMBARI-10314 Implement reordering of a widget on a section. (atkach)

Andrii Tkach il y a 10 ans
Parent
commit
b119d12529

+ 87 - 0
ambari-web/app/assets/data/widget_layouts/HBASE/stack_layout.json

@@ -77,6 +77,60 @@
               "error_threshold": 90
             }
           },
+          {
+            "widget_name": "FILES_LOCAL2",
+            "display_name": "Files Local",
+            "description": "This widget shows percentage of files local.",
+            "widget_type": "NUMBER",
+            "is_visible": "true",
+            "metrics":[
+              {
+                "name": "regionserver.Server.percentFilesLocal",
+                "widget_id": "metrics/hbase/regionserver/percentFilesLocal",
+                "category": "",
+                "service_name": "HBASE",
+                "component_name": "HBASE_REGIONSERVER"
+              }
+            ],
+            "values": [
+              {
+                "name": "Files Local",
+                "value": "${regionserver.Server.percentFilesLocal}"
+              }
+            ],
+            "properties": {
+              "display_unit": "%",
+              "warning_threshold": 70,
+              "error_threshold": 90
+            }
+          },
+          {
+            "widget_name": "FILES_LOCAL3",
+            "display_name": "Files Local",
+            "description": "This widget shows percentage of files local.",
+            "widget_type": "NUMBER",
+            "is_visible": "true",
+            "metrics":[
+              {
+                "name": "regionserver.Server.percentFilesLocal",
+                "widget_id": "metrics/hbase/regionserver/percentFilesLocal",
+                "category": "",
+                "service_name": "HBASE",
+                "component_name": "HBASE_REGIONSERVER"
+              }
+            ],
+            "values": [
+              {
+                "name": "Files Local",
+                "value": "${regionserver.Server.percentFilesLocal}"
+              }
+            ],
+            "properties": {
+              "display_unit": "%",
+              "warning_threshold": 70,
+              "error_threshold": 90
+            }
+          },
           {
             "widget_name": "NAMENODE_HEAP",
             "display_name": "NameNode Heap",
@@ -109,6 +163,39 @@
               "warning_threshold": 0.9,
               "error_threshold": 0.7
             }
+          },
+          {
+            "widget_name": "NAMENODE_HEAP2",
+            "display_name": "NameNode Heap",
+            "widget_type": "GAUGE",
+            "is_visible": "true",
+            "description": "",
+            "metrics":[
+              {
+                "name": "java.lang:type=Memory.HeapMemoryUsage[used]",
+                "widget_id": "metrics/jvm/HeapMemoryUsed",
+                "category": "",
+                "service_name": "HBASE",
+                "component_name": "HBASE_REGIONSERVER"
+              },
+              {
+                "name": "java.lang:type=Memory.HeapMemoryUsage[max]",
+                "widget_id": "metrics/jvm/HeapMemoryMax",
+                "category": "",
+                "service_name": "HBASE",
+                "component_name": "HBASE_REGIONSERVER"
+              }
+            ],
+            "values": [
+              {
+                "name": "NameNode heap",
+                "value": "${java.lang:type=Memory.HeapMemoryUsage[used]/java.lang:type=Memory.HeapMemoryUsage[max]}"
+              }
+            ],
+            "properties": {
+              "warning_threshold": 0.9,
+              "error_threshold": 0.7
+            }
           }
         ]
       }

+ 19 - 3
ambari-web/app/controllers/main/service/info/summary.js

@@ -320,7 +320,7 @@ App.MainServiceInfoSummaryController = Em.Controller.extend({
    * @type {Em.A}
    */
   widgetLayouts: function() {
-    return App.WidgetLayout.find().filterProperty('serviceName', this.get('content.serviceName'));
+    return App.WidgetLayout.find();
   }.property('isWidgetLayoutsLoaded'),
 
   /**
@@ -417,9 +417,25 @@ App.MainServiceInfoSummaryController = Em.Controller.extend({
 
   /**
    * save layout
+   * return {$.ajax}
    */
-  saveLayout: function () {
-
+  saveLayout: function (widgets, layout) {
+    var data = {
+      "layout_name": layout.get('layoutName'),
+      "section_name": layout.get('sectionName'),
+      "scope": layout.get('scope'),
+      "widgetLayoutInfo": widgets.map(function (widget) {
+        return {
+          "widget_name": widget.get('widgetName'),
+          "id": widget.get('widgetId')
+        }
+      })
+    };
+    return App.ajax.send({
+      name: 'widgets.layout.save',
+      sender: this,
+      data: data
+    });
   },
 
   /**

+ 21 - 4
ambari-web/app/styles/enhanced_service_dashboard.less

@@ -21,8 +21,8 @@
 .service-metrics-block {
 
   #add-widget-action-box {
-    margin: 20px 10px 10px 10px;
-    padding: 40px 55px;
+    width: 96%;
+    padding: 43px;
     border: 1px solid #ddd;
     .icon-plus {
       font-size: 70px;
@@ -51,6 +51,12 @@
     height: 150px;
     width: 90%;
   }
+  .span2p4 {
+    width: 19.3%;
+    background-color: white;
+    margin: 5px 0 5px 5px;
+    cursor: move;
+  }
   .widget {
     .spinner {
       margin: 55px auto;
@@ -68,11 +74,22 @@
       font-weight: bold;
       font-size: 35px;
     }
-    .template-widget {
+    .template-widget,
+    .number-widget {
       .frame;
     }
-    .number-widget {
+    .graph-widget {
       .frame;
+      .content {
+        padding-top: 0;
+      }
+      .chart-legend {
+        min-width: 100%;
+        padding: 5px;
+        text-align: left;
+        top: 95px;
+        left: -15px;
+      }
     }
     .gauge-widget {
       .frame;

+ 1 - 1
ambari-web/app/templates/common/widget/gauge_widget.hbs

@@ -18,7 +18,7 @@
 
 <div class="gauge-widget thumbnail">
   {{#if view.isLoaded}}
-    <div class="caption title">{{view.title}}</div>
+    <div class="caption title">{{view.content.displayName}}</div>
     <div class="content"> {{view view.chartView}}</div>
   {{else}}
     <div class="spinner"></div>

+ 26 - 0
ambari-web/app/templates/common/widget/graph_widget.hbs

@@ -0,0 +1,26 @@
+{{!
+* 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.
+}}
+
+<div class="graph-widget thumbnail">
+  {{#if view.isLoaded}}
+    <div class="caption title">{{view.content.displayName}}</div>
+    <div class="content"> {{view view.graphView}}</div>
+  {{else}}
+    <div class="spinner"></div>
+  {{/if}}
+</div>

+ 1 - 1
ambari-web/app/templates/common/widget/number_widget.hbs

@@ -18,7 +18,7 @@
 
 <div class="number-widget thumbnail">
   {{#if view.isLoaded}}
-    <div class="caption title">{{view.title}}</div>
+    <div class="caption title">{{view.content.displayName}}</div>
     <div {{bindAttr class="view.contentColor :content"}}>{{view.value}}</div>
   {{else}}
     <div class="spinner"></div>

+ 1 - 1
ambari-web/app/templates/common/widget/template_widget.hbs

@@ -18,7 +18,7 @@
 
 <div class="template-widget thumbnail">
   {{#if view.isLoaded}}
-    <div class="caption title">{{view.title}}</div>
+    <div class="caption title">{{view.content.displayName}}</div>
     <div class="content"> {{view.value}}</div>
   {{else}}
     <div class="spinner"></div>

+ 15 - 16
ambari-web/app/templates/main/service/info/summary.hbs

@@ -75,7 +75,6 @@
   </div>
 
   {{#if view.isServiceMetricLoaded}}
-    {{#if view.serviceMetricGraphs.length}}
       <div class="service-metrics-block">
         <div class="box">
           <div class="box-header">
@@ -140,21 +139,22 @@
               </div>
             {{/if}}
           </div>
-          <div class="">
+          <div>
+            {{#if App.supports.customizedWidgets}}
+              <div id="widget_layout" class="row-fluid">
+                {{#each widget in controller.widgets}}
+                  <div class="widget span2p4" {{bindAttr id="widget.id"}}>
+                    {{view widget.viewClass contentBinding="widget"}}
+                  </div>
+                {{/each}}
+                <div class="span2p4">
+                  <button id="add-widget-action-box"
+                          class="btn btn-default" {{action "goToAddWidgetWizard" controller.content target="view"}}><i
+                          class="icon-plus"></i></button>
+                </div>
+              </div>
+            {{/if}}
             <table class="graphs">
-              {{#if App.supports.customizedWidgets}}
-                {{#if controller.isWidgetsLoaded}}
-                  <tr id="widget_layout">
-                    {{#each widget in controller.widgets}}
-                      <td>
-                        <div class="widget">
-                          {{view widget.viewClass contentBinding="widget"}}
-                        </div>
-                      </td>
-                    {{/each}}
-                  </tr>
-                {{/if}}
-              {{/if}}
               {{#each graphs in view.serviceMetricGraphs}}
                 <tr>
                   {{#each graph in graphs}}
@@ -170,7 +170,6 @@
           </div>
         </div>
       </div>
-    {{/if}}
   {{/if}}
 </div>
 

+ 11 - 0
ambari-web/app/utils/ajax/ajax.js

@@ -2419,6 +2419,17 @@ var urls = {
     mock: '/data/widget_layouts/HBASE/layouts.json'
   },
 
+  'widgets.layout.save': {
+    real: '/clusters/{clusterName}/widget_layouts/{layout_name}',
+    mock: '',
+    format: function (data) {
+      return {
+        type: 'PUT',
+        data: JSON.stringify(data.data)
+      }
+    }
+  },
+
   'widgets.serviceComponent.metrics.get': {
     real: '/clusters/{clusterName}/services/{serviceName}/components/{componentName}?fields={widgetIds}',
     mock: '/data/metrics/{serviceName}/Append_num_ops_&_Delete_num_ops.json'

+ 8 - 1
ambari-web/app/views/common/chart/linear_time.js

@@ -137,6 +137,13 @@ App.ChartLinearTimeView = Ember.View.extend({
    */
   hasData: true,
 
+  /**
+   * chart height
+   * @type {number}
+   * @default 150
+   */
+  height: 150,
+
   didInsertElement: function () {
     this.loadData();
     this.registerGraph();
@@ -508,7 +515,7 @@ App.ChartLinearTimeView = Ember.View.extend({
     var yaxisElement = document.querySelector(yaxisElementId);
     var legendElement = document.querySelector(legendElementId);
 
-    var height = 150;
+    var height = this.get('height');
     var width = 400;
     var diff = 32;
 

+ 0 - 6
ambari-web/app/views/common/widget/gauge_widget_view.js

@@ -21,11 +21,6 @@ var App = require('app');
 App.GaugeWidgetView = Em.View.extend(App.WidgetMixin, {
   templateName: require('templates/common/widget/gauge_widget'),
 
-  /**
-   * @type {string}
-   */
-  title: '',
-
   /**
    * @type {string}
    */
@@ -40,7 +35,6 @@ App.GaugeWidgetView = Em.View.extend(App.WidgetMixin, {
   drawWidget: function () {
     if (this.get('isLoaded')) {
       this.calculateValues();
-      this.set('title', this.get('content.values')[0].name);
       this.set('value', this.get('content.values')[0].computedValue);
     }
   },

+ 52 - 31
ambari-web/app/views/common/widget/graph_widget_view.js

@@ -18,28 +18,8 @@
 
 var App = require('app');
 
-App.GraphWidgetView = App.ChartLinearTimeView.extend(App.WidgetMixin, {
-
-  /**
-   * @type {string}
-   */
-  id: function () {
-    return this.get('content.id');
-  }.property('content.id'),
-
-  /**
-   * @type {string}
-   */
-  title: function () {
-    return this.get('content.displayName');
-  }.property('content.displayName'),
-
-  /**
-   * @type {string}
-   */
-  renderer: function () {
-    return this.get('properties.graph_type') === 'STACK' ? 'area' : 'line';
-  }.property('properties.graph_type'),
+App.GraphWidgetView = Em.View.extend(App.WidgetMixin, {
+  templateName: require('templates/common/widget/graph_widget'),
 
   /**
    * common metrics container
@@ -59,15 +39,14 @@ App.GraphWidgetView = App.ChartLinearTimeView.extend(App.WidgetMixin, {
    */
   timeStep: 15,
 
-  didInsertElement: Em.K,
-
-  transformToSeries: function (seriesData) {
-    return seriesData;
-  },
+  /**
+   * @type {Array}
+   */
+  data: [],
 
   drawWidget: function () {
     if (this.get('isLoaded')) {
-      this._refreshGraph(this.calculateValues())
+      this.set('data', this.calculateValues());
     }
   },
 
@@ -80,9 +59,11 @@ App.GraphWidgetView = App.ChartLinearTimeView.extend(App.WidgetMixin, {
 
     this.get('content.values').forEach(function (value) {
       var computedExpressions = this.computeExpression(this.extractExpressions(value)[0], metrics);
-      seriesData.push(this.transformData(computedExpressions[value.value.match(this.get('EXPRESSION_REGEX'))[0]], value.name));
+      seriesData.push({
+        name: value.name,
+        data: computedExpressions[value.value.match(this.get('EXPRESSION_REGEX'))[0]]
+      });
     }, this);
-
     return seriesData;
   },
 
@@ -179,5 +160,45 @@ App.GraphWidgetView = App.ChartLinearTimeView.extend(App.WidgetMixin, {
     }, this);
 
     return result;
-  }
+  },
+
+  /**
+   * @type {Em.View}
+   * @class
+   */
+  graphView: App.ChartLinearTimeView.extend({
+
+    /**
+     * graph height
+     * @type {number}
+     */
+    height: 95,
+
+    /**
+     * @type {string}
+     */
+    id: function () {
+      return this.get('parentView.content.id') + '_graph';
+    }.property('parentView.content.id'),
+
+    /**
+     * @type {string}
+     */
+    renderer: function () {
+      return this.get('parentView.content.properties.graph_type') === 'STACK' ? 'area' : 'line';
+    }.property('parentView.content.properties.graph_type'),
+
+    transformToSeries: function (seriesData) {
+      var seriesArray = [];
+
+      seriesData.forEach(function(_series){
+        seriesArray.push(this.transformData(_series.data, _series.name));
+      }, this);
+      return seriesArray;
+    },
+
+    didInsertElement: function () {
+      this._refreshGraph(this.get('parentView.data'));
+    }.observes('parentView.data')
+  })
 });

+ 0 - 6
ambari-web/app/views/common/widget/number_widget_view.js

@@ -21,11 +21,6 @@ var App = require('app');
 App.NumberWidgetView = Em.View.extend(App.WidgetMixin, {
   templateName: require('templates/common/widget/number_widget'),
 
-  /**
-   * @type {string}
-   */
-  title: '',
-
   /**
    * @type {string}
    */
@@ -59,7 +54,6 @@ App.NumberWidgetView = Em.View.extend(App.WidgetMixin, {
     if (this.get('isLoaded')) {
       this.calculateValues();
       this.set('value', this.get('content.values')[0].computedValue);
-      this.set('title', this.get('content.values')[0].name);
     }
   }
 });

+ 0 - 6
ambari-web/app/views/common/widget/template_widget_view.js

@@ -21,11 +21,6 @@ var App = require('app');
 App.TemplateWidgetView = Em.View.extend(App.WidgetMixin, {
   templateName: require('templates/common/widget/template_widget'),
 
-  /**
-   * @type {string}
-   */
-  title: '',
-
   /**
    * @type {string}
    */
@@ -41,7 +36,6 @@ App.TemplateWidgetView = Em.View.extend(App.WidgetMixin, {
     if (this.get('isLoaded')) {
       this.calculateValues();
       this.set('value', this.get('content.values')[0].computedValue);
-      this.set('title', this.get('content.values')[0].name);
     }
   }
 });

+ 36 - 10
ambari-web/app/views/main/service/info/summary.js

@@ -17,6 +17,7 @@
 
 var App = require('app');
 var batchUtils = require('utils/batch_scheduled_requests');
+var misc = require('utils/misc');
 require('views/main/service/service');
 require('data/service_graph_config');
 
@@ -330,16 +331,6 @@ App.MainServiceInfoSummaryView = Em.View.extend(App.UserPref, {
         }));
       });
 
-      if (App.get('supports.customizedWidgets')) {
-        graphObjects.push(Ember.View.extend({
-          classNames: ['last-child'],
-          template: Ember.Handlebars.compile('<button id="add-widget-action-box" class="btn btn-default" {{action "goToAddWidgetWizard" controller.content target="view"}}><i class="icon-plus"></i></button>'),
-          goToAddWidgetWizard: function(evt) {
-            App.router.send('addServiceWidget',evt.context);
-          }
-        }));
-      }
-
       while(graphObjects.length) {
         result.push(graphObjects.splice(0, chunkSize));
       }
@@ -376,6 +367,15 @@ App.MainServiceInfoSummaryView = Em.View.extend(App.UserPref, {
       this.set('currentTimeRangeIndex', 0);
     }
   },
+
+  /**
+   * launch Add Widget wizard
+   * @param event
+   */
+  goToAddWidgetWizard: function (event) {
+    App.router.send('addServiceWidget', event.context);
+  },
+
   /**
    * list of static actions of widget
    * @type {Array}
@@ -569,5 +569,31 @@ App.MainServiceInfoSummaryView = Em.View.extend(App.UserPref, {
         }
       }
     }
+    this.makeSortable();
+  },
+
+  /**
+   * Make widgets' list sortable on New Dashboard style
+   */
+  makeSortable: function () {
+    var self = this;
+    $('html').on('DOMNodeInserted', '#widget_layout', function () {
+      $(this).sortable({
+        items: "> div",
+        cursor: "move",
+        tolerance: "pointer",
+        scroll: false,
+        update: function () {
+          var widgets = misc.sortByOrder($("#widget_layout .widget").map(function () {
+            return this.id;
+          }), self.get('controller.widgets'));
+          //TODO bind to actual layout instance
+          var layout = self.get('controller.widgetLayouts').objectAt(0);
+
+          self.get('controller').saveLayout(widgets, layout);
+        }
+      }).disableSelection();
+      $('html').off('DOMNodeInserted', '#widget_layout');
+    });
   }
 });