SmartIT_Extensions/BMC/smart-it-full-helix/scripts/app/chart/chart-controller.js

458 lines
26 KiB
JavaScript

"use strict";
(function () {
'use strict';
angular.module('chartModule')
.controller('ChartController', ['$scope', 'chartModel', 'userModel', '$q', 'i18nService', '$timeout', 'configurationModel', '$filter', 'metadataModel', 'authService', 'roles', '$window', 'searchModel',
function ($scope, chartModel, userModel, $q, i18nService, $timeout, configurationModel, $filter, metadataModel, authService, roles, $window, searchModel) {
var userPreferenceGroup = 'Dashboard', windowResizeHandler;
function init() {
$scope.state = {
incidentDataIsLoading: true,
workorderDataIsLoading: true,
requestDataIsLoading: true,
changeDataIsLoading: true,
tooManyCompanies: false
};
$scope.selections = {
companies: []
};
$scope.incidentMultiBarChartData = [];
$scope.workorderMultiBarChartData = [];
$scope.servicerequestMultiBarChartData = [];
$scope.incidentAreaLineChartData = [];
$scope.workorderAreaLineChartData = [];
$scope.requestAreaLineChartData = [];
$scope.changePieChartData = [];
$scope.changeBarChartData = [];
$scope.supportGroups = [];
$scope.myCompanySelected = true;
$scope.selectedCompanyItem = {};
$scope.myitsmLocale = window.myitsmLocale;
$scope.perspectiveDropdown = {
data: [],
selectedItem: {}
};
var userFullDataPromise = userModel.getFullCurrentUserData().then(function () {
$scope.userData = userModel.userFullData;
});
$scope.isITSMAgent = authService.isAuthorized(roles.ITSM_AGENT_ROLE);
$scope.isSRMInstalled = configurationModel.isServerApplicationEnabled(EntityVO.TYPE_SERVICEREQUEST)
&& authService.isAuthorized(roles.ITSM_AGENT_ROLE);
$scope.isWOInstalled = configurationModel.isServerApplicationEnabled(EntityVO.TYPE_WORKORDER)
&& authService.isAuthorized(roles.ITSM_AGENT_ROLE);
$scope.isChangeInstalled = configurationModel.isServerApplicationEnabled(EntityVO.TYPE_CHANGE)
&& authService.isAuthorized(roles.ITSM_CHANGE_USER_ROLE);
var userCompaniesPromise = searchModel.getOperatingCompanies(null, -1).then(function (response) {
$scope.selections.companies = _.cloneDeep(response.companies);
$scope.state.tooManyCompanies = response.exceedsChunkSize;
});
$scope.getCompaniesByName = function (name) {
return searchModel.getCompaniesByText(name).then(function (response) {
return { list: response.companies, exceedsChunkSize: response.exceedsChunkSize };
});
};
$scope.setCompany = function (company) {
$scope.selectedCompany = { name: company.name };
$scope.selectedCompanyItem = company;
$scope.fetchDataByCompany($scope.selectedCompany);
};
//todo: Viktor: in my opinion local storage is more proper place to save this small data. + It is not critical to have this data over several machines.
var userPreferencesPromise = userModel.getUserPreferences(userPreferenceGroup).then(function (userConfig) {
if (userConfig.length) {
_.forEach(userConfig, function (property) {
chartModel[property.name] = property.value;
});
}
});
var metadataPromise = metadataModel.getMetadataByTypes([EntityVO.TYPE_INCIDENT, EntityVO.TYPE_WORKORDER, EntityVO.TYPE_SERVICEREQUEST]);
$q.all([userFullDataPromise, userCompaniesPromise, userPreferencesPromise, metadataPromise]).then(function () {
$scope.incidentRadioModel = chartModel.incidentBacklogDateRange;
$scope.workorderRadioModel = chartModel.workorderBacklogDateRange;
$scope.requestRadioModel = chartModel.requestBacklogDateRange;
$scope.selectedCompany = chartModel.statsCompany ? chartModel.statsCompany : { name: $scope.userData.company.name };
$scope.selectedCompany = { name: $scope.selectedCompany.name };
$scope.selectedCompanyItem = _.find($scope.selections.companies, $scope.selectedCompany) || $scope.selectedCompany;
populatePerpectiveList($scope.selectedCompany);
if (chartModel.statsPerspective && chartModel.statsPerspective !== 'myGroups' && chartModel.statsPerspective !== 'allGroups'
&& !_.filter($scope.supportGroups, function (item) {
return item.name === chartModel.statsPerspective;
}).length) {
$scope.clearCache();
}
if (chartModel.statsPerspective === 'myGroups' || chartModel.statsPerspective === 'allGroups') {
$timeout(function () {
$scope.perspectiveDropdown.selectedItem = _.find($scope.perspectiveDropdown.data, { name: chartModel.statsPerspective });
}, 500);
if (chartModel.statsPerspective === 'myGroups') {
$scope.filterGroups = $scope.supportGroups.map(function (group) {
return { name: group.name };
});
}
else {
$scope.filterGroups = [];
}
}
else {
$scope.perspectiveDropdown.selectedItem = _.find($scope.perspectiveDropdown.data, { name: chartModel.statsPerspective });
$scope.filterGroups = [{ name: chartModel.statsPerspective }];
}
$scope.fetchData();
});
}
$scope.refreshBacklogData = function (ticketType, days) {
$scope[ticketType + 'RadioModel'] = days;
chartModel[ticketType + 'BacklogDateRange'] = days;
var userPreference = _.find(userModel.userConfig[userPreferenceGroup], { name: ticketType + 'BacklogDateRange' }), userPrefId = userPreference ? userPreference.id : null;
userModel.updateUserPreferences(userPreferenceGroup, {
id: userPrefId,
name: ticketType + 'BacklogDateRange',
value: days
});
$scope.state[ticketType + 'BacklogIsLoading'] = true;
chartModel.fetchData(ticketType, ['area'], [$scope.selectedCompany], $scope.filterGroups, $scope[ticketType + 'RadioModel']).then(function () {
$scope[ticketType + 'AreaLineChartData'] = chartModel[ticketType + 'StatsDataCache'][ticketType + 'AreaLineChartData'];
}).finally(function () {
$scope.state[ticketType + 'BacklogIsLoading'] = false;
});
};
$scope.colorFunction = function () {
return function (d, i) {
var colorArray = ['#f83200', '#EE8F43', '#ECC35B', '#89C341'];
return colorArray[i];
};
};
$scope.xAreaLineChartFunction = function () {
return function (d) {
if (window.isRtl) {
return $filter('date')(d, '\u200F d \u05d1MMM yyyy');
}
return $filter('date')(d);
};
};
$scope.yMultiBarChartFunction = function () {
return function (d) {
return d3.round(d);
};
};
$scope.xchangePieChartFunction = function () {
return function (d) {
return d.label;
};
};
$scope.ychangePieChartFunction = function () {
return function (d) {
return d.value;
};
};
var barColorArray = ['#95DAED', '#f83200', '#95DAED', '#95DAED', '#95DAED', '#95DAED'];
$scope.changeBarColorFunction = function () {
return function (d, i) {
return barColorArray[i];
};
};
var pieColorArray = ['#f83200', '#95DAED'];
$scope.changePieColorFunction = function () {
return function (d, i) {
return pieColorArray[i];
};
};
$scope.clearCache = function () {
chartModel.clearCache(EntityVO.TYPE_INCIDENT);
chartModel.clearCache(EntityVO.TYPE_WORKORDER);
chartModel.clearCache(EntityVO.TYPE_SERVICEREQUEST);
};
$scope.fetchDataByCompany = function (company) {
var userPreference = {}, userPrefId = '', statsUserPrefId = '', updateGroupPref = false;
populatePerpectiveList(company);
if (chartModel.statsPerspective !== 'allGroups') {
$scope.perspectiveDropdown.selectedItem = _.find($scope.perspectiveDropdown.data, { name: 'allGroups' });
$scope.filterGroups = [];
chartModel.statsPerspective = 'allGroups';
userPreference = _.find(userModel.userConfig[userPreferenceGroup], { name: 'statsPerspective' });
statsUserPrefId = userPreference ? userPreference.id : null;
updateGroupPref = true;
}
userPreference = _.find(userModel.userConfig[userPreferenceGroup], { name: 'statsCompany' });
userPrefId = userPreference ? userPreference.id : null;
$scope.selectedCompany = chartModel.statsCompany = angular.copy(company);
userModel.updateUserPreferences(userPreferenceGroup, {
id: userPrefId,
name: 'statsCompany',
value: chartModel.statsCompany
}).finally(function () {
if (updateGroupPref) {
userModel.updateUserPreferences(userPreferenceGroup, {
id: statsUserPrefId,
name: 'statsPerspective',
value: chartModel.statsPerspective
});
}
});
$scope.clearCache();
$scope.fetchData();
};
$scope.fetchDataByGroup = function (group) {
$scope.perspectiveDropdown.selectedItem = group;
if (group && group.static && (group.name === 'myGroups' || group.name === 'allGroups')) {
chartModel.statsPerspective = group.name;
if (group.name === 'myGroups') {
$scope.filterGroups = $scope.supportGroups.map(function (group) {
return { name: group.name };
});
}
else {
$scope.filterGroups = [];
}
}
else {
chartModel.statsPerspective = group.name;
$scope.filterGroups = [{ name: group.name }];
}
var userPreference = _.find(userModel.userConfig[userPreferenceGroup], { name: 'statsPerspective' }), userPrefId = userPreference ? userPreference.id : null;
userModel.updateUserPreferences(userPreferenceGroup, {
id: userPrefId,
name: 'statsPerspective',
value: chartModel.statsPerspective
});
$scope.clearCache();
$scope.fetchData();
};
$scope.fetchData = function () {
$scope.state.incidentDataIsLoading = true;
var incidentChartDataPromise = chartModel.getChartStatisticsData(EntityVO.TYPE_INCIDENT, ['kpi', 'stack', 'area'], [$scope.selectedCompany], $scope.filterGroups, $scope.incidentRadioModel);
incidentChartDataPromise.then(function () {
$scope.incidentKeyPerformanceData = chartModel.incidentStatsDataCache.incidentKeyPerformanceData;
$scope.incidentMultiBarChartData = processMultiBarChartData(EntityVO.TYPE_INCIDENT, chartModel.incidentStatsDataCache.incidentMultiBarChartData, 'urgencies', 'statuses');
$scope.incidentAreaLineChartData = chartModel.incidentStatsDataCache.incidentAreaLineChartData;
}).finally(function () {
$scope.state.incidentDataIsLoading = false;
});
if ($scope.isSRMInstalled) {
$scope.state.requestDataIsLoading = true;
var requestChartDataPromise = chartModel.getChartStatisticsData(EntityVO.TYPE_SERVICEREQUEST, ['kpi', 'stack', 'area'], [$scope.selectedCompany], $scope.filterGroups, $scope.requestRadioModel);
requestChartDataPromise.then(function () {
$scope.requestKeyPerformanceData = chartModel.requestStatsDataCache.requestKeyPerformanceData;
$scope.requestMultiBarChartData = processMultiBarChartData(EntityVO.TYPE_SERVICEREQUEST, chartModel.requestStatsDataCache.requestMultiBarChartData, 'urgencies', 'statuses');
$scope.requestAreaLineChartData = chartModel.requestStatsDataCache.requestAreaLineChartData;
}).finally(function () {
$scope.state.requestDataIsLoading = false;
});
}
if ($scope.isWOInstalled) {
$scope.state.workorderDataIsLoading = true;
var workorderChartDataPromise = chartModel.getChartStatisticsData(EntityVO.TYPE_WORKORDER, ['kpi', 'stack', 'area'], [$scope.selectedCompany], $scope.filterGroups, $scope.workorderRadioModel);
workorderChartDataPromise.then(function () {
$scope.workorderKeyPerformanceData = chartModel.workorderStatsDataCache.workorderKeyPerformanceData;
$scope.workorderMultiBarChartData = processMultiBarChartData(EntityVO.TYPE_WORKORDER, chartModel.workorderStatsDataCache.workorderMultiBarChartData, 'priorities', 'statuses');
$scope.workorderAreaLineChartData = chartModel.workorderStatsDataCache.workorderAreaLineChartData;
}).finally(function () {
$scope.state.workorderDataIsLoading = false;
});
}
if ($scope.isChangeInstalled) {
$scope.state.changeDataIsLoading = true;
var changeDataPromise = chartModel.getChangeData([$scope.selectedCompany], $scope.filterGroups);
changeDataPromise.then(function () {
var barChartData = [
{
key: 'Change Stats',
values: []
}
];
var pieChartData = [];
var criticalVal = 0;
var totalVal = 0;
_.forEach(chartModel.statsConfig, function (stat) {
var dataPoint = [], pieDataPoint;
dataPoint.push($filter('i18n')('chart.change.label.' + stat.label));
dataPoint.push(stat.value);
barChartData[0].values.push(dataPoint);
if (stat.label === 'critical') {
pieDataPoint = {
name: stat.label,
label: $filter('i18n')('chart.changeDonut.label.' + stat.label),
value: stat.value
};
criticalVal = stat.value;
pieChartData.push(pieDataPoint);
}
if (stat.label === 'all') {
pieDataPoint = {
name: stat.label,
label: $filter('i18n')('chart.changeDonut.label.' + stat.label),
value: stat.value
};
totalVal = stat.value;
pieChartData.push(pieDataPoint);
}
});
$scope.changeBarChartData = barChartData;
_.forEach(pieChartData, function (stat) {
if (stat.name === 'all') {
stat.value = parseInt(stat.value) - criticalVal;
}
});
$scope.changePieChartData = pieChartData;
//drawing custom pie chart instead of using the directive provided
//to add custom UI to match the mockup provided (number in the center and legend positioning)
//and handle legend click behavior to enable redraw of custom UI
$scope.drawCustomPieChart(totalVal);
}).finally(function () {
$scope.state.changeDataIsLoading = false;
});
}
$q.all([incidentChartDataPromise, requestChartDataPromise, workorderChartDataPromise, changeDataPromise]).then(function () {
setTimeout(function () {
d3.selectAll("text").attr('tabindex', 0);
}, 500);
}, 0);
$scope.drawCustomPieChart = function (pieAllStatValue) {
nv.addGraph(function () {
var totalValue = pieAllStatValue;
var displayValue = totalValue; //to be displayed in the middle of the donut
var chart = nv.models.pieChart()
.x($scope.xchangePieChartFunction())
.y($scope.ychangePieChartFunction())
.showLegend(true)
.donut(true)
.donutRatio(.42)
.showLabels(false)
.tooltips(false)
.color($scope.changePieColorFunction());
d3.select('#changeDashboardPieChart svg')
.datum($scope.changePieChartData)
.transition().duration(500).call(chart);
function renderCustomPieChartUI() {
var chartContainer = d3.select('#changeDashboardPieChart');
var availableW = chartContainer && chartContainer[0]
&& chartContainer[0][0] && chartContainer[0][0].clientWidth;
var minLeftPos = 160;
if (window.myitsmLocale == 'zh' || window.myitsmLocale == 'ko' || window.myitsmLocale == 'ja') {
minLeftPos = 60;
}
var leftPos = availableW > 440 ? 100 : minLeftPos;
d3.select('#changeDashboardPieChart .nv-pieChart')
.attr('transform', 'translate(-30, 30)');
d3.select('#changeDashboardPieChart .nv-pieChart .nv-legendWrap')
.attr('transform', window.myitsmLocale == 'de' ? 'translate(20, 80)' : 'translate(0, 80)');
var series = d3.selectAll('#changeDashboardPieChart .nv-pieChart .nv-legendWrap .nv-series');
series.each(function (d, i) {
//if(i === 1)
//totalValue = d.value;
var s = d3.select(this);
//set legend left position based on chart container width for responsiveness
s.attr('transform', 'translate(' + leftPos + ', ' + i * 30 + ')');
s.select('text')
.attr('dx', '12');
});
//Append center text showing total items
var pieOuter = d3.select('#changeDashboardPieChart .nv-pie .nv-pie');
var textBoxElements = pieOuter.selectAll('.donut-center-box');
var textBoxExists = textBoxElements && textBoxElements[0]
&& textBoxElements[0].length;
if (textBoxExists) {
textBoxElements.each(function () {
var tb = d3.select(this);
tb.select('text')
.text(displayValue);
});
}
else {
var textG = pieOuter.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'donut-center-box');
textG.append('text')
.attr('dy', '.35em')
.attr('text-anchor', 'middle')
.attr('class', 'donut-center-text')
.text(displayValue);
}
}
function onLegendClick(pie) {
if (!pie.hasOwnProperty('disabled')) { //d3 does not set disabled on the pie data by default but only after first chart.update
pie.disabled = false;
}
//this is before chart.update, hence disabled 'true' actually means user clicked on disabled legend
//thereby activating it
if (pie.disabled) {
displayValue = totalValue; //show 'All' value
}
else if (!pie.disabled && displayValue === pie.value) {
//TODO: update logic if dataset more than 2
displayValue = totalValue;
}
else {
//legend switched off
displayValue = totalValue - pie.value;
}
}
function handleLegendClick() {
chart.legend.dispatch.on('legendClick', onLegendClick);
if (chart.update) {
var originalUpdate = chart.update;
chart.update = function () {
originalUpdate();
handleLegendClick();
renderCustomPieChartUI();
};
}
}
handleLegendClick();
renderCustomPieChartUI();
windowResizeHandler = chart.update;
angular.element($window).on('resize', windowResizeHandler);
});
};
};
function populatePerpectiveList(company) {
$scope.supportGroups = [];
$scope.perspectiveDropdown.data = [];
$scope.myCompanySelected = !!(company.name === $scope.userData.company.name);
var myGroups = {
name: "myGroups",
static: true,
id: "mygroups",
label: $filter("i18n")("chart.perspective.myGroups")
}, allGroups = {
name: "allGroups",
static: true,
id: "allgroups",
label: $filter("i18n")("chart.perspective.allGroups")
};
if ($scope.myCompanySelected) {
$scope.perspectiveDropdown.data.push(myGroups, allGroups);
}
else {
$scope.perspectiveDropdown.data.push(allGroups);
}
_.forEach($scope.userData.supportGroups, function (group) {
if (group.company && group.company.name === company.name) {
$scope.supportGroups.push({ name: group.name });
$scope.perspectiveDropdown.data.push({ name: group.name, label: group.name });
}
});
}
function processMultiBarChartData(ticketType, chartData, key_x, key_y) {
var metadata = metadataModel.cache[ticketType];
_.each(chartData, function (chartData_x) {
var metadata_x = _.find(metadata[key_x], { label: chartData_x.key });
chartData_x.index = metadata_x && metadata_x.index || 0;
_.each(chartData_x.values, function (chartData_y) {
var metaData_y = _.find(metadata[key_y], { label: chartData_y[0] });
chartData_y.index = metaData_y && metaData_y.index || 0;
});
chartData_x.values = _.sortBy(chartData_x.values, 'index');
});
chartData = _.sortBy(chartData, 'index');
return chartData;
}
init();
$scope.$on('$destroy', function () {
if (windowResizeHandler) {
angular.element($window).off('resize', windowResizeHandler);
}
});
}
]);
}());