EnVisageOnline/Main/Source/EnVisage/Scripts/Angular/Controllers/MixControllers/mixHeaderController.js

745 lines
25 KiB
JavaScript

'use strict';
app
// form initialization needs to be called after data was loaded and angular updated all inputs with the new values
.directive('initForm', ['$timeout', function ($timeout) {
return {
link: function ($scope, elem, attrs, ctrl) {
$scope.$on('dataloaded', function () {
// need to queue form initialization after DOM rendering.
// We should wait for some time to preserve the correct order of execution of functions:
// page options dropdown items must be copied from template and rendered, before the template get deleted
// from the page. See initMixForm function in index.cshtml and initSwitchers in _mixCalendar.cshtml
$timeout(function () {
if (initMixForm && typeof initMixForm === 'function')
initMixForm();
}, 400);
});
}
};
}])
.directive('selectedDataChanged', ['$timeout', function ($timeout) {
return {
link: function ($scope, element, attrs) {
$scope.$on('selectedTeamsViewsChanged', function () {
$timeout(function () { // You might need this timeout to be sure its run after DOM render.
element.trigger("change");
}, 0, false);
})
}
};
}])
.controller('mixHeaderController', ['$scope', '$rootScope', '$http', '$filter', '$timeout', '$element', function ($scope, $rootScope, $http, $filter, $timeout, $element) {
$scope.data = {
Id: null,
AvailableTeamsAndViews: [],
AvailableCostCenters: [],
AvailableProjectRoles: [],
VisibleProjectRoles: [],
SelectedTeamsAndViews: [],
SelectedCostCenters: [],
SelectedProjectRoles: [],
DisableScreen: false,
DisableDelete: false,
ShowOpen: false,
SaveMixHasBeenRequested: false,
ActivateMixHasBeenRequested: false,
ShowSaveForm: false,
IsAvgMode: false, // Show totals summs as average values
DataLoaded: false,
DataChanged: false,
CanDoResourceRace: false,
CanDoTeamRace:false,
canDoRace: false,
RaceScore: 0
};
$scope.$on('resourceCountChanged', function (event, CanRunResourceRace, canDoRace) {
$scope.data.CanDoResourceRace = CanRunResourceRace;
$scope.data.canDoRace = canDoRace;
});
$scope.$on('SuperECCountChanged', function (event, CanDoTeamRace, canDoRace) {
$scope.data.CanDoTeamRace = CanDoTeamRace;
$scope.data.canDoRace = canDoRace;
});
$scope.$on('raceFinished', function (event, RaceScore) {
$scope.data.RaceScore = RaceScore;
var canvasUniquePostfix = Math.uuid();
var gradient = 'linear-gradient(-135deg, #7030A0 20%, #475FBE 30%, #04ABED 40%, #3CBDAF 70%, #8ED054 80%)';
if ($('#raceScoreMagnify').find('div.magnifyContainer'))
$('#raceScoreMagnify').find('div.magnifyContainer').remove();
$('#raceScoreMagnify').smallerbar(RaceScore || 0, 'race-canvas-' + canvasUniquePostfix, true, gradient,RaceScore);
$('#raceScoreMagnify').css({ 'display': '-webkit-box' });
});
$scope.$watch('data.SelectedMix', function (newValue, oldValue) {
if (oldValue != newValue) {
$scope.data.ShowOpen = !!newValue && newValue != $scope.data.Id;
}
});
$scope.$watch('data.Users', function (newValue, oldValue) {
if ((oldValue != newValue) && ($scope.canClearMix())) {
setDataChanged(true);
}
});
$scope.groupTeamsFn = function (item) {
return item.Group.Name;
};
$scope.init = function (initData) {
$scope.data.Id = null;
$scope.data.DataChanged = false;
if (initData) {
if (initData.mixId && (initData.mixId.length > 0)) {
$scope.data.Id = initData.mixId;
}
if (initData.showAvgTotals) {
$scope.data.IsAvgMode = initData.showAvgTotals;
}
}
if (!isMixSelected()) {
initPageFilter();
}
else {
jQuery(document).ready(function () { loadMixData(); });
}
};
$scope.lockScreen = function () {
$scope.data.DisableScreen = true;
if (!isUIBlocked()) {
blockUI();
}
}
$scope.unlockScreen = function () {
unblockUI();
$scope.data.DisableScreen = false;
}
$scope.$on('dataChanged', function (event, value) {
setDataChanged(value);
});
// Leave teamsAndViewsToSelect = undefined to preserve current filter
$scope.$on('changeDisplayView', function (event, newStartDateMs, newEndDateMs, teamsAndViewsToSelect, callbackFn) {
if (!$scope.canApplyFilter())
return;
// Incoming dates are as Text
setFilterDates(newStartDateMs, newEndDateMs);
if (teamsAndViewsToSelect && Array.isArray(teamsAndViewsToSelect) && (teamsAndViewsToSelect.length > 0)) {
setFilterSelectedTeamsAndViews(teamsAndViewsToSelect);
}
$timeout(function () {
$scope.applyFilter(callbackFn);
});
});
$scope.filterCostCentersChanged = function () {
setProjectRolesVisibility();
var mixFilter = getMixFilter();
$rootScope.$broadcast("clientFilterChanged", mixFilter);
};
$scope.filterProjectRolesChanged = function () {
var mixFilter = getMixFilter();
$rootScope.$broadcast("clientFilterChanged", mixFilter);
};
$scope.saveMixSuccess = function (data) {
if (data && data.Id) {
setDataChanged(false);
goToMix(data.Id);
}
else {
// Some unknown problems with mix saving
showErrorModal('Oops!', 'Unexpected error happend during saving of the Mix. Contact your ' +
'system administrators for details');
$scope.unlockScreen();
}
}
$scope.saveMixFailed = function (errorMessage) {
showErrorModal('Oops!', errorMessage);
$scope.unlockScreen();
}
$scope.saveChanges = function () {
$scope.lockScreen();
if (canSaveMix && typeof canSaveMix === 'function' && !canSaveMix()) {
$scope.unlockScreen();
return;
}
var filterData = getFilter4Saving();
$rootScope.$broadcast("saveChanges", filterData, $scope.saveMixSuccess, $scope.saveMixFailed);
};
$scope.activateMix = function () {
$scope.lockScreen();
if (canSaveMix && typeof canSaveMix === 'function' && !canSaveMix()) {
$scope.unlockScreen();
return;
}
var filterData = getFilter4Saving();
$rootScope.$broadcast("saveChanges", filterData, activateMix, undefined, true);
};
$scope.deleteUnopenedMix = function () {
if (!$scope.data.SelectedMix || ($scope.data.SelectedMix.length < 1))
return;
$scope.data.DisableDelete = true;
var mixId = $scope.data.SelectedMix;
try {
bootbox.confirm("Are you sure you want to delete this mix? This action cannot be undone.",
function (result) {
$scope.$apply(function () {
if (result) {
$rootScope.$broadcast("deleteMix", mixId, onUnopenedMixDeleted, onUnopenedMixDeleteFailed);
} else {
// bootbox is not angular directive so we have to tell angular that scope has been updated
$scope.data.DisableDelete = false;
}
});
});
} catch (e) {
$scope.data.DisableDelete = false;
showErrorModal();
unblockUI();
}
};
function onUnopenedMixDeleted() {
var mixId = $scope.data.SelectedMix;
// Remove mix from the AvailableMixes collection
for (var index = $scope.data.AvailableMixes.length - 1; index >= 0; index--) {
if ($scope.data.AvailableMixes[index].Value == mixId) {
$scope.data.AvailableMixes.splice(index, 1);
break;
}
}
$scope.data.DisableDelete = false;
$scope.data.SelectedMix = undefined;
// SA. We need to reset selected Mix. To reset select2 value we should find it in the DOM tree and set
// it's value using jQuery. Angular does all the work, but it can't reset displayed selection in select2,
// because select2 is a JS-control. So it can't be fully managed with the angular digest model
$element.find("*[ng-model='data.SelectedMix']").select2("val", "");
};
function onUnopenedMixDeleteFailed() {
$scope.data.DisableDelete = false;
};
$scope.deleteCurrentMix = function () {
$scope.data.DisableDelete = true;
try {
bootbox.confirm("Are you sure you want to delete this mix? This action cannot be undone.", function (result) {
$scope.$apply(function () {
if (result) {
setDataChanged(false);
$rootScope.$broadcast("deleteMix", $scope.data.Id, goToMix, onDeleteMixFailure);
} else {
// bootbox is not angular directive so we have to tell angular that scope has been updated
$scope.data.DisableDelete = false;
}
});
});
} catch (e) {
$scope.data.DisableDelete = false;
showErrorModal();
unblockUI();
}
};
function onDeleteMixFailure() {
$scope.data.DisableDelete = false;
}
function getFilter4Saving() {
var mixFilter = getMixFilter();
return {
Id: $scope.data.Id,
Name: $scope.data.Name,
StartDate: mixFilter.StartDate,
EndDate: mixFilter.EndDate,
Users: $scope.data.Users,
Filter: {
Selection: {
TeamsViews: mixFilter.TeamsViews,
AvailableTeams: mixFilter.AvailableTeams,
CostCenters: mixFilter.CostCenters,
ProjectRoles: mixFilter.ProjectRoles,
StartDate: mixFilter.StartDate,
EndDate: mixFilter.EndDate
},
Variants: {
AvailableTeamsAndViews: $scope.data.AvailableTeamsAndViews
}
}
};
};
$scope.navigateToMix = function () {
if (!$scope.data.SelectedMix)
return;
goToMix($scope.data.SelectedMix);
};
$scope.navigateToNewMix = function () {
goToMix();
};
$scope.canApplyFilter = function () {
return (($scope.data.SelectedTeamsAndViews || []).length > 0 &&
($scope.data.StartDate || null) != null &&
($scope.data.EndDate || null) != null);
};
$scope.canDoRaceRes = function () {
if ($scope.canApplyFilter() && $scope.data.IsEditable && $scope.data.DataLoaded && $scope.data.CanDoResourceRace && !$scope.data.DataChanged && $scope.data.canDoRace && $scope.data.SelectedTeamsAndViews.length == 1) {
return true;
}
return false;
};
$scope.canDoRaceTeam = function () {
return $scope.canApplyFilter() && $scope.data.IsEditable && $scope.data.DataLoaded && !$scope.data.DataChanged && $scope.data.CanDoTeamRace && $scope.data.canDoRace && $scope.data.SelectedTeamsAndViews.length == 1;
};
$scope.canSaveMix = function () {
return $scope.canApplyFilter() && $scope.data.IsEditable && $scope.data.DataLoaded;
};
$scope.canActivateMix = function () {
return $scope.canSaveMix();
};
$scope.canDeleteMix = function () {
return $scope.data.IsEditable && $scope.data.DataLoaded && isMixSelected();
};
$scope.canClearMix = function () {
return $scope.data.DataLoaded;
};
$scope.applyFilter = function (callbackFn) {
if ($.isFunction(isValidHeader) && !isValidHeader())
return;
setDataChanged(false);
blockUI();
var internalCallBackFn = function (data, newChangeFlagValue) {
if (callbackFn && (typeof callbackFn === 'function')) {
callbackFn(data, newChangeFlagValue);
}
else {
setMixFilter(data, newChangeFlagValue);
}
$scope.data.DataLoaded = true;
};
if (!callbackFn || (! typeof callbackFn !== 'function')) {
callbackFn = setMixFilter;
}
var mixFilter = getMixFilter();
$rootScope.$broadcast("filterChanged", mixFilter, $scope.data.AvailableTeamsAndViews, internalCallBackFn);
setDataChanged(true);
};
$scope.applyRaceRes = function (callbackFn, teamlevel) {
if ($.isFunction(isValidHeader) && !isValidHeader())
return;
setDataChanged(false);
blockUI();
var mixFilter = getMixFilter();
var teamLevel = false;
$rootScope.$broadcast("raceChanged", mixFilter, $scope.data.AvailableTeamsAndViews, teamLevel);
$scope.data.DataLoaded = true;
setDataChanged(true);
};
$scope.applyRaceTeam = function (callbackFn, teamlevel) {
if ($.isFunction(isValidHeader) && !isValidHeader())
return;
setDataChanged(false);
blockUI();
var mixFilter = getMixFilter();
var teamLevel = true;
$rootScope.$broadcast("raceChanged", mixFilter, $scope.data.AvailableTeamsAndViews, teamLevel);
$scope.data.DataLoaded = true;
setDataChanged(true);
};
$scope.applyRace2Team = function (callbackFn) {
if ($.isFunction(isValidHeader) && !isValidHeader())
return;
setDataChanged(false);
blockUI();
var mixFilter = getMixFilter();
var teamLevel = true;
$rootScope.$broadcast("race2Changed", mixFilter, $scope.data.AvailableTeamsAndViews, teamLevel);
$scope.data.DataLoaded = true;
setDataChanged(true);
};
$scope.applyRace2Res = function (callbackFn) {
if ($.isFunction(isValidHeader) && !isValidHeader())
return;
setDataChanged(false);
blockUI();
var mixFilter = getMixFilter();
var teamLevel = false;
$rootScope.$broadcast("race2Changed", mixFilter, $scope.data.AvailableTeamsAndViews, teamLevel);
$scope.data.DataLoaded = true;
setDataChanged(true);
};
function goToMix(mixId) {
if (!mixId)
window.location.href = '/Mix';
else
window.location.href = '/Mix?id=' + mixId;
}
function initPageFilter() {
blockUI();
var request = getAntiXSRFRequest('/Mix/GetPageFilter', {});
try {
$http(request)
.success(function (data, status, headers, config) {
try {
if (!data) {
unblockUI();
return;
}
setMixFilter(data, false);
unblockUI();
} catch (e) {
unblockUI();
showErrorModal();
} finally {
$scope.$broadcast('dataloaded');
}
})
.error(function (data, status, headers, config) {
unblockUI();
showErrorModal();
});
} catch (e) {
unblockUI();
showErrorModal();
}
}
function loadMixData() {
$rootScope.$broadcast('loadMix', $scope.data.Id, setMixFilter);
}
$scope.convertTeam = function () {
$scope.$apply(function () {
var data = $("#team-container").data("capacityPlanner").getData();
for (var j = 0; j < data.length; j++) {
var proj = data[j];
for (var k = 0; k < proj.Positions.length; k++) {
proj.Positions[k].StartDate = DateTimeConverter.jsonDateToMs(proj.Positions[k].StartDate);
proj.Positions[k].EndDate = DateTimeConverter.jsonDateToMs(proj.Positions[k].EndDate);
}
data[j] = proj;
}
var newTeam = {
Id: $('form#editTeamForm #Id').val(),
TVName: $('form#editTeamForm #Name').val(),
CompanyId: $('form#editTeamForm #CompanyId').val(),
CostCenterId: $('form#editTeamForm #CostCenterId').val(),
ReportToId: $('form#editTeamForm #ReportToId').val(),
UserId: $('form#editTeamForm #UserId').select2('val'),
CapacityTeamId: $('form#editTeamForm #CapacityTeamId').val(),
CopyPlanned: $('form#editTeamForm #CopyPlanned').val(),
IsNew: true,
Data: data,
Group: {
Disabled: false,
Name: 'Teams'
}
};
for (var i = 0; i < $scope.data.AvailableTeamsAndViews.length; i++) {
if ($scope.data.AvailableTeamsAndViews[i].Id == newTeam.Id)
return;
}
$scope.data.AvailableTeamsAndViews.push(newTeam);
$scope.data.SelectedTeamsAndViews.push(newTeam);
if ($scope.canApplyFilter())
$scope.applyFilter();
});
$scope.$broadcast('selectedTeamsViewsChanged');
};
$scope.requestMixSaving = function () {
$scope.data.ShowSaveForm = true;
$scope.data.SaveMixHasBeenRequested = true;
$scope.data.ActivateMixHasBeenRequested = false;
};
$scope.requestMixActivation = function () {
$scope.data.ShowSaveForm = !isMixSelected();
$scope.data.SaveMixHasBeenRequested = false;
$scope.data.ActivateMixHasBeenRequested = true;
};
function getMixFilter() {
var startDateMs = DateTimeConverter.stringToMs($scope.data.StartDate);
var endDateMs = DateTimeConverter.stringToMs($scope.data.EndDate);
var filter = {
StartDate: startDateMs,
EndDate: endDateMs,
TeamsViews: $scope.data.SelectedTeamsAndViews,
CostCenters: $scope.data.SelectedCostCenters,
ProjectRoles: $scope.data.SelectedProjectRoles
};
return filter;
}
function setMixFilter(data, setMixChangeFlag) {
// prefill dropdown with team and view values
var selectedTeams = [];
$.each(data.Filter.Selection.TeamsViews || [], function (index, team) {
var teamId = team.Id;
var found = false;
for (var i = 0; i < data.Filter.Variants.AvailableTeamsAndViews.length; i++) {
if (data.Filter.Variants.AvailableTeamsAndViews[i].Id == teamId) {
selectedTeams.push(data.Filter.Variants.AvailableTeamsAndViews[i]);
found = true;
break;
}
}
if (!found && team.IsNew) {
data.Filter.Variants.AvailableTeamsAndViews.push(team);
selectedTeams.push(team);
}
});
$scope.data.Id = data.Id;
$scope.data.Name = data.Name;
$scope.data.StartDate = data.StartDate > 0 ? DateTimeConverter.msFormatAsUtcString(data.StartDate) : null;
$scope.data.EndDate = data.EndDate > 0 ? DateTimeConverter.msFormatAsUtcString(data.EndDate) : null;
$scope.data.Users = data.Users;
$scope.data.SelectedMix = data.Id;
if (data.Filter) {
if (data.Filter.Variants) {
$scope.data.AvailableTeamsAndViews = data.Filter.Variants.AvailableTeamsAndViews;
$scope.data.AvailableUsers = data.Filter.Variants.AvailableUsers;
$scope.data.AvailableMixes = data.Filter.Variants.AvailableMixes;
$scope.data.IsEditable = data.Filter.Variants.IsEditable;
// Client-side filters
$scope.data.AvailableCostCenters = data.Filter.Variants.AvailableCostCenters;
$scope.data.AvailableProjectRoles = data.Filter.Variants.AvailableProjectRoles;
}
if (data.Filter.Selection) {
$scope.data.SelectedTeamsAndViews = selectedTeams;
// Restore selection for client-side filters
setFilterCostCenters(data.Filter.Selection.CostCenters);
setFilterProjectRoles(data.Filter.Selection.ProjectRoles);
}
}
if (isMixSelected()) {
$scope.data.DataLoaded = true;
}
$timeout(function () {
// Reset changes flag. Code must be executed last after loading mix data.
// Queue its' execution after all watchers
setDataChanged(setMixChangeFlag);
});
}
function isMixSelected() {
return !!$scope.data.Id && $scope.data.Id != C_EMPTY_GUID;
}
function activateMix(data) {
$scope.lockScreen();
if (!data || !data.Id) {
$scope.unlockScreen();
return;
}
var mixId = data.Id;
var request = getAntiXSRFRequest('/Mix/ActivateMix', mixId);
try {
$http(request)
.success(function (data, status, headers, config) {
try {
$scope.unlockScreen();
bootbox.alert('Mix have been activated successfully', function () {
setDataChanged(false);
goToMix(mixId);
});
} catch (e) {
$scope.unlockScreen();
showErrorModal();
}
})
.error(function (data, status, headers, config) {
$scope.unlockScreen();
showErrorModal();
});
} catch (e) {
$scope.unlockScreen();
showErrorModal();
}
}
function setDataChanged(value) {
$scope.data.DataChanged = (value === true);
if (value) {
addPageLeaveHandler();
}
else {
removePageLeaveHandler();
}
}
function addPageLeaveHandler() {
window.onbeforeunload = function (e) {
var message = "Mix contains unsaved changes. Are you sure you want to leave the page?",
e = e || window.event;
// For IE and Firefox
if (e) {
e.returnValue = message;
}
// For Safari
return message;
};
}
function removePageLeaveHandler() {
window.onbeforeunload = null;
}
function setFilterDates(startDateMs, endDateMs) {
// Dates are as Text
if (angular.isNumber(startDateMs) && (Number(startDateMs) > 0)) {
$scope.data.StartDate = DateTimeConverter.msFormatAsUtcString(startDateMs);
var startDateObj = DateTimeConverter.msToUtcDate(startDateMs);
$element.find("#MixStartDate").data('datepicker').setDate(startDateObj);
}
if (angular.isNumber(endDateMs) && (Number(endDateMs) > 0)) {
$scope.data.EndDate = DateTimeConverter.msFormatAsUtcString(endDateMs);
var endDateObj = DateTimeConverter.msToUtcDate(endDateMs);
$element.find("#MixEndDate").data('datepicker').setDate(endDateObj);
}
}
function setFilterSelectedTeamsAndViews(teamsAndViews) {
if (!teamsAndViews || !Array.isArray(teamsAndViews) || (teamsAndViews.length < 1))
return;
var visualSelection = [];
var selectedTeams = $.grep($scope.data.AvailableTeamsAndViews, function (teamViewItem, index) {
var found = teamsAndViews.indexOf(teamViewItem.Id) >= 0;
if (found) {
var sortedIndex = $("select#selectedTeamViews").find("option:contains('" + teamViewItem.TVName + "')").val();
if (sortedIndex !== undefined)
visualSelection.push(sortedIndex);
}
return found;
});
$scope.data.SelectedTeamsAndViews = selectedTeams;
$('#selectedTeamViews').select2('val', visualSelection);
}
function setFilterCostCenters(costCentersToSelect) {
$scope.data.SelectedCostCenters = [];
if (!$scope.data.AvailableCostCenters || !angular.isArray($scope.data.AvailableCostCenters))
return;
var availableCostCenters = {};
for (var index = 0; index < $scope.data.AvailableCostCenters.length; index++) {
availableCostCenters[$scope.data.AvailableCostCenters[index].Value] = true;
}
if (costCentersToSelect && angular.isArray(costCentersToSelect)) {
for (var index = 0; index < costCentersToSelect.length; index++) {
var costCenterId = costCentersToSelect[index];
if (costCenterId in availableCostCenters) {
$scope.data.SelectedCostCenters.push(costCenterId);
}
}
}
};
function setFilterProjectRoles(projectRolesToSelect) {
$scope.data.SelectedProjectRoles = projectRolesToSelect;
setProjectRolesVisibility();
};
function getSelect2NewSelection(controlSelection, controlOptions, valuePropertyName) {
var result = null;
if (controlSelection && angular.isArray(controlSelection)) {
var newSelection = [];
if (controlOptions && angular.isArray(controlOptions)) {
var selectedItemsCount = controlSelection.length;
var optionsCount = controlOptions.length;
for (var sIndex = 0; sIndex < selectedItemsCount; sIndex++) {
var itemToCheck = controlSelection[sIndex];
var found = false;
for (var pIndex = 0; pIndex < optionsCount; pIndex++) {
if (controlOptions[pIndex][valuePropertyName] == itemToCheck) {
found = true;
break;
}
}
if (found) {
newSelection.push(itemToCheck);
}
}
}
result = newSelection;
}
return result;
};
function setProjectRolesVisibility() {
if (!$scope.data.AvailableProjectRoles || !angular.isArray($scope.data.AvailableProjectRoles))
// No project roles available for selection
return;
// Fill control with related to selected cost centers options
var expCatsCount = $scope.data.AvailableProjectRoles.length;
var anyCostCenterSelected = $scope.data.SelectedCostCenters && angular.isArray($scope.data.SelectedCostCenters) && ($scope.data.SelectedCostCenters.length > 0);
$scope.data.VisibleProjectRoles = [];
for (var eIndex = 0; eIndex < expCatsCount; eIndex++) {
var expCat = $scope.data.AvailableProjectRoles[eIndex];
if (expCat && expCat.Value && expCat.CostCenterId) {
if (!anyCostCenterSelected || ($scope.data.SelectedCostCenters.indexOf(expCat.CostCenterId) >= 0)) {
$scope.data.VisibleProjectRoles.push(expCat);
}
}
}
// Fix current control selection
var newSelectedExpCats = getSelect2NewSelection($scope.data.SelectedProjectRoles, $scope.data.VisibleProjectRoles, 'Value');
$scope.data.SelectedProjectRoles = newSelectedExpCats;
}
}]);