'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 (even with timeout=0 action will be placed in the queue of browser actions) $timeout(function () { if (initMixForm && typeof initMixForm === 'function') initMixForm(); }); }); } }; }]). 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) { var commonErrorMessage = 'An error occurred while processing your request. Please, try again later.', emptyGuid = '00000000-0000-0000-0000-000000000000'; $scope.data = { Id: null, AvailableTeamsAndViews: [], SelectedTeamsAndViews: [], DisableScreen: false, // SA. ENV-1067 DisableDelete: false, ShowOpen: false, SaveMixHasBeenRequested: false, ActivateMixHasBeenRequested: false, ShowSaveForm: false, IsAvgMode: false, // SA. ENV-1030. Show totals summs as average values DataLoaded: false, // SA. ENV-1067 DataChanged: false // SA. ENV-1153 }; $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) { // SA. ENV-1153 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; // SA. ENV-1153 if (initData) { if (initData.mixId && (initData.mixId.length > 0)) { $scope.data.Id = initData.mixId; } if (initData.showAvgTotals) { // SA. ENV-1030. Totals summs as average $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; } // SA. ENV-1153 $scope.$on('dataChanged', function (event, value) { setDataChanged(value); }); // SA. ENV-1159. Leave teamsAndViewsToSelect = undefined to preserver current filter $scope.$on('changeDisplayView', function (event, newStartDate, newEndDate, teamsAndViewsToSelect, callbackFn) { if (!$scope.canApplyFilter()) return; // Incoming dates are as Text setFilterDates(newStartDate, newEndDate); // SA. ENV-1254 if (teamsAndViewsToSelect && Array.isArray(teamsAndViewsToSelect) && (teamsAndViewsToSelect.length > 0)) { setFilterSelectedTeamsAndViews(teamsAndViewsToSelect); } $timeout(function () { $scope.applyFilter(callbackFn); }); }); $scope.saveMixSuccess = function (data) { if (data && data.Id) { setDataChanged(false); // SA. ENV-1153 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 () { // SA. ENV-1067 $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 () { // SA. ENV-1067 $scope.lockScreen(); if (canSaveMix && typeof canSaveMix === 'function' && !canSaveMix()) { $scope.unlockScreen(); return; } var filterData = getFilter4Saving(); $rootScope.$broadcast("saveChanges", filterData, activateMix); }; // SA. ENV-1138 $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('Oops!', commonErrorMessage); 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('Oops!', commonErrorMessage); 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, 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); }; // SA. ENV-1067 $scope.canSaveMix = function () { return $scope.canApplyFilter() && $scope.data.IsEditable && $scope.data.DataLoaded; }; // SA. ENV-1067 $scope.canActivateMix = function () { return $scope.canSaveMix(); }; // SA. ENV-1143 $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; blockUI(); var mixFilter = getMixFilter(); $rootScope.$broadcast("filterChanged", mixFilter, $scope.data.AvailableTeamsAndViews, callbackFn); $scope.data.DataLoaded = true; // SA. ENV-1067 setDataChanged(true); // SA. ENV-1153 }; 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); unblockUI(); } catch (e) { unblockUI(); showErrorModal('Oops!', commonErrorMessage); } finally { $scope.$broadcast('dataloaded'); } }) .error(function (data, status, headers, config) { unblockUI(); showErrorModal('Oops!', commonErrorMessage); }); } catch (e) { unblockUI(); showErrorModal('Oops!', commonErrorMessage); } } function loadMixData() { $rootScope.$broadcast('loadMix', $scope.data.Id, setMixFilter); } $scope.convertTeam = function () { $scope.$apply(function () { 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: $("#team-container").data("capacityPlanner").getData(), 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 startDate = new Date($scope.data.StartDate); var endDate = new Date($scope.data.EndDate); var filter = { StartDate: Date.UTC(startDate.getFullYear(), startDate.getMonth(), startDate.getDate()), EndDate: Date.UTC(endDate.getFullYear(), endDate.getMonth(), endDate.getDate()), TeamsViews: [] }; filter.TeamsViews = $scope.data.SelectedTeamsAndViews; return filter; } function setMixFilter(data) { // 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 ? $filter('date')(data.StartDate, 'MM/dd/yyyy') : null; $scope.data.EndDate = data.EndDate > 0 ? $filter('date')(data.EndDate, 'MM/dd/yyyy') : 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; } if (data.Filter.Selection) { $scope.data.SelectedTeamsAndViews = selectedTeams; } } var mixSelected = isMixSelected(); $scope.data.DataLoaded = mixSelected; // SA. ENV-1067 $timeout(function () { // SA. ENV-1153. Reset changes flag. Code must be executed last after loading mix data. // Queue its' execution after all watchers setDataChanged(false); }); } function isMixSelected() { return !!$scope.data.Id && $scope.data.Id != emptyGuid; } 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('Oops!', commonErrorMessage); } }) .error(function (data, status, headers, config) { $scope.unlockScreen(); showErrorModal('Oops!', commonErrorMessage); }); } catch (e) { $scope.unlockScreen(); showErrorModal('Oops!', commonErrorMessage); } } // SA. ENV-1153 function setDataChanged(value) { $scope.data.DataChanged = value; 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; } // SA. ENV-1159 function setFilterDates(startDate, endDate) { // Dates are as Text if (startDate && (startDate.length > 0)) { $scope.data.StartDate = startDate; var startDateObj = new Date(startDate); $element.find("#MixStartDate").data('datepicker').setDate(startDateObj); } if (endDate && (endDate.length > 0)) { $scope.data.EndDate = endDate; var endDateObj = new Date(endDate); $element.find("#MixEndDate").data('datepicker').setDate(endDateObj); } } // SA. ENV-1254 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); } }]);