tez-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From prak...@apache.org
Subject tez git commit: TEZ-2345. Tez UI: Enable cell level loading in all DAGs table (Sreenath Somarajapuram via pramachandran)
Date Wed, 22 Apr 2015 08:28:09 GMT
Repository: tez
Updated Branches:
  refs/heads/master ec45c510c -> 9faa54c2c


TEZ-2345. Tez UI: Enable cell level loading in all DAGs table (Sreenath Somarajapuram via
pramachandran)


Project: http://git-wip-us.apache.org/repos/asf/tez/repo
Commit: http://git-wip-us.apache.org/repos/asf/tez/commit/9faa54c2
Tree: http://git-wip-us.apache.org/repos/asf/tez/tree/9faa54c2
Diff: http://git-wip-us.apache.org/repos/asf/tez/diff/9faa54c2

Branch: refs/heads/master
Commit: 9faa54c2c356cccbab7e2c2350bffc6fba7019bd
Parents: ec45c51
Author: Prakash Ramachandran <pramachandran@hortonworks.com>
Authored: Wed Apr 22 13:57:23 2015 +0530
Committer: Prakash Ramachandran <pramachandran@hortonworks.com>
Committed: Wed Apr 22 13:57:23 2015 +0530

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../app/scripts/controllers/dags_controller.js  | 199 +++++++++++--------
 .../app/scripts/mixins/paginated_content.js     |  83 +++-----
 .../main/webapp/app/scripts/views/dropdown.js   |  35 ++++
 tez-ui/src/main/webapp/app/styles/colors.less   |   1 +
 tez-ui/src/main/webapp/app/styles/main.less     | 186 +++++++++++------
 tez-ui/src/main/webapp/app/styles/shared.less   |  10 +-
 tez-ui/src/main/webapp/app/templates/dags.hbs   |  79 +++++++-
 8 files changed, 387 insertions(+), 207 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tez/blob/9faa54c2/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 35cf312..5c91872 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -9,6 +9,7 @@ INCOMPATIBLE CHANGES
   TEZ-1993. Implement a pluggable InputSizeEstimator for grouping fairly
 
 ALL CHANGES:
+  TEZ-2345. Tez UI: Enable cell level loading in all DAGs table
   TEZ-2330. Create reconfigureVertex() API for input based initialization
   TEZ-2292. Add e2e test for error reporting when vertex manager invokes
   plugin APIs

http://git-wip-us.apache.org/repos/asf/tez/blob/9faa54c2/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js b/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js
index f6a7013..94c6505 100644
--- a/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js
+++ b/tez-ui/src/main/webapp/app/scripts/controllers/dags_controller.js
@@ -28,7 +28,6 @@ App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin,
App.C
   // query parameters supported through url. The same named variables in this controller
get
   // bound automatically to the ones defined in the route.
   queryParams: {
-    fromID: true,
     status_filter: 'status',
     user_filter: 'user',
     appId_filter: 'appid',
@@ -38,14 +37,38 @@ App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin,
App.C
   fromID: null,
 
   status_filter: null,
-
   user_filter: null,
-
   appId_filter: null,
-
   dagName_filter: null,
 
-  fields: 'events,primaryfilters,otherinfo',
+  boundFilterValues: Em.Object.create({
+    status: null
+  }),
+  visibleFilters: null,
+
+  init: function () {
+    this._super();
+    this._filterVisiblilityObserver();
+  },
+
+  _paramObserver: function () {
+    this.set('boundFilterValues', Em.Object.create({
+      status: this.get('status_filter'),
+      user: this.get('user_filter'),
+      appId: this.get('appId_filter'),
+      dagName: this.get('dagName_filter')
+    }));
+  }.observes('status_filter', 'user_filter', 'appId_filter', 'dagName_filter'),
+
+  _filterVisiblilityObserver: function () {
+    var visibleFilters = Em.Object.create();
+    this.get('columns').forEach(function (column) {
+      if(column.get('enableFilter')) {
+        visibleFilters.set(column.get('id'), true);
+      }
+    });
+    this.set('visibleFilters', visibleFilters);
+  }.observes('columns'),
 
   loadData: function() {
     var filters = {
@@ -69,7 +92,6 @@ App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin,
App.C
     var that = this,
     store = this.get('store'),
     childEntityType = this.get('childEntityType'),
-    fetcher,
     record;
     var defaultErrMsg = 'Error while loading dag info.';
 
@@ -78,16 +100,22 @@ App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin,
App.C
     store.unloadAll('dagProgress');
 
     store.findQuery(childEntityType, this.getFilterProperties()).then(function(entities){
-      var loaders = [];
+      that.set('entities', entities);
+      that.set('loading', false);
+
       entities.forEach(function (dag) {
         var appId = dag.get('applicationId');
         if(appId) {
-          // Pivot attempt selection logic
           record = store.getById('appDetail', appId);
           if(record && !App.Helpers.misc.isStatusInUnsuccessful(record.get('appState')))
{
             store.unloadRecord(record);
           }
-          fetcher = store.find('appDetail', appId).then(function (app) {
+        }
+      });
+      entities.forEach(function (dag) {
+        var appId = dag.get('applicationId');
+        if(appId) {
+          store.find('appDetail', appId).then(function (app) {
             dag.set('appDetail', app);
             if (dag.get('status') === 'RUNNING') {
               dag.set('status', App.Helpers.misc.getRealStatus(
@@ -95,17 +123,15 @@ App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin,
App.C
                 app.get('appState'),
                 app.get('finalAppStatus')
               ));
-              App.Helpers.misc.removeRecord(store, 'tezApp', 'tez_' + appId);
             }
-            return store.find('tezApp', 'tez_' + appId).then(function (app) {
-              dag.set('tezApp', app);
-            });
+          })
+          .catch(function(error) {
+            Em.Logger.error('Failed to fetch appDetail' + error);
           });
-          loaders.push(fetcher);
-          //Load tezApp details
+
           if (dag.get('status') === 'RUNNING') {
             App.Helpers.misc.removeRecord(store, 'dagProgress', dag.get('id'));
-            amInfoFetcher = store.find('dagProgress', dag.get('id'), {
+            store.find('dagProgress', dag.get('id'), {
               appId: dag.get('applicationId'),
               dagIdx: dag.get('idx')
             })
@@ -115,32 +141,29 @@ App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin,
App.C
             .catch(function(error) {
               Em.Logger.error('Failed to fetch dagProgress' + error);
             });
-            loaders.push(amInfoFetcher);
           }
         }
       });
-      Em.RSVP.allSettled(loaders).then(function(){
-        that.set('entities', entities);
-        that.set('loading', false);
-      });
     }).catch(function(error){
       Em.Logger.error(error);
       var err = App.Helpers.misc.formatError(error, defaultErrMsg);
       var msg = 'error code: %@, message: %@'.fmt(err.errCode, err.msg);
       App.Helpers.ErrorBar.getInstance().show(msg, err.details);
     });
-  },
+  }.observes('fields'),
 
   actions : {
-    filterUpdated: function(filterID, value) {
-      // any validations required goes here.
-      if (!!value) {
-        this.set(filterID, value);
-      } else {
-        this.set(filterID, null);
-      }
+    filterUpdated: function() {
+      Em.run.later();
+      var filterValues = this.get('boundFilterValues');
+      this.setProperties({
+        status_filter: filterValues.get('status') || null,
+        user_filter: filterValues.get('user') || null,
+        appId_filter: filterValues.get('appId') || null,
+        dagName_filter: filterValues.get('dagName') || null,
+      });
       this.loadData();
-    },
+    }
   },
 
   /*
@@ -148,19 +171,32 @@ App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin,
App.C
    * @return Array of column configs
    */
   defaultColumnConfigs: function () {
+    var store = this.get('store');
+
+    function onProgressChange() {
+      var progress = this.get('dag.progress'),
+          pct;
+      if (Ember.typeOf(progress) === 'number') {
+        pct = App.Helpers.number.fractionToPercentage(progress);
+        this.set('progress', pct);
+      }
+    }
+
+    function onStatusChange() {
+      this.set('status', this.get('dag.status'));
+    }
+
     return [
       {
         id: 'dagName',
         headerCellName: 'Dag Name',
-        filterID: 'dagName_filter',
-        tableCellViewClass: Em.Table.TableCell.extend({
-          template: Em.Handlebars.compile(
-            "{{#link-to 'dag.index' view.cellContent.id class='ember-table-content'}}{{view.cellContent.name}}{{/link-to}}")
-        }),
+        templateName: 'components/basic-table/linked-cell',
+        enableFilter: true,
         getCellContent: function(row) {
           return {
-            id: row.get('id'),
-            name: row.get('name')
+            linkTo: 'dag.index',
+            entityId: row.get('id'),
+            displayText: row.get('name')
           };
         }
       },
@@ -172,38 +208,34 @@ App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin,
App.C
       {
         id: 'user',
         headerCellName: 'Submitter',
-        filterID: 'user_filter',
-        contentPath: 'user'
+        contentPath: 'user',
+        enableFilter: true
       },
       {
         id: 'status',
         headerCellName: 'Status',
-        filterID: 'status_filter',
-        filterType: 'dropdown',
-        dropdownValues: App.Helpers.misc.dagStatusUIOptions,
-        tableCellViewClass: Em.Table.TableCell.extend({
-          template: Em.Handlebars.compile(
-            '<span class="ember-table-content">&nbsp;\
-            <i {{bind-attr class=":task-status view.cellContent.statusIcon"}}></i>\
-            &nbsp;&nbsp;{{view.cellContent.status}}\
-            {{#if view.cellContent.progress}} {{bs-badge content=view.cellContent.progress}}{{/if}}</span>')
-        }),
+        templateName: 'components/basic-table/status-cell',
+        enableFilter: true,
         getCellContent: function(row) {
-          var pct;
-          if (Ember.typeOf(row.get('progress')) === 'number') {
-            pct = App.Helpers.number.fractionToPercentage(row.get('progress'));
+          var status = row.get('status'),
+              content = Ember.Object.create({
+                dag: row,
+                status: status,
+                statusIcon: App.Helpers.misc.getStatusClassForEntity(status)
+              });
+
+          if(status == 'RUNNING') {
+            row.addObserver('progress', content, onProgressChange);
+            row.addObserver('status', content, onStatusChange);
           }
-          var dagStatus = row.get('status');
-          return {
-            status: dagStatus,
-            statusIcon: App.Helpers.misc.getStatusClassForEntity(dagStatus),
-            progress: pct
-          };
+
+          return content;
         }
       },
       {
         id: 'startTime',
         headerCellName: 'Start Time',
+        contentPath: 'startTime',
         getCellContent: function(row) {
           return App.Helpers.date.dateFormat(row.get('startTime'));
         }
@@ -219,37 +251,44 @@ App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin,
App.C
         id: 'duration',
         headerCellName: 'Duration',
         getCellContent: function(row) {
-          var st = row.get('startTime');
-          var et = row.get('endTime');
-          if (st && et) {
-            return App.Helpers.date.durationSummary(st, et);
-          }
+          return App.Helpers.date.timingFormat(row.get('duration'), 1);
         }
       },
       {
         id: 'appId',
         headerCellName: 'Application ID',
-        filterID: 'appId_filter',
-        tableCellViewClass: Em.Table.TableCell.extend({
-          template: Em.Handlebars.compile(
-            "{{#if view.cellContent.enableLink}}\
-               {{#link-to 'tez-app' view.cellContent.appId class='ember-table-content'}}{{view.cellContent.appId}}{{/link-to}}\
-             {{else}}\
-               <span class='ember-table-content'>{{view.cellContent.appId}}</span>\
-             {{/if}}")
-        }),
+        templateName: 'components/basic-table/linked-cell',
+        enableFilter: true,
         getCellContent: function(row) {
-          return  {
-            enableLink: row.get('tezApp') && row.get('appDetail'),
-            appId: row.get('applicationId')
+          var appId = row.get('applicationId');
+          if(appId) {
+            return Em.RSVP.allSettled([
+              store.find('appDetail', appId),
+              store.find('tezApp', 'tez_' + appId)
+            ]).then(function (response) {
+              var content = {
+                displayText: row.get('applicationId'),
+                entityId: row.get('applicationId')
+              };
+              if(response.get('0.value') && response.get('1.value')) {
+                content.linkTo = 'tez-app';
+              }
+              return content;
+            });
           }
         }
       },
       {
         id: 'queue',
         headerCellName: 'Queue',
+        templateName: 'components/basic-table/bounded-basic-cell',
         getCellContent: function(row) {
-          return row.get('appDetail.queue') || 'Not Available';
+          var appId = row.get('applicationId');
+          if(appId) {
+            return store.find('appDetail', appId).then(function (app) {
+              return app.get('queue');
+            });
+          }
         }
       }
     ];
@@ -266,12 +305,4 @@ App.DagsController = Em.ObjectController.extend(App.PaginatedContentMixin,
App.C
     );
   }.property(),
 
-  columns: function() {
-    var visibleColumnConfigs = this.get('columnConfigs').filter(function (column) {
-      return this.visibleColumnIds[column.id];
-    }, this);
-
-    return App.Helpers.misc.createColumnsFromConfigs(visibleColumnConfigs);
-  }.property('visibleColumnIds'),
-
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/9faa54c2/tez-ui/src/main/webapp/app/scripts/mixins/paginated_content.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/mixins/paginated_content.js b/tez-ui/src/main/webapp/app/scripts/mixins/paginated_content.js
index 5e158e1..13e27cc 100644
--- a/tez-ui/src/main/webapp/app/scripts/mixins/paginated_content.js
+++ b/tez-ui/src/main/webapp/app/scripts/mixins/paginated_content.js
@@ -19,13 +19,13 @@
 App.PaginatedContentMixin = Em.Mixin.create({
   // paging related values. These are bound automatically to the values in url. via the queryParams
   // defined in the route.
-  count: 10,
+  rowCount: 10,
 
   page: 1,
   fromID: null,
 
   // The dropdown contents for number of items to show.
-  countOptions: [5, 10, 25, 50, 100],
+  rowCountOptions: [5, 10, 25, 50, 100],
 
   isRefreshable: true,
 
@@ -33,14 +33,10 @@ App.PaginatedContentMixin = Em.Mixin.create({
    * store the first dag id on a page so that we can navigate back and store the last one

    * (not shown on page to get the id where next page starts)
    */
-  navIDs: {
-    prevIDs: [],
-    currentID: undefined,
-    nextID: undefined
-  },
+  navIDs: [],
 
   queryParams: {
-    count: true,
+    rowCount: true,
   },
 
   entities: [],
@@ -50,7 +46,11 @@ App.PaginatedContentMixin = Em.Mixin.create({
   load: function() {
     this.resetNavigation();
     this.loadEntities();
-  }.observes('count'),
+  }.observes('rowCount'),
+
+  lastPage: function () {
+    return this.get('navIDs.length') + 1;
+  }.property('navIDs.length'),
 
   sortedContent: function() {
     // convert to a ArrayController. we do not sort at this point as the data is
@@ -59,7 +59,7 @@ App.PaginatedContentMixin = Em.Mixin.create({
       model: this.get('entities')
     });
     this.updatePagination(sorted.toArray());
-    return sorted.slice(0, this.count);
+    return sorted.slice(0, this.rowCount);
   }.property('entities', 'numEntities'),
 
   updateLoading: function () {
@@ -104,65 +104,36 @@ App.PaginatedContentMixin = Em.Mixin.create({
   },
 
   resetNavigation: function() {
-    this.set('navIDs.prevIDs', []);
-    this.set('navIDs.currentID', '');
-    this.set('navIDs.nextID', '');
+    this.set('navIDs', []);
     this.set('fromID', null);
     this.set('page', 1);
   },
 
   updatePagination: function(dataArray) {
-    if (!!dataArray && dataArray.get('length') > 0) {
-      this.set('navIDs.currentID', dataArray.objectAt(0).get('id'));
-      var nextID = undefined;
-      if (dataArray.get('length') > this.count) {
-        // save the last id, so that we can use that as firt id on next page.
-        nextID = dataArray.objectAt(this.count).get('id');
+    var nextFromId = null,
+        navIDs = this.get('navIDs'),
+        rowCount = this.get('rowCount');
+
+    if(dataArray && dataArray.length == rowCount + 1) {
+      nextFromId = dataArray.objectAt(rowCount).get('id');
+      if (navIDs.indexOf(nextFromId) == -1 &&
+          this.get('page') >= navIDs.get('length')) {
+          navIDs.pushObject(nextFromId);
       }
-      this.set('navIDs.nextID', nextID);
     }
   },
 
-  hasPrev: function() {
-    return this.navIDs.prevIDs.length > 0;
-  }.property('navIDs.prevIDs.[]', 'navIDs.prevIDs.length', 'fromID', 'page'),
-
-  hasNext: function() {
-    return !!this.navIDs.nextID;
-  }.property('navIDs.nextID'),
-
   actions:{
-    // go to previous page
-    navigatePrev: function () {
-      var prevPageId = this.navIDs.prevIDs.popObject();
-      this.set('fromID', prevPageId);
-      this.set('loading', true);
-      this.set('page', this.get('page') - 1);
-      this.loadEntities();
-    },
-
     refresh: function () {
       this.load();
     },
 
-    // goto first page.
-    navigateFirst: function() {
-      var firstPageId = this.navIDs.prevIDs[0];
-      this.set('navIDs.prevIDs', []);
-      this.set('fromID', firstPageId);
+    changePage: function (pageNum) {
+      this.set('fromID', this.get('navIDs.' + (pageNum - 2)) || null);
       this.set('loading', true);
-      this.set('page', 1);
+      this.set('page', pageNum);
       this.loadEntities();
-    },
-
-    // go to next page
-    navigateNext: function () {
-      this.navIDs.prevIDs.pushObject(this.navIDs.currentID);
-      this.set('fromID', this.get('navIDs.nextID'));
-      this.set('loading', true);
-      this.set('page', this.get('page') + 1);
-      this.loadEntities();
-    },
+    }
   },
 
   _concatFilters: function(obj) {
@@ -177,7 +148,7 @@ App.PaginatedContentMixin = Em.Mixin.create({
 
   getFilterProperties: function() {
     var params = {
-      limit: this.count + 1
+      limit: this.rowCount + 1
     };
 
     var f = this._paginationFilters;
@@ -213,8 +184,8 @@ App.PaginatedContentMixin = Em.Mixin.create({
       params['secondaryFilter'] = secondary;
     }
 
-    if (!Em.empty(this.fromID)) {
-      params['fromId'] = this.fromID;
+    if (!Em.empty(this.get('fromID'))) {
+      params['fromId'] = this.get('fromID');
     }
 
     return params;

http://git-wip-us.apache.org/repos/asf/tez/blob/9faa54c2/tez-ui/src/main/webapp/app/scripts/views/dropdown.js
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/scripts/views/dropdown.js b/tez-ui/src/main/webapp/app/scripts/views/dropdown.js
new file mode 100644
index 0000000..be97507
--- /dev/null
+++ b/tez-ui/src/main/webapp/app/scripts/views/dropdown.js
@@ -0,0 +1,35 @@
+/**
+ * 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.
+ */
+
+App.Dropdown = Em.Select.extend({
+  currentValue: null,
+  init: function () {
+    this._super();
+    this.set('currentValue', this.get('value'));
+  },
+  change: function() {
+    var value = this.get('value'),
+        target = this.get('target') || this.get('context');
+
+    if(target && value != this.get('currentValue')) {
+      Em.run.later(target.send.bind(target, this.get('action'), value), 100);
+      this.set('currentValue', value);
+    }
+    return true;
+  }
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/9faa54c2/tez-ui/src/main/webapp/app/styles/colors.less
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/styles/colors.less b/tez-ui/src/main/webapp/app/styles/colors.less
index 9bf4b61..aa0d96a 100644
--- a/tez-ui/src/main/webapp/app/styles/colors.less
+++ b/tez-ui/src/main/webapp/app/styles/colors.less
@@ -32,6 +32,7 @@
 
 @text-color: #666666;
 @text-red: red;
+@text-light: #BBBBBB;
 
 @top-nav-bg-color-from: #d5d5d5;
 @top-nav-bg-color-to: #f0f0f0;

http://git-wip-us.apache.org/repos/asf/tez/blob/9faa54c2/tez-ui/src/main/webapp/app/styles/main.less
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/styles/main.less b/tez-ui/src/main/webapp/app/styles/main.less
index 4687ac4..8204b19 100644
--- a/tez-ui/src/main/webapp/app/styles/main.less
+++ b/tez-ui/src/main/webapp/app/styles/main.less
@@ -56,16 +56,6 @@ body, html {
     }
   }
 
-  .table-control {
-    .align-children-left {
-      vertical-align: top;
-      padding-top: 4px;
-    }
-    .align-children-right {
-      margin-top: -8px;
-    }
-  }
-
   .load-component {
     margin-bottom: 5px;
 
@@ -392,6 +382,9 @@ body, html {
 .task-status {
   .fa;
   .fa-lg;
+  .absolute;
+
+  top: 7px;
 
   &.success {
     .fa-icon(check-circle);
@@ -424,6 +417,9 @@ body, html {
     color: @unknown-color;
   }
 }
+.status-msg {
+  margin-left: 20px;
+}
 
 .fa-action {
   .fa;
@@ -531,6 +527,113 @@ div.indent {
   color: black;
 }
 
+.row-select {
+  margin-left: 5px;
+
+  display: inline-block;
+  text-align: center;
+
+  :first-child {
+    margin-bottom: -3px;
+    font-size: .85em;
+  }
+}
+
+.dag-header {
+  .margin-small-horizontal;
+
+  .align-children-left {
+    width: 65%;
+    .inline-block;
+  }
+  .align-children-right {
+    width: 35%;
+    .inline-block;
+  }
+
+  margin-bottom: -5px;
+  margin-top: 10px;
+
+  .pagination-view {
+    .page-list {
+      margin-bottom: 2px;
+    }
+  }
+
+  .filter-elements {
+    .inline-block;
+
+    margin-left: 5px;
+    display: inline-block;
+
+    input[type=text], select {
+      width: 110px;
+    }
+    input, select {
+      height: 20px;
+      border-radius: 5px;
+      border: 1px solid @border-color;
+    }
+
+    .load-counter-check {
+      margin-left: 10px;
+      vertical-align: sub;
+    }
+
+    :first-child {
+      margin-bottom: -3px;
+      font-size: .85em;
+
+      padding: 2px;
+    }
+  }
+}
+
+.pagination-view {
+  .inline-block;
+
+  .page-list {
+    .inline-block;
+    .align-top;
+
+    overflow: hidden;
+
+    border: 1px solid @border-color;
+    border-radius: 5px;
+
+    padding: 0px;
+
+    font-size: 0px;
+
+    li {
+      .inline-block;
+
+      padding: 6px 10px;
+      font-size: 14px;
+
+      border-left: 1px solid @border-color;
+
+      pointer-events: none;
+
+      &.clickable {
+        pointer-events: auto;
+        &:hover {
+          background-color: @bg-grey;
+          cursor: pointer;
+        }
+      }
+    }
+
+    .total-page-count {
+      font-size: .8em;
+    }
+
+    :first-child {
+      border-left: none;
+    }
+  }
+}
+
 .table-container {
   .use-gpu;
 
@@ -599,61 +702,7 @@ div.indent {
     .fa-icon(spinner);
     .fa-spin;
     .fa-fw;
-  }
-
-  .pagination-view {
-    .inline-block;
-
-    .page-list {
-      .inline-block;
-      .align-top;
-
-      border: 1px solid @border-color;
-      border-radius: 5px;
-
-      padding: 0px;
-
-      font-size: 0px;
-
-      li {
-        .inline-block;
-
-        padding: 6px 10px;
-        font-size: 14px;
-
-        border-left: 1px solid @border-color;
-
-        pointer-events: none;
-
-        &.clickable {
-          pointer-events: auto;
-          &:hover {
-            background-color: @bg-grey;
-            cursor: pointer;
-          }
-        }
-      }
-
-      .total-page-count {
-        font-size: .8em;
-      }
-
-      :first-child {
-        border-left: none;
-      }
-    }
-
-    .row-select {
-      margin-left: 5px;
-
-      display: inline-block;
-      text-align: center;
-
-      :first-child {
-        margin-bottom: -3px;
-        font-size: .85em;
-      }
-    }
+    .absolute;
   }
 
   .table-body-container{
@@ -689,6 +738,13 @@ div.indent {
             text-overflow: ellipsis;
             white-space: nowrap;
             height: 18px;
+
+            .message {
+              color: @text-light;
+              .waiting {
+                color: @text-color;
+              }
+            }
           }
 
           .sort-icon {

http://git-wip-us.apache.org/repos/asf/tez/blob/9faa54c2/tez-ui/src/main/webapp/app/styles/shared.less
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/styles/shared.less b/tez-ui/src/main/webapp/app/styles/shared.less
index cebd837..7cece16 100644
--- a/tez-ui/src/main/webapp/app/styles/shared.less
+++ b/tez-ui/src/main/webapp/app/styles/shared.less
@@ -37,6 +37,10 @@
   visibility: hidden !important;
 }
 
+.no-margin {
+  margin: 0px !important;
+}
+
 .no-pointer {
   pointer-events: none;
 }
@@ -45,10 +49,14 @@
   white-space: nowrap;
 }
 
-.align-top{
+.align-top {
   vertical-align: top;
 }
 
+.align-super {
+  vertical-align: super;
+}
+
 .inline-block {
   display: inline-block;
 }

http://git-wip-us.apache.org/repos/asf/tez/blob/9faa54c2/tez-ui/src/main/webapp/app/templates/dags.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/src/main/webapp/app/templates/dags.hbs b/tez-ui/src/main/webapp/app/templates/dags.hbs
index d0e83c4..727e28a 100644
--- a/tez-ui/src/main/webapp/app/templates/dags.hbs
+++ b/tez-ui/src/main/webapp/app/templates/dags.hbs
@@ -21,5 +21,82 @@
 </ul>
 
 <div class='margin-small-vertical'>
-  {{partial 'common/table-with-spinner'}}
+  {{#unless loading}}
+    {{load-time-component
+      isRefreshable=isRefreshable
+      time=sortedContent.0.timeStamp
+      refresh='refresh'
+    }}
+    <div class='dag-header'>
+      <div class="align-children-left">
+        {{#if visibleFilters.dagName}}
+          <div class='filter-elements'>
+            <div>Dag Name</div>
+            {{input
+              action="filterUpdated"
+              value=boundFilterValues.dagName
+              placeholder="Search..."
+            }}
+          </div>
+        {{/if}}
+        {{#if visibleFilters.user}}
+          <div class='filter-elements'>
+            <div>Submitter</div>
+            {{input
+              action="filterUpdated"
+              value=boundFilterValues.user
+              placeholder="Search..."
+            }}
+          </div>
+        {{/if}}
+        {{#if visibleFilters.status}}
+          <div class='filter-elements'>
+            <div>Status</div>
+            {{view App.Dropdown
+              optionValuePath='content.id'
+              optionLabelPath='content.label'
+              classNames='inline-display'
+              action='filterUpdated'
+              content=App.Helpers.misc.dagStatusUIOptions
+              value=boundFilterValues.status
+            }}
+          </div>
+        {{/if}}
+        {{#if visibleFilters.appId}}
+          <div class='filter-elements'>
+            <div>Application ID</div>
+            {{input
+              action="filterUpdated"
+              value=boundFilterValues.appId
+              placeholder="Search..."
+            }}
+          </div>
+        {{/if}}
+      </div><div class="align-children-right">
+        {{view App.BasicTableComponent.PaginationView
+          pageNum=page
+          totalPages=lastPage
+          hideLast=true
+        }}
+        <i {{bind-attr class=':fa-action :fa-cog :left-divider'}} {{action 'selectColumns'}}></i>
+      </div>
+    </div>
+    {{basic-table-component
+      columns=columns
+      rows=sortedContent
+
+      rowCountBinding='rowCount'
+
+      statusMessage=statusMessage
+    }}
+    {{#unless sortedContent.length}}
+      <h1 class="no-margin">No records available!</h1>
+    {{/unless}}
+  {{else}}
+    {{partial 'partials/loading-spinner'}}
+    <div class="text-align-center">
+      {{statusMessage}}
+    </div>
+  {{/unless}}
+
 </div>


Mime
View raw message