EnVisageOnline/Main/Source/EnVisage/Scripts/Plugins/Optimuse.js

494 lines
21 KiB
JavaScript

(function ($) {
var settings = {
title: 'Resource Utilization',
titleClass: 'pv-title',
magnifyBlockCss: {
'height': '110px',
'padding-left': '5px',
'width': '245px'
},
magnifyClass: 'optimuse-bar',
actualMagnifyClass: 'pv-mock-opt',
actualMagnifyTitle: 'Actual',
actualMagnifyTitleCss: {
'margin-left': '-20px',
'margin-top': '-5px'
},
plannedMagnifyClass: 'pv-mock-opt',
plannedMagnifyTitle: 'Planned',
plannedMagnifyTitleCss: {
'margin-left': '-20px',
'margin-top': '-5px'
},
stackedChartClass: 'pv-chart optimuse-magnify',
stackedChartCss: {
'height': '175px',
'padding-left': '5px',
'padding-right': '5px',
'width': '225px'
},
chartModalTitle: 'Resource Usage',
chartModalActualTitle: 'Actual Resource Allocation',
chartModalPlannedTitle: 'Planned Resource Allocation',
isUOMHours: false,
data: null
};
var createSeparatorBlock = function () {
return $('<div class="magnify-seperator"></div>');
};
var createTitleBlock = function () {
return $('<div>' + settings.title + '</div>').addClass(settings.titleClass);
};
var createMagnifyBlock = function () {
var chartUniquePostfix = Math.uuid();
var magnifyActualTitle = $('<div>' + settings.actualMagnifyTitle + '</div>').css(settings.actualMagnifyTitleCss);
var magnifyActualBlock = $('<div data-magnify="actual" id="magnify-actual-' + chartUniquePostfix + '"></div>')
.addClass(settings.actualMagnifyClass)
.append(magnifyActualTitle);
var magnifyPlannedTitle = $('<div>' + settings.plannedMagnifyTitle + '</div>').css(settings.plannedMagnifyTitleCss);
var magnifyPlannedBlock = $('<div data-magnify="planned" id="magnify-planned-' + chartUniquePostfix + '"></div>')
.addClass(settings.plannedMagnifyClass)
.append(magnifyPlannedTitle);
var magnifyBlock = $('<div></div>').css(settings.magnifyBlockCss)
.addClass(settings.magnifyClass)
.append(magnifyActualBlock)
.append(magnifyPlannedBlock);
return magnifyBlock;
};
var createStackedChartBlock = function () {
var blockId = 'magnify-stacked-' + Math.uuid();
var block = $('<div data-magnify="stacked"></div>').addClass(settings.stackedChartClass)
.css(settings.stackedChartCss)
.attr('id', blockId);
return block;
};
var createResourceUsageDialog = function () {
var chartUniquePostfix = Math.uuid();
var dialog =
"<div class='modal fade' data-width='1000' tabindex='-1' role='dialog' style='display: none;'>\
<div class='modal-content'>\
<div class='modal-header'>\
<button type='button' class='close' data-dismiss='modal' aria-hidden='true'>x</button>\
<h4 class='modal-title'>"+ settings.chartModalTitle + "</h4>\
</div>\
<div class='modal-body'>\
<div data-magnify='actual' class='pv-mock-opt' id='magnify-actual-" + chartUniquePostfix + "' style='margin-bottom: -55px;'><div style='margin-left: -23px;; margin-top: -5px;'>" + settings.actualMagnifyTitle + "</div></div>\
<div style='height: 300px;' class='" + settings.stackedChartClass + "' data-chart='actual' id='chart-actual-" + chartUniquePostfix + "'></div>\
<br />\
<div data-magnify='planned' class='pv-mock-opt' id='magnify-planned-" + chartUniquePostfix + "' style='margin-bottom: -55px;'><div style='margin-left: -23px;; margin-top: -5px;'>" + settings.plannedMagnifyTitle + "</div></div>\
<div style='height: 300px;' class='" + settings.stackedChartClass + "' data-chart='planned' id='chart-planned-" + chartUniquePostfix + "'></div>\
</div>\
<div class='modal-footer'>\
<button type='button' class='btn btn-default' data-dismiss='modal'>Close</button>\
</div>\
</div>\
</div>";
return dialog;
};
var createPreloader = function () {
var preloader =
"<div class='loadRotator hidden'>\
<span>\
<img class='valign-middle' src='/Content/images/loadFA.gif' />&nbsp;&nbsp;loading...\
</span>\
</div>";
return preloader;
};
var createStackedChart = function ($this) {
var chartId = getStackedChartBlockId($this);
AmCharts.makeChart(chartId, {
"autoMargins": false,
"categoryField": "planvsact",
"categoryAxis": {
"gridPosition": "start",
"axisAlpha": 0,
"gridAlpha": 0
},
"export": {
"enabled": true
},
"fontFamily": "Open Sans",
"marginTop": 40,
"marginRight": 16,
"marginLeft": -20,
"marginBottom": 0,
"rotate": false,
"type": "serial",
"theme": "light",
"valueAxes": [{
"stackType": "100%",
"axisAlpha": 0,
"gridAlpha": 0,
"labelsEnabled": false,
"position": "left"
}],
"balloon": {
"textAlign": "left"
},
"graphs": [{
"fillAlphas": 0.9,
"fixedColumnWidth": 54,
"fontSize": 10,
"lineAlpha": 0.5,
"lineColor": "#ffe445",
"type": "column",
"labelText": "[[percents]]%",
"labelFunction": createStackedChartLabelText,
"valueField": "underallocatedWeeks",
"summaryField": "underallocatedSummary", // custom field, contact EN if you need some clarification
"balloonFunction": createStackedChartBalloonText
}, {
"fillAlphas": 0.9,
"fixedColumnWidth": 54,
"fontSize": 10,
"lineAlpha": 0.5,
"lineColor": "#b2d235",
"type": "column",
"labelText": "[[percents]]%",
"labelFunction": createStackedChartLabelText,
"valueField": "idealallocatedWeeks",
"summaryField": "idealallocatedSummary", // custom field, contact EN if you need some clarification
"balloonFunction": createStackedChartBalloonText
}, {
"fillAlphas": 0.9,
"fixedColumnWidth": 54,
"fontSize": 10,
"lineAlpha": 0.5,
"lineColor": "#ff2424",
"type": "column",
"labelText": "[[percents]]%",
"labelFunction": createStackedChartLabelText,
"valueField": "overallocatedWeeks",
"summaryField": "overallocatedSummary", // custom field, contact EN if you need some clarification
"balloonFunction": createStackedChartBalloonText
}]
});
};
var createStackedChartBalloonText = function (item, graph) {
var summary = item.dataContext[item.graph.summaryField];
var uomValueText = '';
if (settings.isUOMHours) {
uomValueText = '<td>Hours:</td><td>&nbsp;' + summary.MinHoursValue + ' - ' + summary.MaxHoursValue + '</td>';
} else {
uomValueText = '<td>Resources:</td><td>&nbsp;' + summary.MinResourcesValue + ' - ' + summary.MaxResourcesValue + '</td>';
}
var balloneText = '<table><tr>' + uomValueText + '</tr><tr><td>Weeks:</td><td>&nbsp;' + summary.Weeks + '</td></tr></table>';
return balloneText;
};
var createStackedChartLabelText = function (item, labelText) {
return item.values.percents > 5 ? labelText : '';
};
var createResourceUsageCharts = function ($this) {
var actualId = getResourceUsageChartBlockId($this, false);
var plannedId = getResourceUsageChartBlockId($this, true);
createResourceUsageChart(actualId, settings.chartModalActualTitle);
createResourceUsageChart(plannedId, settings.chartModalPlannedTitle);
};
var createResourceUsageChart = function (chartId, title) {
var chart = findChart(chartId);
if (chart) {
return;
}
AmCharts.makeChart(chartId, {
"categoryAxis": {
"gridPosition": "start",
"axisAlpha": 0,
"gridAlpha": 0,
"parseDates": true
},
"categoryField": "weekEnding",
"depth3D": 2,
"fontFamily": "Open Sans",
"legend": {
"enabled": true,
},
"theme": "light",
"type": "serial",
"titles": [{
"size": 15,
"text": title
}],
"graphs": [{
"title": "Over Allocation",
"balloonFunction": createResourceUsageChartBalloonText,
"valueField": "overAllocated",
"bullet": "round",
"bulletBorderColor": "#FFFFFF",
"bulletBorderThickness": 1,
"bulletSize": 2,
"lineThickness": 1,
"lineAlpha": 1,
"fillAlphas": 0.2,
"stackable": false,
"lineColor": "red"
}, {
"title": "Optimal Allocation",
"balloonFunction": createResourceUsageChartBalloonText,
"valueField": "idealAllocated",
"bullet": "round",
"bulletBorderColor": "#FFFFFF",
"bulletBorderThickness": 1,
"bulletSize": 2,
"lineThickness": 1,
"lineAlpha": 1,
"fillAlphas": 0.2,
"stackable": false,
"lineColor": "green"
}, {
"title": "Under Allocation",
"balloonFunction": createResourceUsageChartBalloonText,
"valueField": "underAllocated",
"bullet": "round",
"bulletBorderColor": "#FFFFFF",
"bulletBorderThickness": 1,
"bulletSize": 2,
"lineThickness": 1,
"lineAlpha": 1,
"fillAlphas": 0.2,
"stackable": false,
"lineColor": "yellow"
}]
});
};
var createResourceUsageChartBalloonText = function (item, graph) {
var balloneText = Math.abs(item.values.value).formatNumber(false) + " units at " + (new Date(item.category)).toDateString();
return balloneText;
};
var createMarkup = function ($this) {
if (!$this) {
throw '$this does not exist';
}
// if markup is already created
if ($this.data('optimuse')) {
return;
}
var preloader = createPreloader();
var container = $('<div></div>').addClass("container hidden")
.append(createSeparatorBlock())
.append(createTitleBlock())
.append(createMagnifyBlock())
.append(createStackedChartBlock())
.append(createResourceUsageDialog());
$this.data('optimuse', true)
.append(preloader)
.append(container);
createStackedChart($this);
createResourceUsageCharts($this);
return $this;
};
var getStackedChartBlockId = function ($this) {
return $this.find('[data-magnify="stacked"]').attr('id');
};
var getResourceUsageChartBlockId = function ($this, planned) {
return $this.find('[data-chart="' + (planned ? "planned" : "actual") + '"]').attr('id');
};
var getStackedChartDataSource = function (data) {
var data = settings.data || {};
var dataSource = [getStackedChartItem(data.ActualCapacity), getStackedChartItem(data.PlannedCapacity)];
return dataSource;
};
var getStackedChartItem = function (capacity) {
var item = {
planvsact: "",
underallocatedSummary: capacity ? capacity.UnderAllocatedSummary : null,
idealallocatedSummary: capacity ? capacity.IdealAllocatedSummary : null,
overallocatedSummary: capacity ? capacity.OverAllocatedSummary : null,
underallocatedWeeks: (capacity && capacity.UnderAllocatedSummary) ? capacity.UnderAllocatedSummary.Weeks || 0 : 0,
idealallocatedWeeks: (capacity && capacity.IdealAllocatedSummary) ? capacity.IdealAllocatedSummary.Weeks || 0 : 0,
overallocatedWeeks: (capacity && capacity.OverAllocatedSummary) ? capacity.OverAllocatedSummary.Weeks || 0 : 0,
};
return item;
};
var getIdealResourceUsagePercentage = function (capacity) {
if (!capacity)
return 0;
var totalCapacity = capacity.TotalHoursCapacity || 0;
var overAllocated = capacity.OverAllocatedSummary ? capacity.OverAllocatedSummary.TotalHoursValue || 0 : 0;
var underAllocated = capacity.UnderAllocatedSummary ? capacity.UnderAllocatedSummary.TotalHoursValue || 0 : 0;
var notIdealAllocated = overAllocated + underAllocated;
if (totalCapacity == 0) {
return notIdealAllocated == 0 ? 100 : 0;
} else {
var notIdealPercentage = Math.min((notIdealAllocated / totalCapacity), 1);
return Math.round(100 * (1 - notIdealPercentage));
}
};
var getSummaryAllocationPercent = function (capacity, summary) {
var totalCapacity = (capacity || {}).TotalHoursCapacity || 0,
summaryAllocated = (summary || {}).TotalHoursValue || 0;
if (totalCapacity == 0) {
return summaryAllocated == 0 ? 0 : 100;
}
else {
return Math.round((summaryAllocated / totalCapacity) * 100);
}
};
var getResourceUsageChartDataSource = function (planned) {
var dataSource = [];
var weekEndings = settings.data.WeekEndings || [];
if (weekEndings.length <= 0) {
return dataSource;
}
var capacity = (planned ? settings.data.PlannedCapacity : settings.data.ActualCapacity);
if (capacity) {
var overAllocatedSummary = capacity.OverAllocatedSummary || {},
underAllocatedSummary = capacity.UnderAllocatedSummary || {},
idealAllocatedSummary = capacity.IdealAllocatedSummary || {};
var overAllocatedValues = (settings.isUOMHours ? overAllocatedSummary.HoursUsageData : overAllocatedSummary.ResourceUsageData) || {},
underAllocatedValues = (settings.isUOMHours ? underAllocatedSummary.HoursUsageData : underAllocatedSummary.ResourceUsageData) || {},
idealAllocatedValues = (settings.isUOMHours ? idealAllocatedSummary.HoursUsageData : idealAllocatedSummary.ResourceUsageData) || {};
for (var weekIndex = 0; weekIndex < weekEndings.length; weekIndex++) {
var weekEnding = weekEndings[weekIndex];
var dataItem = {
weekEnding: weekEnding,
overAllocated: overAllocatedValues[weekEnding] || 0,
underAllocated: underAllocatedValues[weekEnding] || 0,
idealAllocated: idealAllocatedValues[weekEnding] || 0
};
dataSource.push(dataItem);
}
}
return dataSource;
};
var refreshResourceUsageCharts = function ($this) {
var resourceUsageActualChartId = getResourceUsageChartBlockId($this, false);
var resourceUsagePlannedChartId = getResourceUsageChartBlockId($this, true);
updateChart(resourceUsageActualChartId, getResourceUsageChartDataSource(false));
updateChart(resourceUsagePlannedChartId, getResourceUsageChartDataSource(true));
};
var showResourceUsageDialog = function ($this) {
$this.find('.modal').modal('show');
};
var methods = {
init: function (options) {
settings = $.extend(true, settings, options);
this.each(function () {
var $this = $(this);
createMarkup($this);
});
if (settings.data) {
methods.setData.apply(this, [settings.data]);
}
},
showPreloader: function () {
return this.each(function () {
var $this = $(this);
$this.find('.container').addClass('hidden');
$this.find('.loadRotator').removeClass('hidden');
});
},
hidePreloader: function () {
return this.each(function () {
var $this = $(this);
$this.find('.container').removeClass('hidden');
$this.find('.loadRotator').addClass('hidden');
});
},
mouveBarTitles: function () {
var $this = $(this);
$this.find('.container').find("tspan[x='0']").attr("x", "49").css("font-weight", "bold");;
return null;
},
setData: function (data) {
settings.data = data || settings.data;
return this.each(function () {
var $this = $(this);
if (settings.data) {
var magnifyGlassActualId = $this.find('[data-magnify="actual"]').map(function (index, element) {
return $(element).attr('id');
});
var magnifyGlassPlannedId = $this.find('[data-magnify="planned"]').map(function (index, element) {
return $(element).attr('id');
});
methods.drawMagnifyGlass.apply(this, [magnifyGlassActualId, false]);
methods.drawMagnifyGlass.apply(this, [magnifyGlassPlannedId, true]);
$this.find('div.magnifyContainer').on('click', function () {
showResourceUsageDialog($this);
});
var stackedChartId = getStackedChartBlockId($this);
updateChart(stackedChartId, getStackedChartDataSource());
refreshResourceUsageCharts($this);
}
});
},
changeUOMMode: function (isUOMHours) {
settings.isUOMHours = isUOMHours;
return this.each(function () {
var $this = $(this);
refreshResourceUsageCharts($this);
});
},
drawMagnifyGlass: function (magnifyGlassId, planned) {
var data = settings.data || {};
var capacity = (planned ? data.PlannedCapacity : data.ActualCapacity) || {};
var canvasUniquePostfix = Math.uuid();
var gradient = 'linear-gradient(-135deg, #7030A0 20%, #475FBE 30%, #04ABED 40%, #3CBDAF 70%, #8ED054 80%)';
for (var i = 0; i < magnifyGlassId.length; i++) {
$('#' + magnifyGlassId[i]).find('div.magnifyContainer').remove();
$('#' + magnifyGlassId[i]).bar(getIdealResourceUsagePercentage(capacity) || 0, (planned ? 'planned' : 'actual') + '-canvas-' + canvasUniquePostfix, true, gradient);
}
},
drawResourceUsageGraph: function (chartDivId, planned) {
return $(this).each(function () {
var title = planned ? settings.chartModalPlannedTitle : settings.chartModalActualTitle;
createResourceUsageChart(chartDivId, title);
updateChart(chartDivId, getResourceUsageChartDataSource(planned));
});
},
getOverAllocationPercent: function () {
var data = settings.data || 0,
capacity = data.ActualCapacity || {};
return getSummaryAllocationPercent(capacity, capacity.OverAllocatedSummary);
},
getUnderAllocationPercent: function () {
var data = settings.data || 0,
capacity = data.ActualCapacity || {};
return getSummaryAllocationPercent(capacity, capacity.UnderAllocatedSummary);
},
};
$.fn.optimuse = function (options) {
if (typeof options === 'string' && typeof methods[options] === 'function') {
return methods[options].apply(this, Array.prototype.slice.call(arguments, 1));
} else if (typeof options === 'object' || !options) {
return methods.init.apply(this, arguments);
}
};
}(jQuery));