You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tez.apache.org by je...@apache.org on 2014/12/10 04:33:35 UTC

[12/53] tez git commit: TEZ-1605. Landing page for Tez UI (Prakash Ramachandran via jeagles)

TEZ-1605. Landing page for Tez UI (Prakash Ramachandran via jeagles)


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

Branch: refs/heads/master
Commit: f6d851730a204738ec77c57f49af599c1fb11a8a
Parents: c9fefa6
Author: Jonathan Eagles <je...@gmail.com>
Authored: Fri Oct 10 10:40:32 2014 -0500
Committer: Jonathan Eagles <je...@gmail.com>
Committed: Fri Oct 10 10:40:32 2014 -0500

----------------------------------------------------------------------
 tez-ui/Gruntfile.js                             |  11 +-
 tez-ui/README.TXT                               |   4 +-
 tez-ui/app/index.html                           |   1 +
 tez-ui/app/scripts/app.js                       |   2 +-
 tez-ui/app/scripts/components/counter-table.js  |  83 ++++++
 tez-ui/app/scripts/components/page-nav.js       |   6 +-
 tez-ui/app/scripts/components/tasks-view.js     |   4 +
 .../app/scripts/controllers/dag_controller.js   |   8 +-
 tez-ui/app/scripts/controllers/dag_counters.js  |  23 ++
 .../scripts/controllers/dag_index_controller.js |  85 +++---
 .../app/scripts/controllers/dags_controller.js  |  56 ++--
 .../controllers/show_tasks_view_controller.js   | 135 ++++++++++
 .../controllers/task_attempt_controller.js      |  39 +++
 .../app/scripts/controllers/task_controller.js  |  39 +++
 .../controllers/task_index_controller.js        |  30 +++
 .../app/scripts/controllers/tasks_controller.js | 131 +++++++++
 tez-ui/app/scripts/helpers/ajax.js              | 179 -------------
 tez-ui/app/scripts/helpers/handlebarHelpers.js  |  15 +-
 tez-ui/app/scripts/helpers/misc.js              |  57 ++++
 tez-ui/app/scripts/mappers/dag_mapper.js        |  56 ----
 .../app/scripts/models/TimelineRestAdapter.js   | 263 +++++++++++++------
 tez-ui/app/scripts/models/dag.js                |  14 +
 tez-ui/app/scripts/models/task_attempt.js       |   6 +-
 tez-ui/app/scripts/router.js                    |  39 ++-
 tez-ui/app/scripts/views/show_tasks_view.js     |  81 ++++++
 tez-ui/app/scripts/views/tasks_view.js          |  23 ++
 tez-ui/app/styles/main.css                      |  70 -----
 tez-ui/app/styles/main.less                     | 133 ++++++++++
 tez-ui/app/templates/components/page-nav.hbs    |   7 +-
 tez-ui/app/templates/dag.hbs                    |  11 +-
 tez-ui/app/templates/dag/counters.hbs           |  21 ++
 tez-ui/app/templates/dag/index.hbs              | 122 ++++++---
 tez-ui/app/templates/dag/vertex.hbs             |   2 +-
 tez-ui/app/templates/dags.hbs                   |   4 +-
 tez-ui/app/templates/task.hbs                   |  62 +++++
 tez-ui/app/templates/task/counters.hbs          |  21 ++
 tez-ui/app/templates/task/index.hbs             |  59 +++++
 tez-ui/app/templates/taskAttempt/counters.hbs   |  21 ++
 tez-ui/app/templates/task_attempt.hbs           |  51 ++++
 tez-ui/app/templates/tasks.hbs                  |  58 ++++
 tez-ui/app/templates/views/show_tasks.hbs       |  38 +++
 41 files changed, 1553 insertions(+), 517 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/Gruntfile.js
----------------------------------------------------------------------
diff --git a/tez-ui/Gruntfile.js b/tez-ui/Gruntfile.js
index a6d070a..3d3b96f 100644
--- a/tez-ui/Gruntfile.js
+++ b/tez-ui/Gruntfile.js
@@ -204,7 +204,8 @@ module.exports = function (grunt) {
             '.tmp/styles/{,*/}*.css',
             '<%= yeoman.app %>/styles/{,*/}*.css',
             '<%= yeoman.app %>/bower_components/bootstrap/dist/css/bootstrap.css',
-            '<%= yeoman.app %>/bower_components/ember-table/dist/ember-table.css'
+            '<%= yeoman.app %>/bower_components/ember-table/dist/ember-table.css',
+            '<%= yeoman.app %>/bower_components/font-awesome/css/font-awesome.css'
           ]
         }
       }
@@ -282,8 +283,8 @@ module.exports = function (grunt) {
           {
             expand: true,
             flatten: true,
-            src: '<%= yeoman.app %>/bower_components/font-awesome/font/*',
-            dest: '<%= yeoman.dist %>/font/'
+            src: '<%= yeoman.app %>/bower_components/font-awesome/fonts/*',
+            dest: '<%= yeoman.dist %>/fonts/'
           }
         ]
       },
@@ -312,8 +313,8 @@ module.exports = function (grunt) {
           {
             expand: true,
             flatten: true,
-            src: '<%= yeoman.app %>/bower_components/font-awesome/font/*',
-            dest: '.tmp/font/'
+            src: '<%= yeoman.app %>/bower_components/font-awesome/fonts/*',
+            dest: '.tmp/fonts/'
           },
           {
             expand: true,

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/README.TXT
----------------------------------------------------------------------
diff --git a/tez-ui/README.TXT b/tez-ui/README.TXT
index 4e00df0..a633648 100644
--- a/tez-ui/README.TXT
+++ b/tez-ui/README.TXT
@@ -9,8 +9,8 @@ The timeline server (ATS) url by default points to http://localhost:8188. This c
 changed to point to another host by editing the App.AtsBaseUrl entry in the
 app/scripts/app.js file.
 
-For development run 'grunt serve'. This runs a dev server on port 9000. 
-navigate to http://localhost:9000 if a browser does not open automatically.
+For development run 'grunt serve'. This runs a dev server on port 9001. 
+navigate to http://localhost:9001 if a browser does not open automatically.
 Any changes made will be live-reloaded on the browser.
 
 For creating a distribution version, run 'grunt build'. The distributable 

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/index.html
----------------------------------------------------------------------
diff --git a/tez-ui/app/index.html b/tez-ui/app/index.html
index 1ee7859..1653e94 100644
--- a/tez-ui/app/index.html
+++ b/tez-ui/app/index.html
@@ -47,6 +47,7 @@
     <script src="bower_components/ember-json-mapper/ember-json-mapper.js"></script>
     <script src="bower_components/ember-i18n/lib/i18n.js"></script>
     <script src="bower_components/ember-addons.bs_for_ember/dist/js/bs-core.min.js"></script>
+    <script src="bower_components/ember-addons.bs_for_ember/dist/js/bs-basic.min.js"></script>
     <script src="bower_components/ember-addons.bs_for_ember/dist/js/bs-button.min.js"></script>
     <script src="bower_components/ember-addons.bs_for_ember/dist/js/bs-modal.min.js"></script>
     <script src="bower_components/ember-addons.bs_for_ember/dist/js/bs-nav.min.js"></script>

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/app.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/app.js b/tez-ui/app/scripts/app.js
index d8b47d9..c06678e 100644
--- a/tez-ui/app/scripts/app.js
+++ b/tez-ui/app/scripts/app.js
@@ -42,8 +42,8 @@ App.Mappers = Em.Namespace.create();
 require('scripts/translations');
 require('scripts/mixins/*');
 require('scripts/helpers/*');
-require('scripts/models/**/*');
 require('scripts/views/**/*');
+require('scripts/models/**/*');
 require('scripts/mappers/server_data_mapper.js');
 require('scripts/mappers/**/*');
 require('scripts/controllers/**/*');

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/components/counter-table.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/components/counter-table.js b/tez-ui/app/scripts/components/counter-table.js
new file mode 100644
index 0000000..2ee6b76
--- /dev/null
+++ b/tez-ui/app/scripts/components/counter-table.js
@@ -0,0 +1,83 @@
+/**
+ * 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.CounterTableComponent = Ember.Table.EmberTableComponent.extend({
+	hasFooter: false,
+	hasHeader: true,
+	forceFillColumns: true,
+	data: null,
+
+	columns: function() {
+		var groupColumn = Em.Table.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Group',
+      getCellContent: function(row) {
+      	return row.get('counterGroup');
+      }
+    });
+
+		var nameColumn = Em.Table.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Counter Name',
+      tableCellViewClass: Em.Table.TableCell.extend({
+        template: Em.Handlebars.compile(
+          '<span {{bind-attr class=":ember-table-content view.cellContent.isCG:countertable-group-header:countertable-row"}}>\
+          	{{view.cellContent.name}}\
+           </span>')
+      }),
+      getCellContent: function(row) {
+      	return {
+      		isCG: row.get('counters') != undefined,
+      		name: row.get('name')
+      	};
+      }
+    });
+
+		var valueColumn = Em.Table.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Value',
+      tableCellViewClass: Em.Table.TableCell.extend({
+        template: Em.Handlebars.compile(
+          '<span {{bind-attr class=":ember-table-content view.cellContent.isCG:countertable-group-header"}}>\
+          	{{view.cellContent.value}}\
+           </span>')
+      }),
+      getCellContent: function(row) {
+      	return {
+      		isCG: row.get('counters') != undefined,
+      		value: row.get('value')
+      	};
+      }
+    });
+
+    return [nameColumn, valueColumn];
+	}.property(),
+
+	content: function() {
+		var allCounters = [];
+		if (!!this.data) {
+			this.data.forEach(function(cg){
+				allCounters.push(cg);
+				[].push.apply(allCounters, cg.get('counters').content);
+			});
+		}
+		return allCounters;
+	}.property('data'),
+});
+
+Em.Handlebars.helper('counter-table-component', App.CounterTableComponent);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/components/page-nav.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/components/page-nav.js b/tez-ui/app/scripts/components/page-nav.js
index b7c21a6..a6c0f68 100644
--- a/tez-ui/app/scripts/components/page-nav.js
+++ b/tez-ui/app/scripts/components/page-nav.js
@@ -17,6 +17,8 @@
  */
 
 App.PageNavComponent = Em.Component.extend({
+	layoutName: 'components/page-nav',
+	
 	actions: {
 		gotoNext: function() {
 			this.sendAction('navNext');
@@ -28,4 +30,6 @@ App.PageNavComponent = Em.Component.extend({
 			this.sendAction('navFirst');
 		}
 	}
-});
\ No newline at end of file
+});
+
+Em.Handlebars.helper('page-nav-component', App.PageNavComponent);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/components/tasks-view.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/components/tasks-view.js b/tez-ui/app/scripts/components/tasks-view.js
new file mode 100644
index 0000000..494dcfa
--- /dev/null
+++ b/tez-ui/app/scripts/components/tasks-view.js
@@ -0,0 +1,4 @@
+App.ShowTasksComponent = Ember.Component.extend({
+	foo: function() {
+	}.on('didInsertElement')
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/controllers/dag_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/controllers/dag_controller.js b/tez-ui/app/scripts/controllers/dag_controller.js
index 1e2e4f2..414bc1d 100644
--- a/tez-ui/app/scripts/controllers/dag_controller.js
+++ b/tez-ui/app/scripts/controllers/dag_controller.js
@@ -17,9 +17,9 @@
  */
 
 App.DagController = Em.ObjectController.extend(App.Helpers.DisplayHelper, {
-	controllerName: "DagsController",
+	controllerName: 'DagController',
 
-	pageTitle: "Dag",
+	pageTitle: 'Dag',
 
 	loading: true,
 
@@ -29,11 +29,11 @@ App.DagController = Em.ObjectController.extend(App.Helpers.DisplayHelper, {
 
 	pageSubTitle: function() {
 		return this.get('name');
-	}.property(),
+	}.property('name'),
 
 	childDisplayViews: [
 		Ember.Object.create({title: 'Details', linkTo: 'dag.index'}),
-		Ember.Object.create({title: 'Vertex', linkTo: 'dag.vertex'}),
+		Ember.Object.create({title: 'Counters', linkTo: 'dag.counters'}),
 		Ember.Object.create({title: 'Swimlane', linkTo: 'dag.swimlane'})
 	],
 

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/controllers/dag_counters.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/controllers/dag_counters.js b/tez-ui/app/scripts/controllers/dag_counters.js
new file mode 100644
index 0000000..80aee92
--- /dev/null
+++ b/tez-ui/app/scripts/controllers/dag_counters.js
@@ -0,0 +1,23 @@
+/**
+ * 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.
+ */
+
+ //TODO: watch individual counters.
+/*App.DagCountersController = Em.ObjectController.extend({
+	controllerName: 'DagIndexController',
+
+});*/
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/controllers/dag_index_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/controllers/dag_index_controller.js b/tez-ui/app/scripts/controllers/dag_index_controller.js
index 4ecae7d..c9cc99e 100644
--- a/tez-ui/app/scripts/controllers/dag_index_controller.js
+++ b/tez-ui/app/scripts/controllers/dag_index_controller.js
@@ -16,48 +16,45 @@
  * limitations under the License.
  */
 
+ require('scripts/controllers/show_tasks_view_controller');
+
+ //TODO: watch individual counters.
 App.DagIndexController = Em.ObjectController.extend({
-	controllerName: "DagsController",
-
-	counterTableColumns: function() {
-		var groupColumn = Em.Table.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Group',
-      getCellContent: function(row) {
-      	return row.get('counterGroup');
-      }
-    });
-
-		var nameColumn = Em.Table.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Counter Name',
-      getCellContent: function(row) {
-      	return row.get('name');
-      }
-    });
-
-		var valueColumn = Em.Table.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Counter Name',
-      getCellContent: function(row) {
-      	return row.get('value');
-      }
-    });
-
-    return [groupColumn, nameColumn, valueColumn];
-	}.property(),
-
-	counterContent: function() {
-		var allCounters = [];
-		this.get('content').get('counterGroups').forEach(function(cg){
-			cg.get('counters').forEach(function(counter){
-				allCounters.push({
-					counterGroup: cg.get('displayName'),
-					name: counter.get('displayName'),
-					value: counter.get('value')
-				});
-			});
-		});
-		return allCounters;
-	}.property(),
-});
\ No newline at end of file
+	controllerName: 'DagIndexController',
+
+	taskIconStatus: function() {
+		return App.Helpers.misc.getStatusClassForEntity(this.get('model'));
+	}.property('id', 'status', 'counterGroups'),
+
+	totalTasks: function() {
+		return App.Helpers.misc.getCounterValueForDag(this.get('counterGroups'), 
+			this.get('id'), 'org.apache.tez.common.counters.DAGCounter', 'TOTAL_LAUNCHED_TASKS')
+	}.property('id', 'counterGroups'),
+
+	sucessfulTasks: function() {
+		return App.Helpers.misc.getCounterValueForDag(this.get('counterGroups'), this.get('id'),
+			'org.apache.tez.common.counters.DAGCounter', 'NUM_SUCCEEDED_TASKS')
+	}.property('id', 'counterGroups'),
+
+	failedTasks: function() {
+		return App.Helpers.misc.getCounterValueForDag(this.get('counterGroups'), this.get('id'),
+			'org.apache.tez.common.counters.DAGCounter', 'NUM_FAILED_TASKS')
+	}.property('id', 'counterGroups'),
+
+	killedTasks: function() {
+		return App.Helpers.misc.getCounterValueForDag(this.get('counterGroups'), this.get('id'),
+			'org.apache.tez.common.counters.DAGCounter', 'NUM_KILLED_TASKS')
+	}.property('id', 'counterGroups'),
+
+	hasFailedTasks: function() {
+		return this.get('failedTasks') > 0;
+	}.property('id', 'counterGroups'),
+
+	actions: {
+		showFailedTasks: function() {
+			alert('not implemented');
+		}
+	},
+
+	showTasksViewController: App.ShowTasksViewController.create()
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/controllers/dags_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/controllers/dags_controller.js b/tez-ui/app/scripts/controllers/dags_controller.js
index 70ef7f3..e92f281 100644
--- a/tez-ui/app/scripts/controllers/dags_controller.js
+++ b/tez-ui/app/scripts/controllers/dags_controller.js
@@ -17,11 +17,11 @@
  */
 
 App.DagsController = Em.ArrayController.extend({
-	controllerName: "DagsController",
+	controllerName: 'DagsController',
 
-	pageTitle: "Dags",
+	pageTitle: 'Dags',
 
-	pageSubTitle: "All Dags",
+	pageSubTitle: 'All Dags',
 
   /* filtering and sorting related */
   queryParams: {
@@ -138,49 +138,59 @@ App.DagsController = Em.ArrayController.extend({
 
 	/* table view for dags */
   columns: function() {
+    var store = this.get('store');
+    var columnHelper = function(columnName, valName) {
+      return Em.Table.ColumnDefinition.create({
+        textAlign: 'text-align-left',
+        headerCellName: columnName,
+        getCellContent: function(row) {
+          return row.get(valName);
+        }
+      });
+    }
+
     var idColumn = Em.Table.ColumnDefinition.create({
       textAlign: 'text-align-left',
       headerCellName: 'Dag Id',
       tableCellViewClass: Em.Table.TableCell.extend({
-      	template: Em.Handlebars.compile("{{#link-to 'dag' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
+      	template: Em.Handlebars.compile(
+          "{{#link-to 'dag' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
       }),
       getCellContent: function(row) {
       	return row.get('id');
       }
     });
-    var nameColumn = Em.Table.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Name',
-      getCellContent: function(row) {
-      	return row.get('name');
-      }
-    });
-    var userColumn = Em.Table.ColumnDefinition.create({
-      textAlign: 'text-align-left',
-      headerCellName: 'Submitter',
-      getCellContent: function(row) {
-      	return row.get('user');
-      }
-    });
+    var nameColumn = columnHelper('Name', 'name');
+    var userColumn = columnHelper('Submitter', 'user');
     var statusColumn = Em.Table.ColumnDefinition.create({
       textAlign: 'text-align-left',
       headerCellName: 'Status',
+      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}}</span>')
+      }),
       getCellContent: function(row) {
-      	return row.get('status');
+      	return { 
+          status: row.get('status'),
+          statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
+        };
       }
     });
     var startTimeColumn = Em.Table.ColumnDefinition.create({
       textAlign: 'text-align-left',
-      headerCellName: 'SubmissionTime',
+      headerCellName: 'Submission Time',
       getCellContent: function(row) {
       	return App.Helpers.date.dateFormat(row.get('startTime'));
       }
     });
-    var appIdColumn = Em.Table.ColumnDefinition.create({
+    var appIdColumn = columnHelper('Application ID', 'applicationId');
+    var endTimeColumn = Em.Table.ColumnDefinition.create({
       textAlign: 'text-align-left',
-      headerCellName: 'Application Id',
+      headerCellName: 'End Time',
       getCellContent: function(row) {
-      	return row.get('applicationId');
+        return App.Helpers.date.dateFormat(row.get('endTime'));
       }
     });
     return [idColumn, nameColumn, userColumn, statusColumn, startTimeColumn, appIdColumn];

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/controllers/show_tasks_view_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/controllers/show_tasks_view_controller.js b/tez-ui/app/scripts/controllers/show_tasks_view_controller.js
new file mode 100644
index 0000000..d769200
--- /dev/null
+++ b/tez-ui/app/scripts/controllers/show_tasks_view_controller.js
@@ -0,0 +1,135 @@
+/**
+ * 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 parentTypeToATSType = {
+	dag: 'TEZ_DAG_ID',
+	task: 'TEZ_TASK_ID',
+	taskAttempt: 'TEZ_TASK_ATTEMPT_ID'
+};
+
+var childEntityTypeForParent = {
+	dag: 'task',
+	task: 'taskAttempt'
+}
+
+//TODO: This is generic view controller - name it so.
+App.ShowTasksViewController = Em.ObjectController.extend({
+	controllerName: 'ShowTasksViewController',
+
+	count: 10,
+
+  fromID: '',
+
+  fromTS: '',
+
+  /* There is currently no efficient way in ATS to get pagination data, so we fake one.
+   * 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
+  },
+
+	entities: [],
+	loading: true,
+
+  getChildEntityType: function(parentType) {
+    return childEntityTypeForParent[parentType];
+  },
+
+	sortedContent: function() {
+    var sorted = Em.ArrayController.create({
+      model: this.get('entities'),
+      sortProperties: ['startTime'],
+      sortAscending: false
+    });
+    this.updatePagination(sorted.toArray());
+    return sorted.slice(0, this.count);
+  }.property('entities'),
+
+	loadEntities: function() {
+		var that = this;
+		var parentEntityType = this.get('parentEntityType');
+		var childEntityType = childEntityTypeForParent[parentEntityType];
+		this.get('store').unloadAll(childEntityType);
+		this.get('store').findQuery(childEntityType, this.getFilterProperties()).then(function(entities){
+			that.set('entities', entities);
+			that.set('loading', false);
+		}).catch(function(jqXHR){
+			alert('failed');
+		});
+	}.observes('content.parentEntityID', 'fromID'),
+
+	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');
+      }
+      this.set('navIDs.nextID', nextID);
+    }
+  },
+
+  hasPrev: function() {
+    return this.navIDs.prevIDs.length > 0;
+  }.property('navIDs.prevIDs.[]'),
+
+  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);
+    },
+
+    // goto first page.
+    navigateFirst: function() {
+      var firstPageId = this.navIDs.prevIDs[0];
+      this.set('navIDs.prevIDs', []);
+      this.set('fromID', firstPageId);
+      this.set('loading', true);
+    },
+
+    // go to next page
+    navigateNext: function () {
+      this.navIDs.prevIDs.pushObject(this.navIDs.currentID);
+      this.set('fromID', this.get('navIDs.nextID'));
+      this.set('loading', true);
+    },
+  },
+
+	getFilterProperties: function() {
+		var params = {
+			primaryFilter: parentTypeToATSType[this.get('parentEntityType')] + ':' + this.get('parentEntityID'),
+			limit: this.count + 1
+		};
+
+		if (this.fromID) {
+			params['fromId'] = this.fromID;
+		}
+
+		return params;
+	},
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/controllers/task_attempt_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/controllers/task_attempt_controller.js b/tez-ui/app/scripts/controllers/task_attempt_controller.js
new file mode 100644
index 0000000..8fa290e
--- /dev/null
+++ b/tez-ui/app/scripts/controllers/task_attempt_controller.js
@@ -0,0 +1,39 @@
+/**
+ * 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.TaskAttemptController = Em.ObjectController.extend(App.Helpers.DisplayHelper, {
+	controllerName: 'TaskAttemptController',
+
+	pageTitle: 'Task Attempt',
+
+	loading: true,
+
+	updateLoading: function() {
+    this.set('loading', false);
+  }.observes('content'),
+
+	pageSubTitle: function() {
+		return this.get('id');
+	}.property('id'),
+
+	childDisplayViews: [
+		Ember.Object.create({title: 'Details', linkTo: 'taskAttempt.index'}),
+		Ember.Object.create({title: 'Counters', linkTo: 'taskAttempt.counters'}),
+	],
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/controllers/task_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/controllers/task_controller.js b/tez-ui/app/scripts/controllers/task_controller.js
new file mode 100644
index 0000000..ad3e455
--- /dev/null
+++ b/tez-ui/app/scripts/controllers/task_controller.js
@@ -0,0 +1,39 @@
+/**
+ * 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.TaskController = Em.ObjectController.extend(App.Helpers.DisplayHelper, {
+	controllerName: 'TaskController',
+
+	pageTitle: 'Task',
+
+	loading: true,
+
+	updateLoading: function() {
+    this.set('loading', false);
+  }.observes('content'),
+
+	pageSubTitle: function() {
+		return this.get('id');
+	}.property('id'),
+
+	childDisplayViews: [
+		Ember.Object.create({title: 'Details', linkTo: 'task.index'}),
+		Ember.Object.create({title: 'Counters', linkTo: 'task.counters'}),
+	],
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/controllers/task_index_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/controllers/task_index_controller.js b/tez-ui/app/scripts/controllers/task_index_controller.js
new file mode 100644
index 0000000..2cff0d5
--- /dev/null
+++ b/tez-ui/app/scripts/controllers/task_index_controller.js
@@ -0,0 +1,30 @@
+/**
+ * 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.
+ */
+
+ require('scripts/controllers/show_tasks_view_controller');
+
+ //TODO: watch individual counters.
+App.TaskIndexController = Em.ObjectController.extend({
+	controllerName: 'TaskIndexController',
+
+	taskIconStatus: function() {
+		return App.Helpers.misc.getStatusClassForEntity(this.get('model'));
+	}.property('id', 'status', 'counterGroups'),
+
+	showTasksViewController: App.ShowTasksViewController.create()
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/controllers/tasks_controller.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/controllers/tasks_controller.js b/tez-ui/app/scripts/controllers/tasks_controller.js
new file mode 100644
index 0000000..55b45a6
--- /dev/null
+++ b/tez-ui/app/scripts/controllers/tasks_controller.js
@@ -0,0 +1,131 @@
+/**
+ * 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.TasksController = Em.ArrayController.extend({
+	controllerName: 'TasksController',
+
+	pageTitle: 'Tasks',
+
+	pageSubTitle: 'All Tasks',
+
+	/* There is currently no efficient way in ATS to get pagination data, so we fake one.
+   * store the first task 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
+  },
+
+	sortedContent: function() {
+    var sorted = Em.ArrayController.create({
+      model: this.get('content'),
+      sortProperties: ['startTime'],
+      sortAscending: false
+    });
+    this.updatePagination(sorted.toArray());
+    return sorted.slice(0, this.count);
+  }.property('content.isUpdating', 'content.isLoading'),
+
+  updatePagination: function(currentPageTaskIDs) {
+    if (!!currentPageTaskIDs && currentPageTaskIDs.length > 0) {
+      this.set('navIDs.currentID', currentPageTaskIDs[0].id);
+      var nextID = undefined;
+      if (currentPageTaskIDs.length > this.count) {
+        // save the last id, so that we can use that as firt id on next page.
+        nextID = currentPageTaskIDs[this.count].id;
+      }
+      this.set('navIDs.nextID', nextID);
+    }
+  },
+
+  getFilterParams: function(params) {
+  	//TODO: other parameters.
+  	var filterParams = {};
+
+    if (params.dag_id) {
+      filterParams['primaryFilter'] = 'TEZ_DAG_ID:' + params.dag_id;
+    }
+
+    return filterParams;
+  },
+
+	/* table view for tasks */
+  columns: function() {
+    var store = this.get('store');
+    var columnHelper = function(columnName, valName) {
+      return Em.Table.ColumnDefinition.create({
+        textAlign: 'text-align-left',
+        headerCellName: columnName,
+        getCellContent: function(row) {
+          return row.get(valName);
+        }
+      });
+    }
+
+    var idColumn = Em.Table.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Task Id',
+      tableCellViewClass: Em.Table.TableCell.extend({
+      	template: Em.Handlebars.compile(
+          "{{#link-to 'task' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
+      }),
+      getCellContent: function(row) {
+      	return row.get('id');
+      }
+    });
+
+    var vertexColumn = columnHelper('Vertex ID', 'vertexID');
+
+    var startTimeColumn = Em.Table.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Submission Time',
+      getCellContent: function(row) {
+      	return App.Helpers.date.dateFormat(row.get('startTime'));
+      }
+    });
+
+    var endTimeColumn = Em.Table.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'End Time',
+      getCellContent: function(row) {
+        return App.Helpers.date.dateFormat(row.get('endTime'));
+      }
+    });
+
+    var statusColumn = Em.Table.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Status',
+      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}}</span>')
+      }),
+      getCellContent: function(row) {
+      	return { 
+          status: row.get('status'),
+          statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
+        };
+      }
+    });
+    
+    return [idColumn, vertexColumn, startTimeColumn, endTimeColumn, statusColumn];
+  }.property(),
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/helpers/ajax.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/helpers/ajax.js b/tez-ui/app/scripts/helpers/ajax.js
deleted file mode 100644
index f50401e..0000000
--- a/tez-ui/app/scripts/helpers/ajax.js
+++ /dev/null
@@ -1,179 +0,0 @@
-/**
- * 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.
- */
-
-/**
- * Config for each ajax-request
- *
- * Fields example:
- *  mock - testMode url
- *  real - real url (without API prefix)
- *  type - request type (also may be defined in the format method)
- *  format - function for processing ajax params after default formatRequest. May be called with one or two parameters (data, opt). Return ajax-params object
- *  testInProduction - can this request be executed on production tests (used only in tests)
- *
- * @type {Object}
- */
-var urls = {
-  load_dags: {
-    real: App.AtsBaseUrl + '/ws/v1/timeline/TEZ_DAG_ID/{queryParams}',
-    mock: '',
-    apiPrefix: ''
-  },
-
-  load_dag_details: {
-    real: App.AtsBaseUrl + '/ws/v1/timeline/TEZ_DAG_ID/{dag_id}?fields=events,otherinfo,primaryfilters',
-    mock: '',
-    apiPrefix: ''
-  },
-
-  load_vertex_details: {
-    real: App.AtsBaseUrl + '/ws/v1/timeline/TEZ_VERTEX_ID/?primaryFilter=TEZ_DAG_ID:{dag_id}'
-  },
-
-  load_task_attempts: {
-    real: App.AtsBaseUrl + '/ws/v1/timeline/TEZ_TASK_ATTEMPT_ID/?primaryFilter=TEZ_DAG_ID:{dag_id}'
-  },
-};
-
-/**
- * Replace data-placeholders to its values
- *
- * @param {String} url
- * @param {Object} data
- * @return {String}
- */
-var formatUrl = function (url, data) {
-  if (!url) return null;
-  var keys = url.match(/\{\w+\}/g);
-  keys = (keys === null) ? [] : keys;
-  if (keys) {
-    keys.forEach(function (key) {
-      var raw_key = key.substr(1, key.length - 2);
-      var replace;
-      if (!data || !data[raw_key]) {
-        replace = '';
-      }
-      else {
-        replace = data[raw_key];
-      }
-      url = url.replace(new RegExp(key, 'g'), replace);
-    });
-  }
-  return url;
-};
-
-/**
- * this = object from config
- * @return {Object}
- */
-var formatRequest = function (data) {
-  var opt = {
-    type: this.type || 'GET',
-    dataType: 'json',
-    async: true,
-    headers: this.headers
-  };
-  if (App.get('testMode')) {
-    opt.url = formatUrl(this.mock ? this.mock : '', data);
-    opt.type = 'GET';
-  }
-  else {
-    var prefix = this.apiPrefix != null ? this.apiPrefix : App.get('urlPrefix');
-    opt.url = prefix + formatUrl(this.real, data);
-  }
-
-  if (this.format) {
-    jQuery.extend(opt, this.format(data, opt));
-  }
-  return opt;
-};
-
-/**
- * Wrapper for all ajax requests
- *
- * @type {Object}
- */
-var ajax = Em.Object.extend({
-  /**
-   * Send ajax request
-   *
-   * @param {Object} config
-   * @return {$.ajax} jquery ajax object
-   *
-   * config fields:
-   *  name - url-key in the urls-object *required*
-   *  sender - object that send request (need for proper callback initialization) *required*
-   *  data - object with data for url-format
-   *  beforeSend - method-name for ajax beforeSend response callback
-   *  success - method-name for ajax success response callback
-   *  error - method-name for ajax error response callback
-   */
-  send: function (config) {
-
-    Ember.assert('Ajax sender should be defined!', config.sender);
-    Ember.assert('Invalid config.name provided - ' + config.name, urls[config.name]);
-
-    var opt = {},
-      params = {clusterName: App.get('clusterName')};
-
-    if (config.data) {
-      jQuery.extend(params, config.data);
-    }
-
-    opt = formatRequest.call(urls[config.name], params);
-    opt.context = this;
-
-    // object sender should be provided for processing beforeSend, success, error and complete responses
-    opt.beforeSend = function (xhr) {
-      if (config.beforeSend) {
-        config.sender[config.beforeSend](opt, xhr, params);
-      }
-    };
-
-    opt.success = function (data) {
-      console.log("TRACE: The url is: " + opt.url);
-      if (config.success) {
-        config.sender[config.success](data, opt, params);
-      }
-    };
-
-    opt.error = function (request, ajaxOptions, error) {
-      if (config.error) {
-        config.sender[config.error](request, ajaxOptions, error, opt, params);
-      }
-    };
-
-    opt.complete = function (xhr, status) {
-      if (config.complete) {
-        config.sender[config.complete](xhr, status);
-      }
-    };
-
-    return $.ajax(opt);
-  },
-
-  formatUrlFor: function(endpoint, data) {
-    Ember.assert('Ajax sender should be defined!', !!endpoint);
-    Ember.assert('Invalid config.name provided - ' + endpoint, urls[endpoint]);
-
-    return formatUrl(urls[endpoint].real, data);
-  }
-
-});
-
-App.ajax = ajax.create({});

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/helpers/handlebarHelpers.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/helpers/handlebarHelpers.js b/tez-ui/app/scripts/helpers/handlebarHelpers.js
index 5ed5e8e..72e3b0e 100644
--- a/tez-ui/app/scripts/helpers/handlebarHelpers.js
+++ b/tez-ui/app/scripts/helpers/handlebarHelpers.js
@@ -45,4 +45,17 @@ Em.Handlebars.helper('formatDuration', function(startTime, endTime) {
 	}
 
 	return App.Helpers.date.durationSummary(startTime, endTime);
-});
\ No newline at end of file
+});
+
+function replaceAll(str, str1, str2, ignore) 
+{
+    return str.replace(new RegExp(str1.replace(/([\/\,\!\\\^\$\{\}\[\]\(\)\.\*\+\?\|\<\>\-\&])/g,"\\$&"),(ignore?"gi":"g")),(typeof(str2)=="string")?str2.replace(/\$/g,"$$$$"):str2);
+} 
+
+//TODO: needs better indendation.
+Em.Handlebars.helper('formatDiagnostics', function(diagnostics) {
+  var x = replaceAll(diagnostics, '[', '<div class="indent"><i>&nbsp;</i>');
+  x = replaceAll(x, '],', '</div><i>&nbsp;</i>');
+  x = replaceAll(x, ']', '</div>');
+  return new Handlebars.SafeString(x);
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/helpers/misc.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/helpers/misc.js b/tez-ui/app/scripts/helpers/misc.js
new file mode 100644
index 0000000..1a5c4e8
--- /dev/null
+++ b/tez-ui/app/scripts/helpers/misc.js
@@ -0,0 +1,57 @@
+/**
+ * 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.Helpers.misc = {
+	getStatusClassForEntity: function(dag) {
+		if (dag.get('status') == 'FAILED') {
+			return 'failure';
+		}
+
+		var counterGroups = dag.get('counterGroups');
+		var numFailedTasks = this.getCounterValueForDag(counterGroups,
+				dag.get('id'), 'org.apache.tez.common.counters.DAGCounter',
+				'NUM_FAILED_TASKS'
+			); 
+
+		if (numFailedTasks > 0) {
+			return 'warning';
+		}
+
+		return 'success';
+	},
+
+	getCounterValueForDag: function(counterGroups, dagID, counterGroupName, counterName) {
+		if (!counterGroups) {
+			return 0;
+		}
+
+		var cgName = dagID + '/' + counterGroupName;
+		var cg = 	counterGroups.findBy('id', cgName);
+		if (!cg) {
+			return 0;
+		}
+		var counters = cg.get('counters');
+		if (!counters) {
+			return 0;
+		}
+		
+		var counter = counters.findBy('id', cgName + '/' + counterName);
+		if (!counter) return 0;
+
+		return counter.get('value');
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/mappers/dag_mapper.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/mappers/dag_mapper.js b/tez-ui/app/scripts/mappers/dag_mapper.js
deleted file mode 100644
index aa862f9..0000000
--- a/tez-ui/app/scripts/mappers/dag_mapper.js
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- * 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 jsonToDagMap = {
-	id: 'entity',
-	startTime: 'otherinfo.startTime', //TODO: what is the difference between this and the one in the otherinfo
-	endTime: 'otherinfo.endTime',
-	name: 'primaryfilters.dagName',
-	user: 'primaryfilters.user',
-	applicationId: 'otherinfo.applicationId',
-	status: 'otherinfo.status',
-	diagnostics: 'otherinfo.diagnostics'
-};
-
-App.Mappers.Dag = {
-	mapSingle : function(json) {
-		return Em.JsonMapper.map(json, jsonToDagMap);
-	},
-
-	mapMany : function(json) {
-		if (Array.isArray(json.entities)) {
-			return json.entities.map(this.mapSingle);
-		}
-
-		return [];
-	}
-}
-
-App.Mappers.Vertex = {
-	mapSingle: function(json) {
-		return Em.JsonMapper.map(json, jsonToVertexMap);
-	},
-
-	mapMany: function(json) {
-		if (Array.isArray(json.entities)) {
-			return json.entities.map(this.mapSingle);
-		}
-
-		return [];
-	}
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/models/TimelineRestAdapter.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/models/TimelineRestAdapter.js b/tez-ui/app/scripts/models/TimelineRestAdapter.js
index 58d8f4b..5133286 100644
--- a/tez-ui/app/scripts/models/TimelineRestAdapter.js
+++ b/tez-ui/app/scripts/models/TimelineRestAdapter.js
@@ -1,7 +1,25 @@
+/**
+ * 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 typeToPathMap = {
 	dag: 'TEZ_DAG_ID',
 	vertex: 'TEZ_VERTEX_ID',
-	taskAttempt: 'TEZ_TASK_ATTEMPT_ID',
+  task: 'TEZ_TASK_ID',
+	taskAttempt: 'TEZ_TASK_ATTEMPT_ID'
 };
 
 App.TimelineRESTAdapter = DS.RESTAdapter.extend({
@@ -15,12 +33,6 @@ App.TimelineRESTAdapter = DS.RESTAdapter.extend({
 });
 
 App.TimelineSerializer = DS.RESTSerializer.extend({
-	primaryKey: 'entity',
-
-	attrs: {
-		key: 'entity',
-	},
-
 	extractSingle: function(store, primaryType, rawPayload, recordId) {
 		// rest serializer expects singular form of model as the root key.
 		var payload = {};
@@ -36,6 +48,46 @@ App.TimelineSerializer = DS.RESTSerializer.extend({
 		return this._super(store, primaryType, payload);
 	},
 
+  // normalizes countergroups returns counterGroups and counters.
+  normalizeCounterGroupsHelper: function(parentType, parentID, entity) {
+    // create empty countergroups if not there - to make code below easier.
+    entity.otherinfo.counters = entity.otherinfo.counters || {}
+    entity.otherinfo.counters.counterGroups = entity.otherinfo.counters.counterGroups || [];
+
+    var counterGroups = [];
+    var counters = [];
+
+    var counterGroupsIDs = entity.otherinfo.counters.counterGroups.map(function(counterGroup) {
+      var cg = {
+        id: parentID + '/' + counterGroup.counterGroupName,
+        name: counterGroup.counterGroupName,
+        displayName: counterGroup.counterGroupDisplayName,
+        parentID: { // polymorphic requires type and id.
+          type: parentType,
+          id: parentID
+        }
+      };
+      cg.counters = counterGroup.counters.map(function(counter){
+        var c = {
+          id: cg.id + '/' + counter.counterName,
+          name: counter.counterName,
+          displayName: counter.counterDisplayName,
+          value: counter.counterValue,
+          parentID: cg.id
+        };
+        counters.push(c);
+        return c.id;
+      });
+      counterGroups.push(cg);
+      return cg.id;
+    });
+
+    return {
+      counterGroups: counterGroups,
+      counters: counters,
+      counterGroupsIDs: counterGroupsIDs
+    }
+  }
 });
 
 
@@ -51,48 +103,43 @@ var timelineJsonToDagMap = {
   counterGroups: 'counterGroups'
 };
 
-
 App.DagSerializer = App.TimelineSerializer.extend({
-  normalizePayload: function(rawPayload){
-    // no normalization required for multiple case (findAll)
-    if (!!rawPayload.dags) {
-      return rawPayload;
-    }
-
-    var dag = rawPayload.dag;
-    var counterGroups = dag.otherinfo.counters.counterGroups;
+  _normalizeSingleDagPayload: function(dag) {
+    var normalizedCounterGroupData = this.normalizeCounterGroupsHelper('dag', dag.entity, 
+      dag);
+    dag.counterGroups = normalizedCounterGroupData.counterGroupsIDs;
     delete dag.otherinfo.counters;
 
-    // todo move counter parsing outside
-    // flatten the dag -> countergroup -> counter structure
-    var dagID = dag.entity;
-    var allCounters = [];
-
-    // put counter group id in the list
-    dag['counterGroups'] = counterGroups.map(function(cg) {
-      cg.entity = dagID + '/' + cg.counterGroupName;
-      cg.parentID = {
-        type: 'dag',
-        id: dagID
-      };
+    return {
+      dag: dag,
+      counterGroups: normalizedCounterGroupData.counterGroups,
+      counters: normalizedCounterGroupData.counters
+    };
+  },
 
-      // replace the counters with thier id.
-      cg.counters = cg.counters.map(function(counter){
-        counter.entity = cg.entity + '/' + counter.counterName;
-        counter.cgID = cg.entity;
-        allCounters.push(counter);
-        return counter.entity;
-      });
+  normalizePayload: function(rawPayload){
 
-      return cg.entity;
-    });
+    if (!!rawPayload.dags) {
+      // multiple dags - cames here through _findAll/_findQuery
+      var normalizedPayload = {
+        dags: [],
+        counterGroups: [],
+        counters: []
+      };
+      rawPayload.dags.forEach(function(dag){
+        var n = this._normalizeSingleDagPayload(dag);
+        normalizedPayload.dags.push(n.dag);
+        [].push.apply(normalizedPayload.counterGroups, n.counterGroups);
+        [].push.apply(normalizedPayload.counters, n.counters);
+      }, this);
+      
+      // delete so that we dont hang on to the json data.
+      delete rawPayload.dags;
 
-    var normalizedPayload = {
-      'dag': dag,
-      'counterGroups': counterGroups,
-      'counters': allCounters
-    };
-    return normalizedPayload;
+      return normalizedPayload;
+    } else {
+      return this._normalizeSingleDagPayload(rawPayload.dag);
+    }
   },
 
   normalize: function(type, hash, prop) {
@@ -100,58 +147,116 @@ App.DagSerializer = App.TimelineSerializer.extend({
   },
 });
 
-App.CounterGroupSerializer = DS.JSONSerializer.extend({
-  normalize: function(type, hash, prop) {
-    return {
-      'id': hash.entity,
-      'name': hash.counterGroupName,
-      'displayName': hash.counterGroupDisplayName,
-      'counters': hash.counters,
-      'parent': hash.parentID
+var timelineJsonToTaskAttemptMap = {
+  id: 'entity',
+  startTime: 'otherinfo.startTime',
+  endTime: 'otherinfo.endTime',
+  status: 'otherinfo.status',
+  diagnostics: 'otherinfo.diagnostics',
+  counterGroups: 'counterGroups',
+  vertexID: 'primaryfilters.TEZ_VERTEX_ID.0',
+  dagID: 'primaryfilters.TEZ_DAG_ID.0',
+  containerId: { custom: function (source) {
+    var inProgressLogsURL = Em.get(source, 'otherinfo.inProgressLogsURL');
+    var regex = /.*(container_.*?)\/.*/;
+    var match = regex.exec(inProgressLogsURL);
+    return match[1];
+  }}
+};
+
+
+App.TaskAttemptSerializer = App.TimelineSerializer.extend({
+  _normalizeSingleTaskAttemptPayload: function(taskAttempt) {
+    var normalizedCounterGroupData = this.normalizeCounterGroupsHelper('taskAttempt', 
+      taskAttempt.entity, taskAttempt);
+    taskAttempt.counterGroups = normalizedCounterGroupData.counterGroupsIDs;
+    delete taskAttempt.otherinfo.counters;
+
+    return {taskAttempt: taskAttempt, counterGroups: normalizedCounterGroupData.counterGroups,
+      counters: normalizedCounterGroupData.counters
     };
-  }
-});
+  },
+
+  normalizePayload: function(rawPayload){
+
+    if (!!rawPayload.taskAttempts) {
+      var normalizedPayload = {
+        taskAttempts: [],
+        counterGroups: [],
+        counters: []
+      };
+      rawPayload.taskAttempts.forEach(function(taskAttempt){
+        var n = this._normalizeSingleTaskAttemptPayload(taskAttempt);
+        normalizedPayload.taskAttempts.push(n.taskAttempt); 
+        [].push.apply(normalizedPayload.counterGroups, n.counterGroups);
+        [].push.apply(normalizedPayload.counters, n.counters);
+      }, this);
+      
+      // delete so that we dont hang on to the json data.
+      delete rawPayload.taskAttempts;
+      return normalizedPayload;
+    } else {
+      return this._normalizeSingleTaskAttemptPayload(rawPayload.taskAttempt);
+    }
+  },
 
-App.CounterSerializer = DS.JSONSerializer.extend({
   normalize: function(type, hash, prop) {
-    return {
-      'id': hash.entity,
-      'name': hash.counterName,
-      'displayName': hash.counterDisplayName,
-      'value': hash.counterValue,
-      'parent': hash.cgID
-    };
-  }
+    return Em.JsonMapper.map(hash, timelineJsonToTaskAttemptMap);
+  },
 });
 
-var timelineJsonToTaskAttemptMap = {
+var timelineJsonToTaskMap = {
   id: 'entity',
+  dagID: 'primaryfilters.TEZ_DAG_ID.0',
   startTime: 'otherinfo.startTime',
+  vertexID: 'primaryfilters.TEZ_VERTEX_ID.0',
   endTime: 'otherinfo.endTime',
-  dagId: 'primaryfilters.dagId',
-  containerId: 'otherinfo.containerId',
   status: 'otherinfo.status',
+  diagnostics: 'otherinfo.diagnostics',
+  counterGroups: 'counterGroups',
+  vertexID: 'primaryfilters.TEZ_VERTEX_ID.0',
+  dagID: 'primaryfilters.TEZ_DAG_ID.0',
 };
 
+App.TaskSerializer = App.TimelineSerializer.extend({
+  _normalizeSingleTaskPayload: function(task) {
+    var normalizedCounterGroupData = this.normalizeCounterGroupsHelper('task', task.entity, 
+      task);
+    task.counterGroups = normalizedCounterGroupData.counterGroupsIDs;
 
-App.TaskAttemptSerializer = App.TimelineSerializer.extend({
-  normalizePayload: function(rawPayload){
+    delete task.otherinfo.counters;
+
+    return {
+      task: task,
+      counterGroups: normalizedCounterGroupData.counterGroups,
+      counters: normalizedCounterGroupData.counters
+    };
+  },
+
+  normalizePayload: function(rawPayload) {
+    if (!!rawPayload.tasks) {
+      var normalizedPayload = {
+        tasks: [],
+        counterGroups: [],
+        counters: []
+      };
+      rawPayload.tasks.forEach(function(task){
+        var n = this._normalizeSingleTaskPayload(task);
+        normalizedPayload.tasks.push(n.task);
+        [].push.apply(normalizedPayload.counterGroups, n.counterGroups);
+        [].push.apply(normalizedPayload.counters, n.counters);
+      }, this);
+      
+      // delete so that we dont hang on to the json data.
+      delete rawPayload.tasks;
 
-    // TODO - fake a containerId until it is present
-    var taskAttempts = rawPayload.taskAttempts;
-    for (var i = 0; i < taskAttempts.length; ++i) {
-      var taskAttempt = taskAttempts[i];
-      var inProgressLogsURL = taskAttempt.otherinfo.inProgressLogsURL;
-      var regex = /.*(container_.*?)\/.*/;
-      var match = regex.exec(inProgressLogsURL);
-      taskAttempt.otherinfo.containerId = match[1];
-      taskAttempt.primaryfilters.dagId = taskAttempt.primaryfilters.TEZ_DAG_ID[0];
+      return normalizedPayload;
+    } else {
+      return this._normalizeSingleTaskPayload(rawPayload.task);
     }
-    return rawPayload;
   },
 
   normalize: function(type, hash, prop) {
-    var post = Em.JsonMapper.map(hash, timelineJsonToTaskAttemptMap);
-    return post;
+    return Em.JsonMapper.map(hash, timelineJsonToTaskMap);
   },
 });

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/models/dag.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/models/dag.js b/tez-ui/app/scripts/models/dag.js
index 7acf984..df4a5c4 100644
--- a/tez-ui/app/scripts/models/dag.js
+++ b/tez-ui/app/scripts/models/dag.js
@@ -203,7 +203,21 @@ App.DagVertex = DS.Model.extend({
   }.property('duration')
 });
 
+App.Task = App.AbstractEntity.extend({
+  status: DS.attr('status'),
 
+  dagID: DS.attr('string'),
+  
+  vertexID: DS.attr('string'),
+
+  startTime: DS.attr('number'),
+
+  endTime: DS.attr('number'),
+
+  diagnostics: DS.attr('string'),
+
+  counterGroups: DS.hasMany('counterGroup', { inverse: 'parent' })
+});
 
 App.DagVertexState = {
   NEW: "NEW",

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/models/task_attempt.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/models/task_attempt.js b/tez-ui/app/scripts/models/task_attempt.js
index 86cf230..0bc3aec 100644
--- a/tez-ui/app/scripts/models/task_attempt.js
+++ b/tez-ui/app/scripts/models/task_attempt.js
@@ -34,7 +34,9 @@ App.TaskAttempt = App.AbstractEntity.extend({
   // status of the task attempt
 	status: DS.attr('string'),
 
+  vertexID: DS.attr('string'),
 
-});
+  dagID: DS.attr('string'),
 
-App.TaskAttempt.FIXTURES = [];
+  counterGroups: DS.hasMany('counterGroup', { inverse: 'parent' })
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/router.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/router.js b/tez-ui/app/scripts/router.js
index ffac637..a632b7a 100644
--- a/tez-ui/app/scripts/router.js
+++ b/tez-ui/app/scripts/router.js
@@ -19,10 +19,18 @@
 App.Router.map(function() {
 	this.resource('dags', { path: '/' });
 	this.resource('dag', { path: '/dag/:dag_id'}, function() {
+		this.route('counters'),
 		this.route('vertex'),
 		this.route('swimlane')
 	});
-	//this.resource('error', {path: '/error'});
+	this.resource('tasks', {path: '/tasks/:dag_id'});
+	this.resource('task', {path: '/task/:task_id'}, function(){
+		this.route('counters');
+	});
+	this.resource('taskAttempt', {path: '/task_attempt/:task_attempt_id'}, function() {
+		this.route('counters');
+	});
+	//this.resource('error', {path:$ '/error'});
 });
 
 /*
@@ -69,6 +77,8 @@ App.DagsRoute = Em.Route.extend({
 
 		//TODO remove this
 		this.store.unloadAll('dag');
+		this.store.unloadAll('counterGroup');
+		this.store.unloadAll('counter');
 		return this.store.findQuery('dag', queryParams);
 	},
 
@@ -100,3 +110,30 @@ App.DagSwimlaneRoute = Em.Route.extend({
 		this._super(controller, model);
 	}
 });
+
+App.TasksRoute = Em.Route.extend({
+	model: function(params) {
+		var controllerClass = this.controllerFor('tasks');
+		var queryParams = controllerClass.getFilterParams(params);
+		
+		//TODO: fix this
+		this.store.unloadAll('task');
+		this.store.unloadAll('counterGroup');
+		this.store.unloadAll('counter');
+		return this.store.findQuery('task', queryParams);
+	},
+
+	setupController: function(controller, model) {
+		this._super(controller, model);
+	}
+});
+
+App.TaskRoute = Em.Route.extend({
+	model: function(params) {
+		return this.store.find('task', params.task_id);
+	},
+
+	setupController: function(controller, model) {
+		this._super(controller, model);
+	}
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/views/show_tasks_view.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/views/show_tasks_view.js b/tez-ui/app/scripts/views/show_tasks_view.js
new file mode 100644
index 0000000..1c196e5
--- /dev/null
+++ b/tez-ui/app/scripts/views/show_tasks_view.js
@@ -0,0 +1,81 @@
+App.ShowTasksView = Em.View.extend({
+	templateName: 'views/show_tasks',
+
+	parentEntityType: '',
+	parentEntityID: '',
+
+	init: function() {
+		this.controller.set('store', this.store);
+		this.controller.set('content', this);
+	},
+
+	columns: function() {
+    var columnHelper = function(columnName, valName) {
+      return Em.Table.ColumnDefinition.create({
+        textAlign: 'text-align-left',
+        headerCellName: columnName,
+        getCellContent: function(row) {
+          return row.get(valName);
+        }
+      });
+    }
+
+    var idColumn = Em.Table.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      //TODO: change hard coding.
+      headerCellName: this.get('parentEntityType') == 'task' ? 'Task Attempt ID' : 'Task Id',
+      tableCellViewClass: Em.Table.TableCell.extend({
+      	template: Em.Handlebars.compile(
+          this.get('parentEntityType') == 'task' ?
+          "{{#link-to 'taskAttempt' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}" :
+          "{{#link-to 'task' view.cellContent class='ember-table-content'}}{{view.cellContent}}{{/link-to}}")
+      }),
+      getCellContent: function(row) {
+      	return row.get('id');
+      }
+    });
+
+    var vertexColumn = columnHelper('Vertex', 'vertexID');
+
+    var startTimeColumn = Em.Table.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Submission Time',
+      getCellContent: function(row) {
+      	return App.Helpers.date.dateFormat(row.get('startTime'));
+      }
+    });
+
+    var endTimeColumn = Em.Table.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'End Time',
+      getCellContent: function(row) {
+        return App.Helpers.date.dateFormat(row.get('endTime'));
+      }
+    });
+
+    var statusColumn = Em.Table.ColumnDefinition.create({
+      textAlign: 'text-align-left',
+      headerCellName: 'Status',
+      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}}</span>')
+      }),
+      getCellContent: function(row) {
+      	return { 
+          status: row.get('status'),
+          statusIcon: App.Helpers.misc.getStatusClassForEntity(row)
+        };
+      }
+    });
+    
+    //TODO: for now a hack change this to take columns and type to show.
+    if (this.get('parentEntityType') == 'task') {
+      return [idColumn, startTimeColumn, endTimeColumn, statusColumn];
+    } else {
+      return [idColumn, vertexColumn, startTimeColumn, endTimeColumn, statusColumn];
+    }
+  }.property(),
+
+});
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/scripts/views/tasks_view.js
----------------------------------------------------------------------
diff --git a/tez-ui/app/scripts/views/tasks_view.js b/tez-ui/app/scripts/views/tasks_view.js
new file mode 100644
index 0000000..9f1f953
--- /dev/null
+++ b/tez-ui/app/scripts/views/tasks_view.js
@@ -0,0 +1,23 @@
+App.TasksView = Em.View.extend({
+	templateName: 'views/tasks_view',
+
+	init: function() {
+	},
+	parentEntityType: '', 
+	parentEntityID: '',
+
+	tasks: [],
+
+	getTasks: function() {
+		return;
+		var store = this.get('controller').get('store');
+		store.findQuery('task', {
+			primaryFilter: 'TEZ_DAG_ID:' + this.get('parentEntityID')
+		}).then(function() {
+		});
+	}.observes('parentEntityID'),
+
+	foo: function() {
+		var x = this.get('parentEntityID');
+	}.on('didInsertElement')
+});

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/styles/main.css
----------------------------------------------------------------------
diff --git a/tez-ui/app/styles/main.css b/tez-ui/app/styles/main.css
deleted file mode 100644
index 67082e9..0000000
--- a/tez-ui/app/styles/main.css
+++ /dev/null
@@ -1,70 +0,0 @@
-
-/* misc helpers */
-.align-right {
-	float: right;
-}
-
-.align-left {
-	float: left;
-}
-
-.align-clear {
-	clear: both;
-}
-
-.margin-small {
-	margin: 15px;
-}
-
-.margin-small-horizontal {
-	margin: 15px 0px;
-}
-
-.margin-medium {
-	margin: 30px;
-}
-
-.type-table {
-	display: table;
-}
-
-.fill-full {
-	width: 100%;
-	height: 100%;
-}
-
-/* Navigation */
-.page-nav-link {
-	cursor: pointer;
-	color: black;
-}
-
-.page-nav-link.disabled {
-	pointer-events: none;
-	color:lightgray;
-}
-
-.pill-container {
-
-}
-
-.pill-container {
-	display: table-cell;
-	margin-right: 30px;
-}
-
-.pill-container a {
-	height: 30px !important;
-	padding: 5px 15px !important;
-}
-
-/* dag page */
-.detail-list {
-	table-layout: fixed;
-	overflow: hidden;
-	white-space: nowrap;
-}
-
-.detail-list td:first-child {
-	width:120px;
-}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/styles/main.less
----------------------------------------------------------------------
diff --git a/tez-ui/app/styles/main.less b/tez-ui/app/styles/main.less
new file mode 100644
index 0000000..570b7c5
--- /dev/null
+++ b/tez-ui/app/styles/main.less
@@ -0,0 +1,133 @@
+@import "../bower_components/font-awesome/less/font-awesome";
+
+.fa-icon(@name) {
+    @content: "fa-var-@{name}";
+    &:before {content: @@content}
+}
+
+/* colors */
+@success-color: limegreen;
+@error-color: crimson; 
+@warning-color: orange;
+
+/* misc helpers */
+.align-right {
+	float: right;
+}
+
+.align-left {
+	float: left;
+}
+
+.align-clear {
+	clear: both;
+}
+
+.margin-small {
+	margin: 15px;
+}
+
+.margin-small-horizontal {
+	margin: 15px 0px;
+}
+
+.margin-medium {
+	margin: 30px;
+}
+
+.type-table {
+	display: table;
+}
+
+.fill-full {
+	width: 100%;
+	height: 100%;
+}
+
+/* Navigation */
+.page-nav-link {
+	.fa;
+	//TODO: change this and remove below .fa-3x;
+	font-size: 2.5em;
+	cursor: pointer;
+	color: black;
+
+	&.disabled {
+		pointer-events: none;
+		color:lightgray;
+	}
+
+	&.nav-first {
+		.fa-icon(arrow-circle-o-left);
+	}
+
+	&.nav-prev {
+		.fa-icon(arrow-circle-left)
+	}
+
+	&.nav-next {
+		.fa-icon(arrow-circle-right);
+	}
+}
+
+.pill-container {
+	display: table-cell;
+	margin-right: 30px;
+
+	a {
+		height: 30px !important;
+		padding: 5px 15px !important;
+	}
+}
+
+/* dag page */
+.detail-list {
+	table-layout: fixed;
+	overflow: hidden;
+	white-space: nowrap;
+
+	td {
+		padding: 0px 20px 0px 0px;
+	}
+
+	td:first-child {
+		width:120px;
+	}
+}
+
+/* status related */
+.task-status {
+	.fa;
+	.fa-lg;
+
+	&.success {
+		.fa-icon(check-circle);
+		color: @success-color;
+	}
+
+	&.failure {
+		.fa-icon(exclamation-circle);
+		color: @error-color;
+	}
+
+	&.warning {
+		.fa-icon(exclamation-triangle);
+		color: @warning-color;
+	}
+}
+
+div.indent {
+	margin: 0px 0px 0px 20px;
+	i {
+		.fa;
+		.fa-icon(angle-double-right);
+	}
+}
+
+.countertable-group-header {
+	background-color: lightgray;
+}
+
+.countertable-row {
+	margin: 0px 0px 0px 15px;
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/templates/components/page-nav.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/app/templates/components/page-nav.hbs b/tez-ui/app/templates/components/page-nav.hbs
index 04cad94..85a254c 100644
--- a/tez-ui/app/templates/components/page-nav.hbs
+++ b/tez-ui/app/templates/components/page-nav.hbs
@@ -16,7 +16,6 @@
 * limitations under the License.
 }}
 
-{{!TODO: change to classes. and disable rather than hiding}}
- <i {{bind-attr class=':fa-arrow-circle-o-left :fa :fa-3x :page-nav-link hasPrev:enabled:disabled'}} {{action 'gotoFirst'}}></i>
- <i {{bind-attr class=':fa-arrow-circle-left :fa :fa-3x :page-nav-link hasPrev:enabled:disabled'}} {{action 'gotoPrev'}}></i>
- <i {{bind-attr class=':fa-arrow-circle-right :fa :fa-3x :page-nav-link hasNext:enabled:disabled'}} {{action 'gotoNext'}}></i>
+<i {{bind-attr class=':page-nav-link :nav-first hasPrev:enabled:disabled'}} {{action 'gotoFirst'}}></i>
+<i {{bind-attr class=':page-nav-link :nav-prev hasPrev:enabled:disabled'}} {{action 'gotoPrev'}}></i>
+<i {{bind-attr class=':page-nav-link :nav-next hasNext:enabled:disabled'}} {{action 'gotoNext'}}></i>

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/templates/dag.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/app/templates/dag.hbs b/tez-ui/app/templates/dag.hbs
index e0ccd05..cd71631 100644
--- a/tez-ui/app/templates/dag.hbs
+++ b/tez-ui/app/templates/dag.hbs
@@ -1,12 +1,15 @@
 {{partial "utils/pageHeader"}}
 {{#unless loading}}
 	<div class='margin-small-horizontal'>
-		{{#link-to 'dags'}}<i class='fa-arrow-circle-left fa fa-2x'>&nbsp;Dags</i>{{/link-to}}
+		{{#link-to 'dags'}}
+			<i class='fa-arrow-circle-left fa fa-2x'>
+				<span style='font-size: 85%'>&nbsp;Dags</span>
+			</i>
+		{{/link-to}}
 	</div>
 
 	<div class='type-table fill-full margin-small'>
 		<div class='align-left'>
-			{{!simple-kv-list data=this columns=commonDisplayProperties}}
 
 			<table class='detail-list'>
 				<tbody>
@@ -18,6 +21,10 @@
 						<td>{{t common.applicationId}}</td>
 						<td>{{applicationId}}</td>
 					</tr>
+					<tr>
+						<td>{{t common.user}}</td>
+						<td>{{user}}</td>
+					</tr>
 				</tbody>
 			</table>
 

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/templates/dag/counters.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/app/templates/dag/counters.hbs b/tez-ui/app/templates/dag/counters.hbs
new file mode 100644
index 0000000..ecebc21
--- /dev/null
+++ b/tez-ui/app/templates/dag/counters.hbs
@@ -0,0 +1,21 @@
+{{!
+* 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='table-container margin-medium' style='width: 50%;'>
+  {{counter-table-component data=counterGroups}}
+</div>

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/templates/dag/index.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/app/templates/dag/index.hbs b/tez-ui/app/templates/dag/index.hbs
index 08610e1..2fe37fc 100644
--- a/tez-ui/app/templates/dag/index.hbs
+++ b/tez-ui/app/templates/dag/index.hbs
@@ -1,41 +1,83 @@
-<table style="table-layout:fixed;overflow:hidden;white-space:nowrap;">
-	<thead>
-		<tr>
-			<th style="width:120px;"></th>
-			<th></th>
-		</tr>
-	</thread>
-	<tbody>
-		<tr>
-			<td>{{t common.status}}</td>
-			<td>{{status}}</td>
-		</tr>
-		<tr>
-			<td>{{t common.user}}</td>
-			<td>{{user}}</td>
-		</tr>
-		<tr>
-			<td>{{t common.time.start}}</td>
-			<td>{{formatUnixTimestamp startTime}}</td>
-		</tr>
-		<tr>
-			<td>{{t common.time.end}}</td>
-			<td>{{formatUnixTimestamp endTime}}</td>
-		</tr>
-		<tr>
-			<td>{{t common.time.duration}}</td>
-			<td>{{formatDuration startTime endTime}}</td>
-		</tr>
-	</tbody>
-</table>
+{{!
+* 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.
+}}
 
-<!-- TODO: merge the groups so that we can collapse - treeview? -->
-<div class="margin-small h4">DAG Counters</div>
-<div class='table-container margin-small'>
-  {{table-component
-    hasFooter=false
-    columnsBinding='counterTableColumns'
-    contentBinding='counterContent'
-    forceFillColumns=true
-  }}
-</div>
\ No newline at end of file
+<div class='type-table fill-full margin-small-horizontal'>
+	<div class='align-left'>
+		<table class='detail-list'>
+			<tbody>
+				<tr>
+					<td>{{t common.status}}</td>
+					<td>
+						{{status}}
+						<i {{bind-attr class=':task-status taskIconStatus'}}></i>
+					</td>
+				</tr>
+				<tr>
+					<td>Total Tasks</td>
+					<td>{{totalTasks}}</td>
+				</tr>
+				<tr>
+					<td>Successful Tasks</td>
+					<td>{{sucessfulTasks}}</td>
+				</tr>
+				<tr>
+					<td>Failed Tasks</td>
+					<td>
+						{{failedTasks}}
+					</td>
+					<td>
+						{{#if hasFailedTasks}}
+							{{!TODO: place link to the tasks that failed}}
+							{{#link-to 'tasks' id}}Failed Tasks{{/link-to}}
+						{{/if}}
+					</td>
+				</tr>
+				<tr>
+					<td>Killed Tasks</td>
+					<td>{{killedTasks}}</td>
+				</tr>
+			</tbody>
+		</table>
+	</div>
+	<div class='align-right'>
+		<table class='detail-list'>
+			<tbody>
+				<tr>
+					<td>{{t common.time.start}}</td>
+					<td>{{formatUnixTimestamp startTime}}</td>
+				</tr>
+				<tr>
+					<td>{{t common.time.end}}</td>
+					<td>{{formatUnixTimestamp endTime}}</td>
+				</tr>
+				<tr>
+					<td>{{t common.time.duration}}</td>
+					<td>{{formatDuration startTime endTime}}</td>
+				</tr>
+			</tbody>
+		</table>
+	</div>
+</div>
+
+{{#if diagnostics}}
+	{{#bs-panel heading="Diagnostics" collapsible=false dismiss=false type='danger'}}
+    {{formatDiagnostics diagnostics}}
+	{{/bs-panel}}
+{{/if}}
+
+{{view App.ShowTasksView parentEntityType='dag' store=store parentEntityID=content.id controllerBinding='showTasksViewController'}}

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/templates/dag/vertex.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/app/templates/dag/vertex.hbs b/tez-ui/app/templates/dag/vertex.hbs
index 494a609..decadbc 100644
--- a/tez-ui/app/templates/dag/vertex.hbs
+++ b/tez-ui/app/templates/dag/vertex.hbs
@@ -1 +1 @@
-PlaceHolder for vertex
\ No newline at end of file
+PlaceHolder for tasks
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/templates/dags.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/app/templates/dags.hbs b/tez-ui/app/templates/dags.hbs
index 86dfca4..647ef42 100644
--- a/tez-ui/app/templates/dags.hbs
+++ b/tez-ui/app/templates/dags.hbs
@@ -21,7 +21,7 @@
 {{#unless loading}}
 	<div class='margin-small'>
 		<span class='align-left'>
-			{{page-nav 
+			{{page-nav-component
 				hasPrev=hasPrev
 				hasNext=hasNext
 				navNext='navigateNext'
@@ -43,7 +43,7 @@
 
 	<div class='margin-small'>
 		<span class='align-left'>
-			{{page-nav 
+			{{page-nav-component
 				hasPrev=hasPrev
 				hasNext=hasNext
 				navNext='navigateNext'

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/templates/task.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/app/templates/task.hbs b/tez-ui/app/templates/task.hbs
new file mode 100644
index 0000000..112dc92
--- /dev/null
+++ b/tez-ui/app/templates/task.hbs
@@ -0,0 +1,62 @@
+{{!
+* 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.
+}}
+
+{{partial "utils/pageHeader"}}
+{{#unless loading}}
+	{{!--
+	<div class='margin-small-horizontal'>
+		{{#link-to 'tasks' dagID}}
+			<i class='fa-arrow-circle-left fa fa-2x'>
+				<span style='font-size: 85%'>&nbsp;Tasks</span>
+			</i>
+		{{/link-to}}
+	</div>
+	--}}
+
+	<div class='type-table fill-full '>
+		<div class='align-left'>
+
+			<table class='detail-list'>
+				<tbody>
+					<tr>
+						<td>{{t common.id}}</td>
+						<td>{{id}}</td>
+					</tr>
+					<tr>
+						<td>Vertex ID</td>
+						<td>{{vertexID}}</td>
+					</tr>
+					<tr>
+						<td>DAG ID</td>
+						<td>{{dagID}}</td>
+					</tr>
+				</tbody>
+			</table>
+
+		</div>
+
+		<div class='pill-container align-right'>
+			{{bs-pills contentBinding='childDisplayViews' selectedBinding='childDisplayViewSelected' size='lg'}}
+		</div>
+
+	</div>
+
+	{{outlet}}
+{{else}}
+		{{partial 'utils/loadingSpinner'}}	
+{{/unless}}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/templates/task/counters.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/app/templates/task/counters.hbs b/tez-ui/app/templates/task/counters.hbs
new file mode 100644
index 0000000..4c082e1
--- /dev/null
+++ b/tez-ui/app/templates/task/counters.hbs
@@ -0,0 +1,21 @@
+{{!
+* 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='table-container margin-medium' style='width: 50%;'>
+  {{counter-table-component data=counterGroups}}
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/templates/task/index.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/app/templates/task/index.hbs b/tez-ui/app/templates/task/index.hbs
new file mode 100644
index 0000000..33da8fd
--- /dev/null
+++ b/tez-ui/app/templates/task/index.hbs
@@ -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.
+}}
+
+<div class='type-table fill-full margin-small-horizontal'>
+	<div class='align-left'>
+		<table class='detail-list'>
+			<tbody>
+				<tr>
+					<td>{{t common.status}}</td>
+					<td>
+						{{status}}
+						<i {{bind-attr class=':task-status taskIconStatus'}}></i>
+					</td>
+				</tr>
+			</tbody>
+		</table>
+	</div>
+	<div class='align-right'>
+		<table class='detail-list'>
+			<tbody>
+				<tr>
+					<td>{{t common.time.start}}</td>
+					<td>{{formatUnixTimestamp startTime}}</td>
+				</tr>
+				<tr>
+					<td>{{t common.time.end}}</td>
+					<td>{{formatUnixTimestamp endTime}}</td>
+				</tr>
+				<tr>
+					<td>{{t common.time.duration}}</td>
+					<td>{{formatDuration startTime endTime}}</td>
+				</tr>
+			</tbody>
+		</table>
+	</div>
+</div>
+
+{{#if diagnostics}}
+	{{#bs-panel heading="Diagnostics" collapsible=false dismiss=false type='danger'}}
+    {{formatDiagnostics diagnostics}}
+	{{/bs-panel}}
+{{/if}}
+
+{{view App.ShowTasksView parentEntityType='task' store=store parentEntityID=content.id controllerBinding='showTasksViewController'}}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/templates/taskAttempt/counters.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/app/templates/taskAttempt/counters.hbs b/tez-ui/app/templates/taskAttempt/counters.hbs
new file mode 100644
index 0000000..4c082e1
--- /dev/null
+++ b/tez-ui/app/templates/taskAttempt/counters.hbs
@@ -0,0 +1,21 @@
+{{!
+* 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='table-container margin-medium' style='width: 50%;'>
+  {{counter-table-component data=counterGroups}}
+</div>
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/templates/task_attempt.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/app/templates/task_attempt.hbs b/tez-ui/app/templates/task_attempt.hbs
new file mode 100644
index 0000000..d09f28d
--- /dev/null
+++ b/tez-ui/app/templates/task_attempt.hbs
@@ -0,0 +1,51 @@
+{{!
+* 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.
+}}
+
+{{partial "utils/pageHeader"}}
+{{#unless loading}}
+	<div class='type-table fill-full '>
+			<div class='align-left'>
+				<table class='detail-list'>
+					<tbody>
+						<tr>
+							<td>{{t common.id}}</td>
+							<td>{{id}}</td>
+						</tr>
+						<tr>
+							<td>Vertex ID</td>
+							<td>{{vertexID}}</td>
+						</tr>
+						<tr>
+							<td>DAG ID</td>
+							<td>{{dagID}}</td>
+						</tr>
+					</tbody>
+				</table>
+
+			</div>
+
+			<div class='pill-container align-right'>
+				{{bs-pills contentBinding='childDisplayViews' selectedBinding='childDisplayViewSelected' size='lg'}}
+			</div>
+
+		</div>
+
+		{{outlet}}
+{{else}}
+		{{partial 'utils/loadingSpinner'}}	
+{{/unless}}

http://git-wip-us.apache.org/repos/asf/tez/blob/f6d85173/tez-ui/app/templates/tasks.hbs
----------------------------------------------------------------------
diff --git a/tez-ui/app/templates/tasks.hbs b/tez-ui/app/templates/tasks.hbs
new file mode 100644
index 0000000..4c84164
--- /dev/null
+++ b/tez-ui/app/templates/tasks.hbs
@@ -0,0 +1,58 @@
+{{!
+ * 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.
+}}
+
+{{partial 'utils/pageHeader'}}
+
+{{#unless loading}}
+	<div class='margin-small'>
+		<span class='align-left'>
+			{{page-nav-component
+				hasPrev=hasPrev
+				hasNext=hasNext
+				navNext='navigateNext'
+				navPrev='navigatePrev'
+				navFirst='navigateFirst'
+			}}
+		</span>
+		<div class='align-clear'> </div>
+	</div>
+
+	<div class='table-container'>
+	  {{table-component
+	    hasFooter=false
+	    columnsBinding='columns'
+	    contentBinding='sortedContent'
+	    forceFillColumns=true
+	  }}
+	</div>
+
+	<div class='margin-small'>
+		<span class='align-left'>
+			{{page-nav-component
+				hasPrev=hasPrev
+				hasNext=hasNext
+				navNext='navigateNext'
+				navPrev='navigatePrev'
+				navFirst='navigateFirst'
+			}}
+		</span>
+		<div class='align-clear'></div>
+	</div>
+{{else}}
+	{{partial 'utils/loadingSpinner'}}	
+{{/unless}}
\ No newline at end of file