You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficcontrol.apache.org by da...@apache.org on 2017/05/08 19:23:06 UTC

[2/5] incubator-trafficcontrol git commit: hooks up dashboard page

hooks up dashboard page


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

Branch: refs/heads/master
Commit: a81c975ab034791f38e6638f845266cbe3c700fa
Parents: 848c0c8
Author: Jeremy Mitchell <mi...@gmail.com>
Authored: Fri May 5 15:08:58 2017 -0600
Committer: Dan Kirkwood <da...@gmail.com>
Committed: Mon May 8 13:21:54 2017 -0600

----------------------------------------------------------------------
 traffic_ops/experimental/ui/app/src/app.js      |   8 +
 .../ui/app/src/common/api/AuthService.js        |   2 +-
 .../ui/app/src/common/api/CDNService.js         |  36 +-
 .../ui/app/src/common/api/CacheStatsService.js  |  63 +++
 .../experimental/ui/app/src/common/api/index.js |   3 +-
 .../ui/app/src/common/filters/PercentFilter.js  |  16 +
 .../ui/app/src/common/filters/index.js          |   1 +
 .../cacheGroups/WidgetCacheGroupsController.js  |  41 ++
 .../common/modules/widget/cacheGroups/index.js  |  21 +
 .../cacheGroups/widget.cacheGroups.tpl.html     |  44 ++
 .../widget/capacity/WidgetCapacityController.js | 101 ++++
 .../widget/capacity/_widget.capacity.scss       |  14 +
 .../src/common/modules/widget/capacity/index.js |  21 +
 .../widget/capacity/widget.capacity.tpl.html    |  40 ++
 .../widget/cdnChart/WidgetCDNChartController.js | 189 +++++++
 .../widget/cdnChart/_widget.cdnChart.scss       |  30 ++
 .../src/common/modules/widget/cdnChart/index.js |  21 +
 .../widget/cdnChart/widget.cdnChart.tpl.html    |  37 ++
 .../changeLogs/WidgetChangeLogsController.js    |  37 ++
 .../common/modules/widget/changeLogs/index.js   |  21 +
 .../changeLogs/widget.changeLogs.tpl.html       |  26 +
 .../widget/routing/WidgetRoutingController.js   |  33 ++
 .../src/common/modules/widget/routing/index.js  |  21 +
 .../widget/routing/widget.routing.tpl.html      |  88 ++++
 .../app/src/common/service/utils/DateUtils.js   | 131 +++++
 .../app/src/common/service/utils/NumberUtils.js |  80 +++
 .../ui/app/src/common/service/utils/index.js    |   2 +
 .../monitor/dashboard/DashboardController.js    |  33 +-
 .../monitor/dashboard/dashboard.tpl.html        | 509 ++-----------------
 .../modules/private/monitor/dashboard/index.js  |  11 +-
 .../private/monitor/dashboard/view/index.js     |  53 ++
 .../ui/app/src/scripts/shared-libs.js           |   8 +
 .../experimental/ui/app/src/styles/main.scss    |   2 +
 .../experimental/ui/app/src/styles/theme.scss   |   2 +-
 .../experimental/ui/grunt/browserify2.js        |  60 +++
 35 files changed, 1296 insertions(+), 509 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/app.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/app.js b/traffic_ops/experimental/ui/app/src/app.js
index ac4a99d..082872c 100644
--- a/traffic_ops/experimental/ui/app/src/app.js
+++ b/traffic_ops/experimental/ui/app/src/app.js
@@ -155,6 +155,7 @@ var trafficOps = angular.module('trafficOps', [
 
         // dashboards
         require('./modules/private/monitor/dashboard').name,
+        require('./modules/private/monitor/dashboard/view').name,
         require('./modules/private/monitor/map').name,
 
         // common modules
@@ -264,6 +265,13 @@ var trafficOps = angular.module('trafficOps', [
         require('./common/modules/table/users').name,
         require('./common/modules/table/userDeliveryServices').name,
 
+        // widgets
+        require('./common/modules/widget/cacheGroups').name,
+        require('./common/modules/widget/capacity').name,
+        require('./common/modules/widget/cdnChart').name,
+        require('./common/modules/widget/changeLogs').name,
+        require('./common/modules/widget/routing').name,
+
         // models
         require('./common/models').name,
         require('./common/api').name,

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/api/AuthService.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/api/AuthService.js b/traffic_ops/experimental/ui/app/src/common/api/AuthService.js
index d1b7c9c..e302de4 100644
--- a/traffic_ops/experimental/ui/app/src/common/api/AuthService.js
+++ b/traffic_ops/experimental/ui/app/src/common/api/AuthService.js
@@ -30,7 +30,7 @@ var AuthService = function($rootScope, $http, $state, $location, $q, $state, htt
                         $location.search('redirect', null); // remove the redirect query param
                         $location.url(redirect);
                     } else {
-                        $location.url('/monitor/map');
+                        $location.url('/monitor/dashboard');
                     }
                 },
                 function(fault) {

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/api/CDNService.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/api/CDNService.js b/traffic_ops/experimental/ui/app/src/common/api/CDNService.js
index 2616c80..b24f412 100644
--- a/traffic_ops/experimental/ui/app/src/common/api/CDNService.js
+++ b/traffic_ops/experimental/ui/app/src/common/api/CDNService.js
@@ -17,7 +17,7 @@
  * under the License.
  */
 
-var CDNService = function(Restangular, locationUtils, messageModel) {
+var CDNService = function($http, $q, Restangular, locationUtils, messageModel, ENV) {
 
     this.getCDNs = function(queryParams) {
         return Restangular.all('cdns').getList(queryParams);
@@ -88,7 +88,39 @@ var CDNService = function(Restangular, locationUtils, messageModel) {
             );
     };
 
+    this.getCapacity = function() {
+        var request = $q.defer();
+
+        $http.get(ENV.api['root'] + "cdns/capacity")
+            .then(
+                function(result) {
+                    request.resolve(result.data.response);
+                },
+                function(fault) {
+                    request.reject();
+                }
+            );
+
+        return request.promise;
+    };
+
+    this.getCurrentStats = function() {
+        var request = $q.defer();
+
+        $http.get(ENV.api['root'] + "current_stats")
+            .then(
+                function(result) {
+                    request.resolve(result.data.response);
+                },
+                function(fault) {
+                    request.reject();
+                }
+            );
+
+        return request.promise;
+    };
+
 };
 
-CDNService.$inject = ['Restangular', 'locationUtils', 'messageModel'];
+CDNService.$inject = ['$http', '$q', 'Restangular', 'locationUtils', 'messageModel', 'ENV'];
 module.exports = CDNService;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/api/CacheStatsService.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/api/CacheStatsService.js b/traffic_ops/experimental/ui/app/src/common/api/CacheStatsService.js
new file mode 100644
index 0000000..3bcd97a
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/api/CacheStatsService.js
@@ -0,0 +1,63 @@
+/*
+ * 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 CacheStatsService = function($http, $q, httpService, ENV) {
+
+	this.getBandwidth = function(cdnName, start, end) {
+		var request = $q.defer();
+
+		var url = ENV.api['root'] + "cache_stats",
+			params = { cdnName: cdnName, metricType: 'bandwidth', startDate: start.seconds(00).format(), endDate: end.seconds(00).format()};
+
+		$http.get(url, { params: params })
+			.then(
+				function(result) {
+					request.resolve(result.data.response);
+				},
+				function(fault) {
+					request.reject();
+				}
+			);
+
+		return request.promise;
+	};
+
+	this.getConnections = function(cdnName, start, end) {
+		var request = $q.defer();
+
+		var url = ENV.api['root'] + "cache_stats",
+			params = { cdnName: cdnName, metricType: 'connections', startDate: start.seconds(00).format(), endDate: end.seconds(00).format()};
+
+		$http.get(url, { params: params })
+			.then(
+				function(result) {
+					request.resolve(result.data.response);
+				},
+				function(fault) {
+					request.reject();
+				}
+			);
+
+		return request.promise;
+	};
+
+};
+
+CacheStatsService.$inject = ['$http', '$q', 'httpService', 'ENV'];
+module.exports = CacheStatsService;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/api/index.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/api/index.js b/traffic_ops/experimental/ui/app/src/common/api/index.js
index 042cd93..1e6c597 100644
--- a/traffic_ops/experimental/ui/app/src/common/api/index.js
+++ b/traffic_ops/experimental/ui/app/src/common/api/index.js
@@ -22,7 +22,8 @@ module.exports = angular.module('trafficOps.api', [])
     .service('asnService', require('./ASNService'))
     .service('cacheGroupService', require('./CacheGroupService'))
     .service('cacheGroupParameterService', require('./CacheGroupParameterService'))
-    .service('cdnService', require('./CDNService'))
+	.service('cacheStatsService', require('./CacheStatsService'))
+	.service('cdnService', require('./CDNService'))
     .service('changeLogService', require('./ChangeLogService'))
     .service('deliveryServiceService', require('./DeliveryServiceService'))
 	.service('deliveryServiceRegexService', require('./DeliveryServiceRegexService'))

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/filters/PercentFilter.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/filters/PercentFilter.js b/traffic_ops/experimental/ui/app/src/common/filters/PercentFilter.js
new file mode 100644
index 0000000..21951d9
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/filters/PercentFilter.js
@@ -0,0 +1,16 @@
+var PercentFilter = function() {
+	return function(input) {
+		input = parseFloat(input);
+		input *= 100;
+		if(input % 1 === 0) {
+			input = input.toFixed(0);
+		}
+		else {
+			input = input.toFixed(2);
+		}
+		return input + '%';
+	};
+};
+
+PercentFilter.$inject = [];
+module.exports = PercentFilter;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/filters/index.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/filters/index.js b/traffic_ops/experimental/ui/app/src/common/filters/index.js
index 93c6a6c..f912a6c 100644
--- a/traffic_ops/experimental/ui/app/src/common/filters/index.js
+++ b/traffic_ops/experimental/ui/app/src/common/filters/index.js
@@ -19,4 +19,5 @@
 
 module.exports = angular.module('trafficOps.filters', [])
     .filter('offsetFilter', require('./OffsetFilter'))
+	.filter('percentFilter', require('./PercentFilter'))
 ;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/cacheGroups/WidgetCacheGroupsController.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/cacheGroups/WidgetCacheGroupsController.js b/traffic_ops/experimental/ui/app/src/common/modules/widget/cacheGroups/WidgetCacheGroupsController.js
new file mode 100644
index 0000000..c5c0f54
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/cacheGroups/WidgetCacheGroupsController.js
@@ -0,0 +1,41 @@
+/*
+ * 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 WidgetCacheGroupsController = function(cacheGroupHealth, $scope) {
+
+	// pagination
+	$scope.currentLocationPage = 1;
+	$scope.cacheGroupsPerPage = 10;
+
+	$scope.onlinePercent = function(location) {
+		return (location.online / (location.online + location.offline)) * 100;
+	};
+
+	var init = function() {
+		if (cacheGroupHealth) {
+			// only set this if it's passed in
+			$scope.cacheGroupHealth = cacheGroupHealth;
+		}
+	};
+	init();
+
+};
+
+WidgetCacheGroupsController.$inject = ['cacheGroupHealth', '$scope'];
+module.exports = WidgetCacheGroupsController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/cacheGroups/index.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/cacheGroups/index.js b/traffic_ops/experimental/ui/app/src/common/modules/widget/cacheGroups/index.js
new file mode 100644
index 0000000..21abb68
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/cacheGroups/index.js
@@ -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.
+ */
+
+module.exports = angular.module('trafficOps.widget.cacheGroups', [])
+	.controller('WidgetCacheGroupsController', require('./WidgetCacheGroupsController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/cacheGroups/widget.cacheGroups.tpl.html
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/cacheGroups/widget.cacheGroups.tpl.html b/traffic_ops/experimental/ui/app/src/common/modules/widget/cacheGroups/widget.cacheGroups.tpl.html
new file mode 100644
index 0000000..eeb0fe5
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/cacheGroups/widget.cacheGroups.tpl.html
@@ -0,0 +1,44 @@
+<div class="x_title">
+    <h3>Cache Groups <small>{{cacheGroupHealth.totalOnline/(cacheGroupHealth.totalOnline + cacheGroupHealth.totalOffline) | percentFilter}} online</small></h3>
+</div>
+<div class="x_content">
+    <div id="cache-groups-outer-container">
+        <div id="cacheGroupsContainer">
+            <div class="alert alert-info" ng-show="(cacheGroupHealth.cachegroups | filter:search:strict).length == 0">
+                No matching cache groups
+            </div>
+            <div class="list-group">
+                <a class="cache-group-health list-group-item" ng-repeat="cg in cacheGroupHealth.cachegroups | orderBy:[onlinePercent, 'name'] | filter:search:strict | offsetFilter:(currentCacheGroupsPage-1)*cacheGroupsPerPage | limitTo:cacheGroupsPerPage">
+                    <div class="row">
+                        <table class="cache-groups-table table">
+                            <tbody>
+                            <tr>
+                                <td class="col-lg-4 col-md-4 col-sm-4">{{cg.name}}</td>
+                                <td class="col-lg-4 col-md-4 col-sm-4">{{cg.online/(cg.online + cg.offline) | percentFilter}} Online [ {{cg.online}} Online | {{cg.offline}} Offline ]</td>
+                                <td class="col-lg-4 col-md-4 col-sm-4">
+                                    <div class="progress">
+                                        <div class="progress-bar bg-green" role="progressbar" ng-style="{'width': onlinePercent(cg) + '%'}">
+                                            <span class="sr-only">{{cg.online}} Servers Online | {{cg.offline}} Servers Offline</span>
+                                        </div>
+                                    </div>
+                                </td>
+                            </tr>
+                            </tbody>
+                        </table>
+                    </div>
+                </a>
+            </div>
+        </div>
+        <div>
+            <div ng-show="(cacheGroupHealth.cachegroups | filter:search:strict).length > 0">
+                <uib-pagination class="cache-groups-pagination pagination-md" boundary-links="true" max-size="2" total-items="(cacheGroupHealth.cachegroups | filter:search:strict).length" items-per-page="cacheGroupsPerPage" ng-model="currentCacheGroupsPage" previous-text="&lsaquo;" next-text="&rsaquo;" first-text="&laquo;" last-text="&raquo;"></uib-pagination>
+            </div>
+            <div class="input-group cache-group-search-form">
+                <input type="text" class="filter-input form-control" placeholder="Filter cache groups..." ng-model="search.name">
+                <span class="filter-input-group-btn input-group-btn">
+                    <button class="btn btn-default" type="button"><i class="fa fa-search"></i></button>
+                </span>
+            </div>
+        </div>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/WidgetCapacityController.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/WidgetCapacityController.js b/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/WidgetCapacityController.js
new file mode 100644
index 0000000..6a0227a
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/WidgetCapacityController.js
@@ -0,0 +1,101 @@
+/*
+ * 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 WidgetCapacityController = function($scope, cdnService) {
+
+	var getCapacity = function() {
+		cdnService.getCapacity()
+			.then(function(response) {
+				$scope.availablePercent = Math.round(response.availablePercent * 100) / 100;
+				$scope.utilizedPercent = Math.round(response.utilizedPercent * 100) / 100;
+				$scope.maintenancePercent = Math.round(response.maintenancePercent * 100) / 100;
+				$scope.unavailablePercent = Math.round(response.unavailablePercent * 100) / 100;
+
+				var data = [];
+
+				data.push({
+					label: "Available",
+					color: '#1ABB9C',
+					data: $scope.availablePercent
+				});
+				data.push({
+					label: "Utilized",
+					color: '#3498DB',
+					data: $scope.utilizedPercent
+				});
+				data.push({
+					label: "Maintenance",
+					color: '#73879C',
+					data: $scope.maintenancePercent
+				});
+				data.push({
+					label: "Down",
+					color: '#E74C3C',
+					data: $scope.unavailablePercent
+				});
+
+				buildGraph(data);
+			});
+
+	};
+
+	var buildGraph = function(graphData) {
+
+		var options = {
+			series: {
+				pie: {
+					show: true,
+					innerRadius: 0.5,
+					radius: 1,
+					label: {
+						show: false
+					}
+				}
+			},
+			grid: {
+				hoverable: true
+			},
+			tooltip: true,
+			tooltipOpts: {
+				cssClass: "capacityChartTooltip",
+				content: "%s: %p.2%",
+				defaultTheme: false
+			},
+			legend: {
+				show: false
+			}
+		};
+
+		$.plot($("#capacityChart"), graphData, options);
+	};
+
+	$scope.availablePercent = 0;
+	$scope.utilizedPercent = 0;
+	$scope.maintenancePercent = 0;
+	$scope.unavailablePercent = 0;
+
+	var init = function() {
+		getCapacity();
+	};
+	init();
+
+};
+
+WidgetCapacityController.$inject = ['$scope', 'cdnService'];
+module.exports = WidgetCapacityController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/_widget.capacity.scss
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/_widget.capacity.scss b/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/_widget.capacity.scss
new file mode 100644
index 0000000..33723c8
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/_widget.capacity.scss
@@ -0,0 +1,14 @@
+#capacityChart {
+  left: 0px;
+  top: 0px;
+  width: 200px;
+  height: 200px;
+}
+.capacityChartTooltip {
+  padding: 3px 5px;
+  background-color: #000;
+  z-index: 100;
+  color: #fff;
+  opacity: .80;
+  filter: alpha(opacity=85);
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/index.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/index.js b/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/index.js
new file mode 100644
index 0000000..cecf072
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/index.js
@@ -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.
+ */
+
+module.exports = angular.module('trafficOps.widget.capacity', [])
+	.controller('WidgetCapacityController', require('./WidgetCapacityController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/widget.capacity.tpl.html
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/widget.capacity.tpl.html b/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/widget.capacity.tpl.html
new file mode 100644
index 0000000..96f1fb5
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/capacity/widget.capacity.tpl.html
@@ -0,0 +1,40 @@
+<div class="x_title">
+    <h3>Overall Capacity</h3>
+</div>
+<div class="x_content">
+    <table class="" style="width:100%">
+        <tr>
+            <td>
+                <div id="capacityChart"></div>
+            </td>
+            <td>
+                <table class="tile_info">
+                    <tr>
+                        <td>
+                            <p><i class="fa fa-square green"></i>Available </p>
+                        </td>
+                        <td>{{availablePercent}}%</td>
+                    </tr>
+                    <tr>
+                        <td>
+                            <p><i class="fa fa-square blue"></i>Utilized </p>
+                        </td>
+                        <td>{{utilizedPercent}}%</td>
+                    </tr>
+                    <tr>
+                        <td>
+                            <p><i class="fa fa-square gray"></i>Maint </p>
+                        </td>
+                        <td>{{maintenancePercent}}%</td>
+                    </tr>
+                    <tr>
+                        <td>
+                            <p><i class="fa fa-square red"></i>Down </p>
+                        </td>
+                        <td>{{unavailablePercent}}%</td>
+                    </tr>
+                </table>
+            </td>
+        </tr>
+    </table>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/WidgetCDNChartController.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/WidgetCDNChartController.js b/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/WidgetCDNChartController.js
new file mode 100644
index 0000000..22e2a64
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/WidgetCDNChartController.js
@@ -0,0 +1,189 @@
+/*
+ * 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 WidgetCDNChartController = function(cdn, $scope, $timeout, $filter, $q, cdnService, cacheStatsService, dateUtils, locationUtils, numberUtils) {
+
+	var getCDN = function(id) {
+		cdnService.getCDN(id)
+			.then(function(result) {
+				$scope.cdn = result;
+				getCurrentStats($scope.cdn.name);
+				getChartData($scope.cdn.name, moment().subtract(1, 'days'), moment().subtract(10, 'seconds'));
+			});
+	};
+
+	var getCurrentStats = function(cdnName) {
+		cdnService.getCurrentStats()
+			.then(function(result) {
+				$scope.currentStats = _.find(result.currentStats, function(item) {
+					return item.cdn == cdnName;
+				});
+			});
+	};
+
+	var getChartData = function(cdnName, start, end) {
+		var promises = [];
+
+		// get cdn bandwidth
+		promises.push(cacheStatsService.getBandwidth(cdnName, start, end));
+
+		// get cdn connections
+		promises.push(cacheStatsService.getConnections(cdnName, start, end));
+
+		$q.all(promises)
+			.then(
+				function(responses) {
+					// set chart data
+					var bandwidthChartData = buildBandwidthChartData(responses[0], start),
+						connectionsChartData = buildConnectionsChartData(responses[1], start);
+
+					$timeout(function () {
+						buildChart(bandwidthChartData, connectionsChartData);
+					}, 100);
+				},
+				function(fault) {
+					buildChart([], []); // build an empty chart
+				});
+
+	};
+
+	var buildBandwidthChartData = function(result, start) {
+		var normalizedChartData = [],
+			series = result.series;
+
+		if (angular.isDefined(series)) {
+			_.each(series.values, function(seriesItem) {
+				if (moment(seriesItem[0]).isSame(start) || moment(seriesItem[0]).isAfter(start)) {
+					normalizedChartData.push([ moment(seriesItem[0]).valueOf(), numberUtils.convertTo(seriesItem[1], $scope.unitSize) ]); // converts data to appropriate unit
+				}
+			});
+		}
+
+		return normalizedChartData;
+	};
+
+	var buildConnectionsChartData = function(result, start) {
+		var normalizedChartData = [],
+			series = result.series;
+
+		if (angular.isDefined(series)) {
+			_.each(series.values, function(seriesItem) {
+				if (moment(seriesItem[0]).isSame(start) || moment(seriesItem[0]).isAfter(start)) {
+					if (_.isNumber(seriesItem[1])) {
+						normalizedChartData.push([ moment(seriesItem[0]).valueOf(), seriesItem[1] ]);
+					}
+				}
+			});
+		}
+
+		return normalizedChartData;
+	};
+
+	var buildChart = function(bandwidthChartData, connectionsChartData) {
+
+		var options = {
+			xaxis: {
+				mode: "time",
+				timezone: "utc",
+				twelveHourClock: false
+			},
+			yaxes: [
+				{
+					position: "left",
+					axisLabel: "Bandwidth (Gbps)",
+					axisLabelUseCanvas: true,
+					axisLabelFontSizePixels: 12,
+					axisLabelFontFamily: 'Verdana, Arial',
+					axisLabelPadding: 3
+				},
+				{
+					position: "right",
+					axisLabel: "Connections",
+					axisLabelUseCanvas: true,
+					axisLabelFontSizePixels: 12,
+					axisLabelFontFamily: 'Verdana, Arial',
+					axisLabelPadding: 3
+				}
+			],
+			grid: {
+				hoverable: true,
+				axisMargin: 20
+			},
+			tooltip: {
+				show: true,
+				content: function(label, xval, yval, flotItem){
+					var tooltipString = dateUtils.dateFormat(xval, "UTC: ddd mmm d yyyy H:MM:ss tt (Z)") + '<br>';
+					tooltipString += '<span>' + label + ': ' + $filter('number')(yval, 2) + '</span><br>'
+					return tooltipString;
+				}
+			}
+		};
+
+		var series = [
+			{ label: "Bandwidth", yaxis: 1, color: colors[Math.floor(Math.random() * 17) + 1], data: bandwidthChartData },
+			{ label: "Connections", yaxis: 2, color: colors[Math.floor(Math.random() * 17) + 1], data: connectionsChartData }
+		];
+
+		$.plot($("#bps-chart-" + $scope.cdn.id), series, options);
+
+	};
+
+	var colors = [
+		'#1ABB9C',
+		'#3498DB',
+		'#73879C',
+		'#E74C3C',
+		'#946E83',
+		'#615055',
+		'#000000',
+		'#9FD356',
+		'#3A6B3D',
+		'#405672',
+		'#FF5000',
+		'#E39878',
+		'#6D6B6D',
+		'#54000B',
+		'#077187',
+		'#0D295E',
+		'#5B0554'
+	];
+
+	$scope.cdn;
+
+	$scope.unitSize = 'Gb';
+
+	$scope.randomId = '_' + Math.random().toString(36).substr(2, 9);
+
+	$scope.navigateToPath = locationUtils.navigateToPath;
+
+	angular.element(document).ready(function () {
+		var cdnId;
+		if (cdn) {
+			cdnId = cdn.id;
+		} else {
+			// cdn wasn't passed in. we need to figure it out on our own
+			cdnId = $('#' + $scope.randomId).closest('.chartContainer').data('cdnid');
+		}
+		getCDN(cdnId);
+	});
+
+};
+
+WidgetCDNChartController.$inject = ['cdn', '$scope', '$timeout', '$filter', '$q', 'cdnService', 'cacheStatsService', 'dateUtils', 'locationUtils', 'numberUtils'];
+module.exports = WidgetCDNChartController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/_widget.cdnChart.scss
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/_widget.cdnChart.scss b/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/_widget.cdnChart.scss
new file mode 100644
index 0000000..e5ff8b2
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/_widget.cdnChart.scss
@@ -0,0 +1,30 @@
+/*
+
+
+ Licensed 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.
+
+*/
+
+.bps-chart-container {
+  position: relative;
+  text-align: center;
+  background-color: transparent;
+  padding: 30px 60px;
+
+  .bps-chart {
+    height: 200px;
+    min-width: 310px;
+  }
+
+}
+

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/index.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/index.js b/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/index.js
new file mode 100644
index 0000000..7100951
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/index.js
@@ -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.
+ */
+
+module.exports = angular.module('trafficOps.widget.cdnChart', [])
+	.controller('WidgetCDNChartController', require('./WidgetCDNChartController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/widget.cdnChart.tpl.html
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/widget.cdnChart.tpl.html b/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/widget.cdnChart.tpl.html
new file mode 100644
index 0000000..c1bef77
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/cdnChart/widget.cdnChart.tpl.html
@@ -0,0 +1,37 @@
+<div id="{{::randomId}}" class="dashboard_graph">
+    <div class="row x_title">
+        <div class="col-md-6">
+            <h3><a ng-click="navigateToPath('/admin/cdns/' + cdn.id)">{{::cdn.name}}</a></h3>
+        </div>
+    </div>
+    <div class="col-md-9 col-sm-9 col-xs-12">
+        <div class="bps-chart-container">
+            <div id="bps-chart-{{::cdn.id}}" class="bps-chart"></div>
+        </div>
+    </div>
+    <div class="col-md-3 col-sm-3 col-xs-12 bg-white">
+        <div class="x_title">
+            <h3>Statistics</h3>
+            <div class="clearfix"></div>
+        </div>
+        <div class="col-md-12 col-sm-12 col-xs-6">
+            <table class="countries_list">
+                <tbody>
+                    <tr>
+                        <td>Utilization %</td>
+                        <td class="fs15 fw700 text-right">{{currentStats.bandwidth/currentStats.capacity | percentFilter}} of {{currentStats.capacity | number:0}} Gbps</td>
+                    </tr>
+                    <tr>
+                        <td>Current Bandwidth</td>
+                        <td class="fs15 fw700 text-right">{{currentStats.bandwidth | number:2}} Gbps</td>
+                    </tr>
+                    <tr>
+                        <td>Current Connections</td>
+                        <td class="fs15 fw700 text-right">{{currentStats.connections | number:0}}</td>
+                    </tr>
+                </tbody>
+            </table>
+        </div>
+    </div>
+    <div class="clearfix"></div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/changeLogs/WidgetChangeLogsController.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/changeLogs/WidgetChangeLogsController.js b/traffic_ops/experimental/ui/app/src/common/modules/widget/changeLogs/WidgetChangeLogsController.js
new file mode 100644
index 0000000..335fac5
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/changeLogs/WidgetChangeLogsController.js
@@ -0,0 +1,37 @@
+/*
+ * 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 WidgetChangeLogsController = function(changeLogs, $scope) {
+
+	$scope.getRelativeTime = function(date) {
+		return moment(date).fromNow();
+	};
+
+	var init = function() {
+		if (changeLogs) {
+			// only set this if it's passed in
+			$scope.changeLogs = changeLogs;
+		}
+	};
+	init();
+
+};
+
+WidgetChangeLogsController.$inject = ['changeLogs', '$scope'];
+module.exports = WidgetChangeLogsController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/changeLogs/index.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/changeLogs/index.js b/traffic_ops/experimental/ui/app/src/common/modules/widget/changeLogs/index.js
new file mode 100644
index 0000000..e71fb2c
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/changeLogs/index.js
@@ -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.
+ */
+
+module.exports = angular.module('trafficOps.widget.changeLogs', [])
+	.controller('WidgetChangeLogsController', require('./WidgetChangeLogsController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/changeLogs/widget.changeLogs.tpl.html
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/changeLogs/widget.changeLogs.tpl.html b/traffic_ops/experimental/ui/app/src/common/modules/widget/changeLogs/widget.changeLogs.tpl.html
new file mode 100644
index 0000000..a3ceba2
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/changeLogs/widget.changeLogs.tpl.html
@@ -0,0 +1,26 @@
+<div class="x_title">
+    <h3>Change Logs</h3>
+</div>
+<div class="x_content">
+    <div class="dashboard-widget-content">
+        <ul class="list-unstyled timeline widget">
+            <li ng-repeat="changeLog in ::changeLogs">
+                <div class="block">
+                    <div class="block_content">
+                        <h2 class="title">{{::changeLog.level}}</h2>
+                        <div class="byline">
+                            <span>{{::getRelativeTime(changeLog.lastUpdated)}}</span> by <a>{{::changeLog.user}}</a>
+                        </div>
+                        <p class="excerpt">{{::changeLog.message}}</p>
+                    </div>
+                </div>
+            </li>
+        </ul>
+    </div>
+    <div class="text-center">
+        <a>
+            <strong><a ng-click="navigateToPath('/admin/change-logs')">See All Change Logs</a></strong>
+            <i class="fa fa-angle-right"></i>
+        </a>
+    </div>
+</div>

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/routing/WidgetRoutingController.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/routing/WidgetRoutingController.js b/traffic_ops/experimental/ui/app/src/common/modules/widget/routing/WidgetRoutingController.js
new file mode 100644
index 0000000..dc8f03c
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/routing/WidgetRoutingController.js
@@ -0,0 +1,33 @@
+/*
+ * 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 WidgetRoutingController = function(routing, $scope) {
+
+	var init = function() {
+		if (routing) {
+			// only set this if it's passed in
+			$scope.routing = routing;
+		}
+	};
+	init();
+
+};
+
+WidgetRoutingController.$inject = ['routing', '$scope'];
+module.exports = WidgetRoutingController;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/routing/index.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/routing/index.js b/traffic_ops/experimental/ui/app/src/common/modules/widget/routing/index.js
new file mode 100644
index 0000000..b7b3e94
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/routing/index.js
@@ -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.
+ */
+
+module.exports = angular.module('trafficOps.widget.routing', [])
+	.controller('WidgetRoutingController', require('./WidgetRoutingController'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/modules/widget/routing/widget.routing.tpl.html
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/modules/widget/routing/widget.routing.tpl.html b/traffic_ops/experimental/ui/app/src/common/modules/widget/routing/widget.routing.tpl.html
new file mode 100644
index 0000000..d45372c
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/modules/widget/routing/widget.routing.tpl.html
@@ -0,0 +1,88 @@
+<div class="x_title">
+    <h3>Routing Methods</h3>
+</div>
+<div class="x_content">
+    <div class="widget_summary">
+        <div class="w_left w_25">
+            <span>Native</span>
+        </div>
+        <div class="w_center w_55">
+            <div class="progress">
+                <div class="progress-bar bg-green" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 60%;">
+                    <span class="sr-only">60% Complete</span>
+                </div>
+            </div>
+        </div>
+        <div class="w_right w_20">
+            <span>60%</span>
+        </div>
+        <div class="clearfix"></div>
+    </div>
+
+    <div class="widget_summary">
+        <div class="w_left w_25">
+            <span>3rd Party</span>
+        </div>
+        <div class="w_center w_55">
+            <div class="progress">
+                <div class="progress-bar bg-green" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 20%;">
+                    <span class="sr-only">60% Complete</span>
+                </div>
+            </div>
+        </div>
+        <div class="w_right w_20">
+            <span>20%</span>
+        </div>
+        <div class="clearfix"></div>
+    </div>
+    <div class="widget_summary">
+        <div class="w_left w_25">
+            <span>DSR</span>
+        </div>
+        <div class="w_center w_55">
+            <div class="progress">
+                <div class="progress-bar bg-green" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 10%;">
+                    <span class="sr-only">60% Complete</span>
+                </div>
+            </div>
+        </div>
+        <div class="w_right w_20">
+            <span>10%</span>
+        </div>
+        <div class="clearfix"></div>
+    </div>
+    <div class="widget_summary">
+        <div class="w_left w_25">
+            <span>Static</span>
+        </div>
+        <div class="w_center w_55">
+            <div class="progress">
+                <div class="progress-bar bg-green" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 5%;">
+                    <span class="sr-only">60% Complete</span>
+                </div>
+            </div>
+        </div>
+        <div class="w_right w_20">
+            <span>5%</span>
+        </div>
+        <div class="clearfix"></div>
+    </div>
+    <div class="widget_summary">
+        <div class="w_left w_25">
+            <span>Federated</span>
+        </div>
+        <div class="w_center w_55">
+            <div class="progress">
+                <div class="progress-bar bg-green" role="progressbar" aria-valuenow="60" aria-valuemin="0" aria-valuemax="100" style="width: 5%;">
+                    <span class="sr-only">60% Complete</span>
+                </div>
+            </div>
+        </div>
+        <div class="w_right w_20">
+            <span>5%</span>
+        </div>
+        <div class="clearfix"></div>
+    </div>
+
+</div>
+

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/service/utils/DateUtils.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/service/utils/DateUtils.js b/traffic_ops/experimental/ui/app/src/common/service/utils/DateUtils.js
new file mode 100644
index 0000000..e554d02
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/service/utils/DateUtils.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.
+ */
+
+var DateUtils = function() {
+
+	this.dateFormat = function () {
+		// source: http://blog.stevenlevithan.com/archives/date-time-format
+		var	token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
+			timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
+			timezoneClip = /[^-+\dA-Z]/g,
+			pad = function (val, len) {
+				val = String(val);
+				len = len || 2;
+				while (val.length < len) val = "0" + val;
+				return val;
+			};
+
+		// Regexes and supporting functions are cached through closure
+		return function (date, mask, utc) {
+			var dF = this.dateFormat;
+
+			// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
+			if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
+				mask = date;
+				date = undefined;
+			}
+
+			// Passing date through Date applies Date.parse, if necessary
+			date = date ? new Date(date) : new Date;
+			if (isNaN(date)) throw SyntaxError("invalid date");
+
+			mask = String(dF.masks[mask] || mask || dF.masks["default"]);
+
+			// Allow setting the utc argument via the mask
+			if (mask.slice(0, 4) == "UTC:") {
+				mask = mask.slice(4);
+				utc = true;
+			}
+
+			var	_ = utc ? "getUTC" : "get",
+				d = date[_ + "Date"](),
+				D = date[_ + "Day"](),
+				m = date[_ + "Month"](),
+				y = date[_ + "FullYear"](),
+				H = date[_ + "Hours"](),
+				M = date[_ + "Minutes"](),
+				s = date[_ + "Seconds"](),
+				L = date[_ + "Milliseconds"](),
+				o = utc ? 0 : date.getTimezoneOffset(),
+				flags = {
+					d:    d,
+					dd:   pad(d),
+					ddd:  dF.i18n.dayNames[D],
+					dddd: dF.i18n.dayNames[D + 7],
+					m:    m + 1,
+					mm:   pad(m + 1),
+					mmm:  dF.i18n.monthNames[m],
+					mmmm: dF.i18n.monthNames[m + 12],
+					yy:   String(y).slice(2),
+					yyyy: y,
+					h:    H % 12 || 12,
+					hh:   pad(H % 12 || 12),
+					H:    H,
+					HH:   pad(H),
+					M:    M,
+					MM:   pad(M),
+					s:    s,
+					ss:   pad(s),
+					l:    pad(L, 3),
+					L:    pad(L > 99 ? Math.round(L / 10) : L),
+					t:    H < 12 ? "a"  : "p",
+					tt:   H < 12 ? "am" : "pm",
+					T:    H < 12 ? "A"  : "P",
+					TT:   H < 12 ? "AM" : "PM",
+					Z:    utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
+					o:    (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
+					S:    ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
+				};
+
+			return mask.replace(token, function ($0) {
+				return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
+			});
+		};
+	}();
+
+	this.dateFormat.masks = {
+		"default":      "ddd mmm dd yyyy HH:MM:ss",
+		shortDate:      "m/d/yy",
+		mediumDate:     "mmm d, yyyy",
+		longDate:       "mmmm d, yyyy",
+		fullDate:       "dddd, mmmm d, yyyy",
+		shortTime:      "h:MM TT",
+		mediumTime:     "h:MM:ss TT",
+		longTime:       "h:MM:ss TT Z",
+		isoDate:        "yyyy-mm-dd",
+		isoTime:        "HH:MM:ss",
+		isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
+		isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
+	};
+
+	this.dateFormat.i18n = {
+		dayNames: [
+			"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
+			"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
+		],
+		monthNames: [
+			"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+			"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
+		]
+	};
+
+};
+
+DateUtils.$inject = [];
+module.exports = DateUtils;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/service/utils/NumberUtils.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/service/utils/NumberUtils.js b/traffic_ops/experimental/ui/app/src/common/service/utils/NumberUtils.js
new file mode 100644
index 0000000..9a66563
--- /dev/null
+++ b/traffic_ops/experimental/ui/app/src/common/service/utils/NumberUtils.js
@@ -0,0 +1,80 @@
+/*
+
+
+ Licensed 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 NumberUtils = function($filter) {
+
+	var k = 1000,
+		sizes = ['B', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb'];
+
+	this.addCommas = function(nStr)
+	{
+		nStr += '';
+		x = nStr.split('.');
+		x1 = x[0];
+		x2 = x.length > 1 ? '.' + x[1] : '';
+		var rgx = /(\d+)(\d{3})/;
+		while (rgx.test(x1)) {
+			x1 = x1.replace(rgx, '$1' + ',' + '$2');
+		}
+		return x1 + x2;
+	};
+
+	/**
+	 * This function takes big scary kilobit numbers and 'shrinks' them to a friendly version
+	 * i.e. 10,000 kilobits is easier read as 10 megabits...
+	 */
+	this.shrink = function(kilounits) {
+		if (!angular.isNumber(kilounits) || kilounits == 0) return [ 0, 'Kb' ];
+		var units = kilounits * 1000;
+		var i = Math.floor(Math.log(units) / Math.log(k));
+		if (i < 1) { i = 1 } // kilobits is the lowest we will go
+		if (i > 5) { i = 5 } // petabits is the highest we will go
+		return [ Math.round((units / Math.pow(k, i)) * 100) / 100, sizes[i] ];
+	};
+
+	this.convertTo = function(kilounits, size) {
+		if (!angular.isNumber(kilounits)) return null;
+		if (kilounits == 0) return 0;
+		var units = kilounits * 1000;
+		var i = sizes.indexOf(size);
+		if (i == -1) {
+			return 0;
+		}
+		return Math.round((units / Math.pow(k, i)) * 100) / 100;
+	};
+
+	this.average = function(arr)
+	{
+		if (!angular.isArray(arr) || arr.length == 0 ) return 0;
+		return _.reduce(arr, function(memo, num) {
+				return memo + num;
+			}, 0) / arr.length;
+	}
+
+	this.ratio = function(numerator, denominator)
+	{
+		if (numerator === 0 || denominator === 0) {
+			return 'N/A';
+		} else {
+			return $filter('number')(numerator/denominator, 2) + ':1';
+		}
+	}
+
+};
+
+NumberUtils.$inject = ['$filter'];
+module.exports = NumberUtils;

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/common/service/utils/index.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/common/service/utils/index.js b/traffic_ops/experimental/ui/app/src/common/service/utils/index.js
index 0eaa2d1..1420100 100644
--- a/traffic_ops/experimental/ui/app/src/common/service/utils/index.js
+++ b/traffic_ops/experimental/ui/app/src/common/service/utils/index.js
@@ -18,7 +18,9 @@
  */
 
 module.exports = angular.module('trafficOps.utils', [])
+    .service('dateUtils', require('./DateUtils'))
     .service('formUtils', require('./FormUtils'))
     .service('locationUtils', require('./LocationUtils'))
+    .service('numberUtils', require('./NumberUtils'))
     .service('serverUtils', require('./ServerUtils'))
     .service('stringUtils', require('./StringUtils'));

http://git-wip-us.apache.org/repos/asf/incubator-trafficcontrol/blob/a81c975a/traffic_ops/experimental/ui/app/src/modules/private/monitor/dashboard/DashboardController.js
----------------------------------------------------------------------
diff --git a/traffic_ops/experimental/ui/app/src/modules/private/monitor/dashboard/DashboardController.js b/traffic_ops/experimental/ui/app/src/modules/private/monitor/dashboard/DashboardController.js
index 2abe702..e4715d3 100644
--- a/traffic_ops/experimental/ui/app/src/modules/private/monitor/dashboard/DashboardController.js
+++ b/traffic_ops/experimental/ui/app/src/modules/private/monitor/dashboard/DashboardController.js
@@ -17,32 +17,21 @@
  * under the License.
  */
 
-var DashboardController = function(changeLogs, cacheGroupHealth, $scope, locationUtils) {
+var DashboardController = function(cacheGroupHealth, cdns, currentStats, $scope) {
 
-	$scope.changeLogs = changeLogs;
+	$scope.cacheGroupHealth = cacheGroupHealth;
 
-	$scope.getRelativeTime = function(date) {
-		return moment(date).fromNow();
-	};
+	$scope.cdns = _.filter(cdns, function(cdn) {
+		// we don't want the "ALL" cdn which is not really a cdn
+		return cdn.name != 'ALL';
+	});
 
-	$scope.navigateToPath = locationUtils.navigateToPath;
-
-
-	$scope.locationHealth = {
-		totalOnline: cacheGroupHealth.totalOnline,
-		totalOffline: cacheGroupHealth.totalOffline,
-		locations: cacheGroupHealth.cachegroups
-	};
-
-	// pagination
-	$scope.currentLocationPage = 1;
-	$scope.locationsPerPage = 10;
-
-	$scope.onlinePercent = function(location) {
-		return location.online / (location.online + location.offline);
-	};
+	$scope.totalStats = _.find(currentStats.currentStats, function(item) {
+		// total stats are buried in a hash where cdn = total
+		return item.cdn == 'total';
+	});
 
 };
 
-DashboardController.$inject = ['changeLogs', 'cacheGroupHealth', '$scope', 'locationUtils'];
+DashboardController.$inject = ['cacheGroupHealth', 'cdns', 'currentStats', '$scope'];
 module.exports = DashboardController;