Merge pull request #543 from jaredevantabor/dash-graphs

Updating the dashboard graphs to 3.0
This commit is contained in:
jaredevantabor
2015-12-18 12:48:22 -08:00
17 changed files with 372 additions and 136 deletions

View File

@@ -1093,13 +1093,13 @@ input[type="checkbox"].checkbox-no-label {
.icon-job-changed:before,
.icon-job-ok:before,
.icon-job-OK:before,
.icon-job-failed:before,
.icon-job-skipped:before {
content: "\f111";
}
.icon-job-stopped:before,
.icon-job-error:before,
.icon-job-failed:before,
.icon-job-canceled:before,
.icon-job-unreachable:before {
content: "\f06a";

View File

@@ -24,6 +24,7 @@ body {
padding-left: 20px;
padding-right: 20px;
}
#main-menu-container {
.navbar {
margin-bottom: 0;

View File

@@ -35,11 +35,12 @@ svg.nvd3-svg {
.nvtooltip {
position: absolute;
background-color: rgba(255,255,255,1.0);
background-color: #848992;
padding: 1px;
border: 1px solid rgba(0,0,0,.2);
border: 1px solid #848992;
border-radius: 5px;
z-index: 10000;
color: #ffffff;
font-family: Arial;
font-size: 13px;
text-align: left;
@@ -70,7 +71,7 @@ svg.nvd3-svg {
.nvtooltip.x-nvtooltip,
.nvtooltip.y-nvtooltip {
padding: 8px;
padding: 10px;
}
.nvtooltip h3 {
@@ -127,9 +128,11 @@ svg.nvd3-svg {
}
.nvtooltip table td.legend-color-guide div {
width: 8px;
height: 8px;
width: 12px;
height: 12px;
vertical-align: middle;
border: 1px solid #ffffff;
border-radius: 5px;
}
.nvtooltip .footer {
@@ -569,6 +572,8 @@ svg.nvd3-svg {
.nvd3.nv-historicalStockChart .nv-axis .nv-axislabel {
font-weight: bold;
fill: #848992;
font-family: 'Open Sans';
}
.nvd3.nv-historicalStockChart .nv-dragTarget {

View File

@@ -476,7 +476,7 @@ nv.nearestValueIndex = function (values, searchVal, threshold) {
theadEnter.append("tr")
.append("td")
.attr("colspan",3)
.append("strong")
// .append("strong")
.classed("x-value",true)
.html(headerFormatter(d.value));
@@ -9744,8 +9744,8 @@ nv.models.scatterChart = function() {
, showYAxis = true
, rightAlignYAxis = false
, tooltips = true
, tooltipX = function(key, x, y) { return '<strong>' + x + '</strong>' }
, tooltipY = function(key, x, y) { return '<strong>' + y + '</strong>' }
, tooltipX = function(key, x, y) { return '<div>' + x + '</div>' }
, tooltipY = function(key, x, y) { return '<div>' + y + '</div>' }
, tooltip = function(key, x, y, date) { return '<h3>' + key + '</h3>'
+ '<p>' + date + '</p>' }
, state = nv.utils.state()

View File

@@ -3,54 +3,76 @@
@import "../../shared/branding/colors.less";
.DashboardGraphs {
margin-top: 15px;
border: solid 1px #a9a9a9;
border-radius: 4px;
margin-top: 20px;
border: solid 1px #e1e1e1;
border-radius: 5px;
background-color: #ffffff;
padding-top:20px;
padding-left: 20px;
padding-right: 20px;
}
.DashboardGraphs-tabSection {
flex: 1;
.DashboardGraphs-headerSection{
display: flex;
// align-items: baseline;
flex-direction: row;
flex-wrap: wrap;
align-items: flex-start;
justify-content: flex-start;
}
.DashboardGraphs-tab {
flex: 1;
padding: 10px;
border-right: solid 1px @disabled-item-border;
border-bottom: solid 1px @disabled-item-border;
color: #b7b7b7;
background-color: #ffffff;
font-size: 12px;
border: 1px solid #e1e1e1;
height: 30px;
border-radius: 5px;
margin-right: 20px;
padding-left: 10px;
padding-right: 10px;
padding-bottom: 5px;
padding-top: 5px;
transition: background-color 0.2s;
text-transform: uppercase;
text-align: center;
font-size: 20px;
color: @disabled-item-text;
background-color: @disabled-item-background;
white-space: nowrap;
}
.DashboardGraphs-tab--firstTab {
border-top-left-radius: 4px;
width: 90px;
}
.DashboardGraphs-tab--lastTab {
border-top-right-radius: 4px;
border-right: 0;
width:100px;
margin-right: auto;
}
.DashboardGraphs-tab:hover {
color: #000;
color: #b7b7b7;
background-color: #f6f6f6;
cursor: pointer;
}
.DashboardGraphs-tab.is-selected {
background-color: @enabled-item-background;
color: @enabled-item-text;
border-bottom: 0;
.DashboardGraphs-tab:active {
color: #b7b7b7;
background-color: #d7d7d7;
cursor: pointer;
}
.DashboardGraphs-tab.is-selected:hover {
cursor: default;
.DashboardGraphs-tab:focus {
color: #b7b7b7;
}
.DashboardGraphs-tab.is-selected {
color: #ffffff;
background-color: #d7d7d7;
}
.DashboardGraphs-graphSection {
display: block;
flex: 1;
padding-top:20px;
}
.DashboardGraphs-graphContainer {
@@ -63,25 +85,72 @@
display: block;
}
.DashboardGraphs-filterLabelIcon{
color: #d7d7d7;
font-size: 14px;
padding-top: 5px;
}
.DashboardGraphs-filterLabel{
color: #d7d7d7;
font-size: 12px;
padding-right: 10px;
padding-left: 10px;
padding-top: 2px;
text-transform: uppercase;
padding-top:5px;
}
.DashboardGraphs-graph {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
margin-bottom: 19px;
padding: 20px;
}
.nv-axislabel {
font-weight: bold !important;
fill: #b7b7b7 !important;
font-family: 'Open Sans' !important;
}
.nv-axis text {
fill: #b7b7b7 !important; //rgb(169, 178, 189);
font-family: 'Open Sans' !important;
}
.DashboardGraphs-graphToolbar {
display: flex;
justify-content: flex-end;
margin-bottom: 6px;
flex-direction: row;
flex-wrap: wrap;
}
.DashboardGraphs-filterDropdown {
.DashboardGraphs-filterDropdownText {
flex: initial;
color: #b7b7b7;
background-color: #ffffff;
font-size: 12px;
padding-right: 15px;
text-transform: uppercase;
white-space: nowrap;
padding-right: 10px;
padding-left: 10px;
height: 20px;
border: 1px solid #e1e1e1;
border-radius: 5px;
transition: background-color 0.2s;
}
.DashboardGraphs-filterDropdownText:hover {
color: #b7b7b7;
background-color: #f6f6f6;
}
.DashboardGraphs-filterIcon{
color: #d7d7d7;
font-size: 14px;
width: 20px;
padding-left:10px;
padding-right: 10px;
}
.DashboardGraphs-filterDropdownItems {
@@ -89,6 +158,15 @@
left: auto;
top: auto;
box-shadow: none;
text-transform: uppercase;
}
.DashboardGraphs-periodDropdown{
padding-top:5px;
}
.DashboardGraphs-jobTypeDropdown{
padding-top:5px;
}
.DashboardGraphs-filterDropdownItems--period {
@@ -98,3 +176,41 @@
.DashboardGraphs-filterDropdownItems--jobType {
margin-left: -84px;
}
.DashboardGraphs-statusFilters{
padding-top: 5px;
}
.DashboardGraphs-statusFilter{
color: #b7b7b7;
background-color: #ffffff;
font-size: 12px;
text-transform: uppercase;
padding-right: 10px;
padding-left: 10px;
height: 20px;
border: 1px solid #e1e1e1;
border-radius: 5px;
transition: background-color 0.2s;
margin-left: 10px;
line-height:1;
}
.DashboardGraphs-statusFilter:hover{
cursor: pointer;
background-color: #f6f6f6;
}
.DashboardGraphs-statusFilter.is-selected {
color: #ffffff;
background-color: #d7d7d7;
}
.DashboardGraphs-hostStatusLabel--successful{
text-anchor: start !important;
}
.DashboardGraphs-hostStatusLabel--failed{
text-anchor: end !important;
}

View File

@@ -12,6 +12,11 @@ export default
scope.hostStatusSelected = false;
}
function clearStatus() {
scope.isSuccessful = true;
scope.isFailed = true;
}
scope.toggleGraphStatus = function (graphType) {
clearGraphs();
if (graphType === "jobStatus") {
@@ -22,7 +27,38 @@ export default
scope.$broadcast("resizeGraphs");
};
scope.toggleJobStatusGraph = function (status) {
if (status === "successful") {
scope.isSuccessful = !scope.isSuccessful;
if(!scope.isSuccessful && scope.isFailed){
status = 'successful';
}
else if(scope.isSuccessful && scope.isFailed){
status = 'both';
}
else if(!scope.isSuccessful && !scope.isFailed){
status = 'successful';
scope.isFailed = true;
}
} else if (status === "failed") {
scope.isFailed = !scope.isFailed;
if(scope.isSuccessful && scope.isFailed){
status = 'both';
}
if(scope.isSuccessful && !scope.isFailed){
status = 'failed';
}
else if(!scope.isSuccessful && !scope.isFailed){
status = 'failed';
scope.isSuccessful = true;
}
}
scope.$broadcast("jobStatusChange", status);
};
// initially toggle jobStatus graph
clearStatus();
clearGraphs();
scope.toggleGraphStatus("jobStatus");
}

View File

@@ -1,5 +1,5 @@
<div class="DashboardGraphs">
<div class="DashboardGraphs-tabSection">
<div class="DashboardGraphs-headerSection">
<div class="DashboardGraphs-tab DashboardGraphs-tab--firstTab"
ng-click="toggleGraphStatus('jobStatus')"
ng-class="{'is-selected': jobStatusSelected }">
@@ -10,6 +10,70 @@
ng-class="{'is-selected': hostStatusSelected }">
Host Status
</div>
<div class="DashboardGraphs-graphToolbar" ng-show="!hostStatusSelected">
<i class="fa fa-filter DashboardGraphs-filterLabelIcon"></i>
<div class="DashboardGraphs-filterLabel">Period</div>
<div class="DashboardGraphs-periodDropdown">
<a id="period-dropdown" role="button"
data-toggle="dropdown"
data-target="#"
href="/page.html"
class="DashboardGraphs-filterDropdownText">
Past Month <i class="fa fa-chevron-down DashboardGraphs-filterIcon"></i>
</a>
<ul class="dropdown-menu DashboardGraphs-filterDropdownItems
DashboardGraphs-filterDropdownItems--period" role="menu" aria-labelledby="period-dropdown">
<li>
<a class="n" id="day" >Past 24 Hours </a>
</li>
<li>
<a class="n" id="week">Past Week</a>
</li>
<li>
<a class="n" id="month">Past Month</a>
</li>
</ul>
</div>
<div class="DashboardGraphs-filterLabel">Job Type</div>
<div class="DashboardGraphs-jobTypeDropdown">
<a id="type-dropdown" role="button" data-toggle="dropdown" data-target="#" class="DashboardGraphs-filterDropdownText"
href="/page.html">
All <i class="fa fa-chevron-down DashboardGraphs-filterIcon"></i>
</a>
<ul class="dropdown-menu DashboardGraphs-filterDropdownItems
DashboardGraphs-filterDropdownItems--jobType" role="menu" aria-labelledby="type-dropdown">
<li>
<a class="m" id="all">All</a>
</li>
<li>
<a class="m" id="inv_sync">Inventory Sync</a>
</li>
<li>
<a class="m" id="scm_update">SCM Update</a>
</li>
<li>
<a class="m" id="playbook_run">Playbook Run</a>
</li>
</ul>
</div>
<div class="DashboardGraphs-statusFilters">
<button class="DashboardGraphs-statusFilter DashboardGraphs-statusFilter--jobStatus"
ng-click="toggleJobStatusGraph('successful')"
ng-class="{'is-selected': isSuccessful }">
<i class="fa icon-job-successful DashboardGraphs-statusFilterIcon">
</i> Successful
</button>
<button class="DashboardGraphs-statusFilter DashboardGraphs-statusFilter--jobStatus"
ng-click="toggleJobStatusGraph('failed')"
ng-class="{'is-selected': isFailed }">
<i class="fa icon-job-failed DashboardGraphs-statusFilterIcon">
</i> Failed
</button>
</div>
</div>
</div>
<div class="DashboardGraphs-graphSection">
<div class="DashboardGraphs-graphContainer" auto-size-module

View File

@@ -20,7 +20,7 @@ function AutoSizeModule($window) {
function adjustSize() {
if (attrs.graphType === "hostStatus") {
if (element.parent().width() > 596) {
element.height(596);
element.height(320);//596);
} else {
element.height(element.parent().width());
}

View File

@@ -24,7 +24,8 @@ function HostStatusGraph($compile, $window, adjustGraphSize, templateUrl) {
scope.$watch(attr.data, function(data) {
if (data && data.hosts) {
createGraph(data);
scope.data = data;
createGraph();
}
});
@@ -52,31 +53,52 @@ function HostStatusGraph($compile, $window, adjustGraphSize, templateUrl) {
$(".DashboardGraphs-graph--hostStatusGraph").removeResize(adjustHostGraphSize);
});
function createGraph(data) {
if(data.hosts.total+data.hosts.failed>0){
data = [
{ "label": "Successful",
"color": "#60D66F",
"value" : data.hosts.total - data.hosts.failed
} ,
{ "label": "Failed",
function createGraph() {
var data, colors, color;
if(scope.data.hosts.total+scope.data.hosts.failed>0){
if(scope.status === "successful"){
data = [
{ "label": "SUCCESSFUL",
"color": "#5bbdbf",
"value" : scope.data.hosts.total - scope.data.hosts.failed
}];
colors = ['#5bbdbf'];
}
else if (scope.status === "failed"){
data = [{ "label": "FAILED",
"color" : "#ff5850",
"value" : data.hosts.failed
}
];
"value" : scope.data.hosts.failed
}];
colors = ['#ff5850'];
}
else {
data = [
{ "label": "SUCCESSFUL",
"color": "#5bbdbf",
"value" : scope.data.hosts.total - scope.data.hosts.failed
} ,
{ "label": "FAILED",
"color" : "#ff5850",
"value" : scope.data.hosts.failed
}
];
colors = ['#5bbdbf', '#ff5850'];
}
host_pie_chart = nv.models.pieChart()
.margin({bottom: 15})
.x(function(d) { return d.label; })
.x(function(d) {
return d.label +': '+ Math.round((d.value/scope.data.hosts.total)*100) + "%";
})
.y(function(d) { return d.value; })
.showLabels(true)
.showLegend(false)
.growOnHover(false)
.labelThreshold(0.01)
.tooltipContent(function(x, y) {
return '<b>'+x+'</b>'+ '<p>' + Math.floor(y.replace(',','')) + ' Hosts ' + '</p>';
return '<p>'+x+'</p>'+ '<p>' + Math.floor(y.replace(',','')) + ' HOSTS ' + '</p>';
})
.labelType("percent")
.color(['#60D66F', '#ff5850']);
.color(colors);
d3.select(element.find('svg')[0])
.datum(data)
@@ -88,6 +110,33 @@ function HostStatusGraph($compile, $window, adjustGraphSize, templateUrl) {
"font-weight":400,
"src": "url(/static/assets/OpenSans-Regular.ttf)"
});
if(scope.status === "failed"){
color = "#ff5850";
}
else{
color = "#5bbdbf";
}
d3.select(element.find(".nv-label text")[0])
.attr("class", "DashboardGraphs-hostStatusLabel--successful")
.style({
"font-family": 'Open Sans',
"text-anchor": "start",
"font-size": "16px",
"text-transform" : "uppercase",
"fill" : color,
"src": "url(/static/assets/OpenSans-Regular.ttf)"
});
d3.select(element.find(".nv-label text")[1])
.attr("class", "DashboardGraphs-hostStatusLabel--failed")
.style({
"font-family": 'Open Sans',
"text-anchor" : "end !imporant",
"font-size": "16px",
"text-transform" : "uppercase",
"fill" : "#ff5850",
"src": "url(/static/assets/OpenSans-Regular.ttf)"
});
adjustGraphSize();
return host_pie_chart;

View File

@@ -1 +1 @@
<svg width="100%" height="100%" preserveAspectRatio="xMinYMin"></svg>
<svg width="100%" height="100%" ></svg>

Before

Width:  |  Height:  |  Size: 70 B

After

Width:  |  Height:  |  Size: 40 B

View File

@@ -34,35 +34,46 @@ function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustG
scope.$watch('data', function(value) {
if (value) {
createGraph(scope.period, scope.jobType, value);
createGraph(scope.period, scope.jobType, value, scope.status);
}
}, true);
function recreateGraph(period, jobType) {
graphDataService.get(period, jobType)
function recreateGraph(period, jobType, status) {
graphDataService.get(period, jobType, status)
.then(function(data) {
scope.data = data;
scope.period = period;
scope.jobType = jobType;
scope.status = status;
});
}
function createGraph(period, jobtype, data){
scope.$on('jobStatusChange', function(event, status){
recreateGraph(scope.period, scope.jobType, status);
});
function createGraph(period, jobtype, data, status){
scope.period = period;
scope.jobType = jobtype;
scope.status = status;
var timeFormat, graphData = [
{ "color": "#60D66F",
"key": "Successful",
{ "color": "#5bbdbf",
"key": "SUCCESSFUL",
"values": data.jobs.successful
},
{ "key" : "Failed" ,
{ "key" : "FAILED" ,
"color" : "#ff5850",
"values": data.jobs.failed
}
];
graphData = _.reject(graphData, function(num){
if(status!== undefined && status === num.key.toLowerCase()){
return num;
}
});
if(period==="day") {
timeFormat="%H:%M";
}
@@ -82,20 +93,22 @@ function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustG
job_status_chart
.x(function(d,i) { return i; })
.useInteractiveGuideline(true) //We want nice looking tooltips and a guideline!
.showLegend(true) //Show the legend, allowing users to turn on/off line series.
.showLegend(false) //Show the legend, allowing users to turn on/off line series.
.showYAxis(true) //Show the y-axis
.showXAxis(true); //Show the x-axis
job_status_chart.interactiveLayer.tooltip.fixedTop(-10); //distance from the top of the chart to tooltip
job_status_chart.interactiveLayer.tooltip.distance(-1); //distance from interactive line to tooltip
job_status_chart.xAxis
.axisLabel("Time")//.showMaxMin(true)
.axisLabel("TIME")//.showMaxMin(true)
.tickFormat(function(d) {
var dx = graphData[0].values[d] && graphData[0].values[d].x || 0;
return dx ? d3.time.format(timeFormat)(new Date(Number(dx+'000'))) : '';
});
job_status_chart.yAxis //Chart y-axis settings
.axisLabel('Jobs')
.axisLabel('JOBS')
.tickFormat(d3.format('.f'));
d3.select(element.find('svg')[0])
@@ -109,27 +122,25 @@ function JobStatusGraph($rootScope, $compile , $location, $window, Wait, adjustG
});
// when the Period drop down filter is used, create a new graph based on the
d3.selectAll(element.find(".n"))
.on("click", function() {
$('.n').on("click", function(){
period = this.getAttribute("id");
$('#period-dropdown').replaceWith("<a id=\"period-dropdown\" role=\"button\" data-toggle=\"dropdown\" data-target=\"#\" href=\"/page.html\">"+this.text+"<span class=\"caret\"><span>\n");
$('#period-dropdown').replaceWith("<a id=\"period-dropdown\" class=\"DashboardGraphs-filterDropdownText\" role=\"button\" data-toggle=\"dropdown\" data-target=\"#\" href=\"/page.html\">"+this.text+
"<i class=\"fa fa-chevron-down DashboardGraphs-filterIcon\"></i>\n");
scope.$parent.isFailed = true;
scope.$parent.isSuccessful = true;
recreateGraph(period, job_type);
});
//On click, update with new data
d3.selectAll(element.find(".m"))
.on("click", function() {
$('.m').on("click", function(){
job_type = this.getAttribute("id");
$('#type-dropdown').replaceWith("<a id=\"type-dropdown\" role=\"button\" data-toggle=\"dropdown\" data-target=\"#\" href=\"/page.html\">"+this.text+"<span class=\"caret\"><span>\n");
$('#type-dropdown').replaceWith("<a id=\"type-dropdown\" class=\"DashboardGraphs-filterDropdownText\" role=\"button\" data-toggle=\"dropdown\" data-target=\"#\" href=\"/page.html\">"+this.text+
"<i class=\"fa fa-chevron-down DashboardGraphs-filterIcon\"></i>\n");
scope.$parent.isFailed = true;
scope.$parent.isSuccessful = true;
recreateGraph(period, job_type);
});
job_status_chart.legend.margin({top: 1, right:0, left:24, bottom: 0});
adjustGraphSize(job_status_chart, element);
}
function onResize() {

View File

@@ -14,13 +14,16 @@ export default
function JobStatusGraphData(Rest, getBasePath, processErrors, $rootScope, $q) {
function pluck(property, promise) {
function pluck(property, promise, status) {
return promise.then(function(value) {
if(status === "successful" || status === "failed"){
delete value[property].jobs[status];
}
return value[property];
});
}
function getData(period, jobType) {
function getData(period, jobType, status) {
var url, dash_path = getBasePath('dashboard');
if(dash_path === '' ){
processErrors(null,
@@ -48,7 +51,7 @@ function JobStatusGraphData(Rest, getBasePath, processErrors, $rootScope, $q) {
return $q.reject(response);
});
return pluck('data', result);
return pluck('data', result, status);
}
return {
@@ -64,12 +67,12 @@ function JobStatusGraphData(Rest, getBasePath, processErrors, $rootScope, $q) {
});
});
},
get: function(period, jobType) {
get: function(period, jobType, status) {
this.destroyWatcher();
this.setupWatcher(period, jobType);
return getData(period, jobType);
return getData(period, jobType, status);
}
};

View File

@@ -1,46 +1 @@
<div class="DashboardGraphs-graphToolbar">
<div class="DashboardGraphs-filterDropdown">
Period:
<a id="period-dropdown" role="button" data-toggle="dropdown" data-target="#" href="/page.html">
Past Month<span class="caret"></span>
</a>
<ul class="dropdown-menu DashboardGraphs-filterDropdownItems
DashboardGraphs-filterDropdownItems--period" role="menu" aria-labelledby="period-dropdown">
<li>
<a class="n" id="day" >Past 24 Hours </a>
</li>
<li>
<a class="n" id="week">Past Week</a>
</li>
<li>
<a class="n" id="month">Past Month</a>
</li>
</ul>
</div>
<div class="DashboardGraphs-filterDropdown">
Job Type:
<a id="type-dropdown" role="button" data-toggle="dropdown" data-target="#" href="/page.html">
All<span class="caret"></span>
</a>
<ul class="dropdown-menu DashboardGraphs-filterDropdownItems
DashboardGraphs-filterDropdownItems--jobType" role="menu" aria-labelledby="type-dropdown">
<li>
<a class="m" id="all">All</a>
</li>
<li>
<a class="m" id="inv_sync">Inventory Sync</a>
</li>
<li>
<a class="m" id="scm_update">SCM Update</a>
</li>
<li>
<a class="m" id="playbook_run">Playbook Run</a>
</li>
</ul>
</div>
</div>
<svg width="100%" height="100%"></svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 39 B

View File

@@ -2,7 +2,7 @@
.Footer {
height: 40px;
background-color: #d7d7d7;
background-color: #f6f6f6;
color: #848992;
width: 100%;
z-index: 1040;

View File

@@ -62,7 +62,6 @@ export default
// if the user clicks outside of the mobile menu,
// close it if it's open
$("body").on('click', function(e) {
e.stopPropagation();
if ($(e.target).parents(".MainMenu").length === 0) {
scope.isHiddenOnMobile = true;
}

View File

@@ -26,7 +26,7 @@
@tip-background: #0088CC;
@tip-color: #fff;
@green: #60D66F;
@green: #5bbdbf;
@red: #ff5850;
@red-hover: #FA8C87;
@red-focus: #FF1105;

View File

@@ -50,9 +50,6 @@
</div>
</div>
<!-- Password Dialog -->
<div id="password-modal" style="display: none;"></div>
<div id="idle-modal" style="display:none">Your session will expire in <span id="remaining_seconds">60</span> seconds, would you like to continue?</div>