EnVisageOnline/Main/Source/EnVisage/Views/PeopleResource/_scheduleNonProjectTime.cshtml

777 lines
35 KiB
Plaintext

@using EnVisage.Code
@using EnVisage.Models
@using System.Linq
@using EnVisage.Code.Extensions
@model NonProjectTimeModel
@{
string userId = User.Identity.GetID();
var allocCats = string.Empty;
foreach (var item in Utils.GetNonProjectTimeCategories())
{
if (!string.IsNullOrEmpty(allocCats))
{
allocCats += ",";
}
allocCats += "{ id: '" + item.Value + "', text: '" + item.Text + "'}";
}
var distribution = string.Empty;
if (Model != null && Model.NonProjectTimeAllocations != null)
{
var distributionModel = Model.NonProjectTimeAllocations
.ToDictionary(x => Utils.ConvertToUnixDate(x.WeekEndingDate), x => new
{
x.HoursOff,
StartDate = Utils.ConvertToUnixDate(x.WeekStartDate),
EndDate = Utils.ConvertToUnixDate(x.WeekEndingDate)
});
distribution = Newtonsoft.Json.JsonConvert.SerializeObject(distributionModel);
}
// Get teams list for display in assignment picker
var teamOptsForSelect2 = Utils.GetTeams(userId, true);
// Get resources list for display in assignment picker
DateTime dp = Model.Id.Equals(Guid.Empty) ? DateTime.UtcNow.Date : Model.NonProjectTimeStartDate.ToUniversalTime().Date;
List<Guid> teamsToBrowse =
teamOptsForSelect2.Where(x => !String.IsNullOrEmpty(x.Value) && (x.IsAccessible || x.Value == Model.TeamId.ToString())).Select(x => new Guid(x.Value)).ToList();
// Format SelectAll option for teams list
var selectAllOption = teamOptsForSelect2.FirstOrDefault(x => String.IsNullOrEmpty(x.Value));
if (selectAllOption != null)
{
selectAllOption.Value = Guid.Empty.ToString();
selectAllOption.Text = "Select All";
selectAllOption.IsAccessible = true;
}
var resourceOptsForSelect2 = Utils.GetResourcesByTeams(teamsToBrowse, dp, Model.TeamId);
if (!SecurityManager.CheckSecurityObjectPermission(Areas.AccessToOtherResources, AccessLevel.Read))
{
resourceOptsForSelect2 =
from optGroup in resourceOptsForSelect2
where optGroup.children.Any(x => Guid.Equals(new Guid(x.id), ViewBag.CurrentResourceId))
let children = optGroup.children.Where(x => Guid.Equals(new Guid(x.id), ViewBag.CurrentResourceId)).ToList()
select new Utils.Select2DataOptionGroup
{
id = optGroup.id,
text = optGroup.text,
children = children
};
}
}
<script type="text/javascript">
var isNew = @(Model.Id.Equals(Guid.Empty) ? "true" : "false");
var jsonDistribution = '@Html.Raw(distribution)';
var currentDistribution;
var nptAllocations = jsonDistribution ? JSON.parse(jsonDistribution) : {};
var C_EMPTY_GUID_VALUE = '@(Guid.Empty.ToString())';
var _stDataChanged = false;
function onSTDataChanged() {
_stDataChanged = true;
}
function resetSTDataChanged() {
_stDataChanged = false;
}
function isSTDataChanged() {
return _stDataChanged;
}
function getResourceOptions() {
var resourcesOpts = [];
@foreach (var optGroup in resourceOptsForSelect2)
{
// Creates server resource options on client side
<text>
resourcesOpts.push({
text: "@optGroup.text",
children: [
</text>
foreach(var optItem in optGroup.children)
{
<text>
{
id: "@optItem.id",
text: '@Html.Raw(optItem.text.Replace("'", "\\'"))'
},
</text>
}
<text>
]
});
</text>
}
return resourcesOpts;
}
function getTeamOptions() {
var opts = [];
@foreach (var optItem in teamOptsForSelect2)
{
// Creates server team options on client side
if (optItem.IsAccessible || optItem.Value == Model.TeamId.ToString())
{
<text>
opts.push({
id: "@optItem.Value",
text: '@Html.Raw(optItem.Text.Replace("'", "\\'"))'
});
</text>
}
}
return opts;
}
function initNonProjectTimes(needToRecalculate, needToApplyAllocations) {
// Init Resources picker control.
// SA: Resource picker is based on Input control, because in other case (of select or listbox)
// Select2 stores selected items in order, which is different of the displayed to user one.
// As Select2 is based on input control, we should specify options for display via select2 initialization
var resourceOpts = getResourceOptions();
$("#@Html.IdFor(model => model.ResourcesAsText)").select2({
placeholder: "Select Resources",
allowClear: true,
multiple: true,
data: resourceOpts
}).on('change', function() {
fillNonProjectTimeWeeksGrid(true);
});
var teamOpts = getTeamOptions();
$("#@Html.IdFor(model => model.TeamsAsText)").select2({
placeholder: "Select Teams",
allowClear: true,
multiple: true,
data: teamOpts,
formatResult: function (item, container, query) {
var result = $("<span>" + item.text + "</span>")
if (item.id == C_EMPTY_GUID_VALUE) {
$(result).attr("class", "select2-group-option");
$(container).css("border-bottom", "2px solid #ccc");
}
return result;
}
})
.on("change", function (e) {
if (e && e.val && e.val.indexOf(C_EMPTY_GUID_VALUE) >= 0) {
// Perform selection of all elements
var newSelection = [];
var options = $(e.target).data("select2").opts.data;
if (options) {
for (var index = 1; index < options.length; index++) {
newSelection.push(options[index].id);
}
}
$(e.currentTarget).select2('val', newSelection);
}
fillNonProjectTimeWeeksGrid(true);
});
if("@Model.IsHistory" == "True"){
$("#btnSaveNPTime").css("display", "none");
$("#@Html.IdFor(model=> model.NonProjectTimeName)").prop("disabled", "disabled");
$("#@Html.IdFor(model=> model.Resources)").prop("disabled", "disabled");
$("#@Html.IdFor(model=> model.NonProjectTimeCategoryId)").prop("disabled", "disabled");
$("#@Html.IdFor(model=> model.NonProjectTimeStartDate)").prop("disabled", "disabled");
$("#@Html.IdFor(model=> model.NonProjectTimeEndDate)").prop("disabled", "disabled");
$("#@Html.IdFor(model=> model.NonProjectTimeDuration)").prop("disabled", "disabled");
$("#@Html.IdFor(model=> model.NonProjectTimeCost)").prop("disabled", "disabled");
$("#@Html.IdFor(model=> model.Details)").prop("disabled", "disabled");
$("#@Html.IdFor(model=> model.IsTeamAssignmentMode)").prop("disabled", "disabled");
$("#@Html.IdFor(model=> model.Permanent)").prop("disabled", "disabled");
$("#@Html.IdFor(model=> model.IsPercentsMode)").prop("disabled", "disabled");
$("#@Html.IdFor(model=> model.ResourcesAsText)").prop("disabled", "disabled");
}
$('#npTimeForm input.select2-default').css('width', '125px');
$("#@Html.IdFor(model=>model.NonProjectTimeCategoryId)").select2({
@if (SecurityManager.CheckSecurityObjectPermission(Areas.RD_ResourceNPTAllocationCategory, AccessLevel.Write))
{
<text>
createSearchChoice: function(term, data) {
if ($(data).filter(function() { return this.text.localeCompare(term) === 0; }).length === 0) {
return { id: (term == '') ? null : C_EMPTY_GUID_VALUE, text: term };
}
},
</text>
}
multiple: false,
data: [@Html.Raw(allocCats)]
});
fillNonProjectTimeWeeksGrid(needToRecalculate, needToApplyAllocations);
updateSliderTitles();
$('#btnSaveNPTime').click(function () {
var data = $("#@Html.IdFor(model=>model.NonProjectTimeCategoryId)").select2('data');
$("#@Html.IdFor(model=>model.NonProjectTimeCategoryName)").val(data == null? "" : data.text);
$.validator.unobtrusive.parseDynamicContent('#npTimeForm');
});
$('#npTimeForm').find('input[type=checkbox],input[type=text],select,textarea')
.not('#@Html.IdFor(x=> x.IsPercentsMode)')
.on("change", function () {
if (typeof onSTDataChanged === 'function')
onSTDataChanged();
});
$('#@Html.IdFor(x=> x.Permanent)').switcher({
on_state_content: 'Permanent',
off_state_content: 'One Time'
}).on('change', function(){
var isPermanent = $(this).is(':checked');
$('#bs-datepicker-np-time-range').datePickerRange('setState', !isPermanent);
if (!isPermanent) {
var startDate = $('#@Html.IdFor(x=> x.NonProjectTimeStartDate)').datepicker('getDate');
if (startDate && (startDate.getTime() || 0) > 0) {
$('#@Html.IdFor(x=> x.NonProjectTimeEndDate)').datepicker('setDate', addDaysToDate(startDate, 14));
}
}
$('#effective-date-container').toggleClass('hidden');
}).parent().css("width", "100px");
if(("@Model.IsHistory" == "True"))
$("#@Html.IdFor(x => x.Permanent)").prop("disabled", "disabled");
$('#@Html.IdFor(x=> x.IsPercentsMode)').switcher({
on_state_content: 'Percents',
off_state_content: 'Hours'
}).parent().css("width", "100px");
$('#@Html.IdFor(x=> x.IsPercentsMode)').on('change', function () {
fillNonProjectTimeWeeksGrid(false);
$('#hours-mode-message').toggleClass('hidden');
});
$('#@Html.IdFor(x=> x.IsTeamAssignmentMode)').switcher({
on_state_content: 'Teams',
off_state_content: 'Resources'
}).parent().css("width", "100px");
$('#@Html.IdFor(x=> x.IsTeamAssignmentMode)').on('change', function () {
setAssignmentBlockState();
});
setAssignmentBlockState();
$.validator.unobtrusive.parseDynamicContent('form#npTimeForm');
var options2 = {
orientation: $('body').hasClass('right-to-left') ? "auto right" : 'auto auto',
startDate: '@Constants.MIN_SELECTABLE_DATE',
endDate: '@Constants.MAX_SELECTABLE_DATE'
};
var pickerRangeSettings = {
datePickerOptions: options2,
multiple: !$('#@Html.IdFor(x=> x.Permanent)').is(':checked')
};
$('#bs-datepicker-np-time-range').datePickerRange(pickerRangeSettings).on('changeDate', function (evt) {
var _sDate = new Date($('#@Html.IdFor(model => model.NonProjectTimeStartDate)').val());
if (_sDate.getTime()){
$('#@Html.IdFor(model => model.NonProjectTimeEndDate)').data('datepicker').setStartDate(_sDate);
var effectiveDateOfChange = $('#@Html.IdFor(model => model.EffectiveDateOfChange)').data('datepicker');
if (effectiveDateOfChange){
var isPermanent = $('#@Html.IdFor(x=> x.Permanent)').is(':checked');
if (isPermanent){
var effectiveDateOfChangeValue = effectiveDateOfChange.getDate();
effectiveDateOfChange.setStartDate(_sDate);
if (effectiveDateOfChangeValue && effectiveDateOfChangeValue.getTime() && effectiveDateOfChangeValue < _sDate)
effectiveDateOfChange.setDate(_sDate);
}
}
}
});
$('#@Html.IdFor(x=> x.EffectiveDateOfChange)').datepicker($.extend(true, options2, {startDate: '@Model.NonProjectTimeStartDate.ToShortDateString()'}));
$('#@Html.IdFor(model => model.NonProjectTimeStartDate)').data('prev-value', getLocal(@Utils.ConvertToUnixDate(Model.NonProjectTimeStartDate))).change(onDateChange);
$('#@Html.IdFor(model => model.NonProjectTimeEndDate)').data('prev-value', getLocal(@(Model.NonProjectTimeEndDate.HasValue ? Utils.ConvertToUnixDate(Model.NonProjectTimeEndDate.Value) : (long?)null))).change(onDateChange);
$('#@Html.IdFor(model => model.NonProjectTimeEndDate)').data('datepicker').setStartDate((new Date('@Model.NonProjectTimeStartDate.ToString("yyyy-MM-dd")')));
resetSTDataChanged();
}
function setAssignmentBlockState() {
var isTeamMode = $('#@Html.IdFor(x=> x.IsTeamAssignmentMode)').is(':checked');
if (isTeamMode) {
$("#nptResourcesBlock").hide();
$("#nptTeamsBlock").show();
}
else {
$("#nptTeamsBlock").hide();
$("#nptResourcesBlock").show();
}
}
function onDateChange(ctrl, e) {
var startDateCtrl = $('#@Html.IdFor(model => model.NonProjectTimeStartDate)'),
endDateCtrl = $('#@Html.IdFor(model => model.NonProjectTimeEndDate)');
var startDate = new Date(startDateCtrl.val()),
prevStartDate = new Date(startDateCtrl.data('prev-value')),
endDate = new Date(endDateCtrl.val()),
prevEndDate = new Date(endDateCtrl.data('prev-value'));
if ((startDate.getTime() || 0) == (prevStartDate.getTime() || 0) &&
(endDate.getTime() || 0) == (prevEndDate.getTime() || 0)) {
return;
}
startDateCtrl.data('prev-value', startDate.getTime());
endDateCtrl.data('prev-value', endDate.getTime());
refreshDuration();
fillNonProjectTimeWeeksGrid(true);
}
function refreshDuration() {
var isPermanent = $('#@Html.IdFor(x=> x.Permanent)').is(':checked');
if (isPermanent) {
$('#@Html.IdFor(model => model.NonProjectTimeDuration)').val('Permanent');
}
else {
var startDateCtrl = $('#@Html.IdFor(model => model.NonProjectTimeStartDate)'),
endDateCtrl = $('#@Html.IdFor(model => model.NonProjectTimeEndDate)');
$('#@Html.IdFor(model => model.NonProjectTimeDuration)').val(formatDateRange(startDateCtrl.val(), endDateCtrl.val(), true));
}
};
function onDependentChange(event, ui) {
timeOffChanged(this, parseInt(ui.value));
};
function hoursChanged(control){
var hours = parseInt($(control).val()) || 0;
timeOffChanged(control, hours);
};
function timeOffChanged(control, value){
var weekEnding = new Date($(control).parent("td").children(".weekending-hidden").val()),
weekEndingUtc = weekEnding.getTime();
updateDistributionValue(weekEndingUtc, value);
$(control).children(".sliderValue").val(value);
var percentVal = currentDistribution[weekEndingUtc].PercentOff;
$(control).children(".sliderTitle").html(percentVal + "%");
onSTDataChanged();
};
function updateSliderTitles() {
$(".slider-container").each(function (i, e) {
var weekEnding = new Date($(e).parent("td").children(".weekending-hidden").val()),
weekEndingUtc = weekEnding.getTime();
var percentVal = currentDistribution[weekEndingUtc].PercentOff;
$(e).children('.sliderTitle').html(percentVal + "%");
});
}
function onFailure(xhr) {
showErrorModal();
}
function onSuccess(result) {
handleAjaxResponse(result, function() {
onSuccessCallback(result.Content);
}, null, null, $('#npTimeForm'));
}
function onSuccessCallback(data) {
var nptCompletlyBeforeResourceStartDate = @((int)NonProjectTimeDatesCheckResultModel.ViolationType.NptCompletlyBeforeResourceStartDate);
var nptCompletlyAfterResourceEndDate = @((int)NonProjectTimeDatesCheckResultModel.ViolationType.NptCompletlyAfterResourceEndDate);
var nptPartiallyBeforeResourceStartDate = @((int)NonProjectTimeDatesCheckResultModel.ViolationType.NptPartiallyBeforeResourceStartDate);
var nptPartiallyAfterResourceEndDate = @((int)NonProjectTimeDatesCheckResultModel.ViolationType.NptPartiallyAfterResourceEndDate);
var nptOutBothResourceDates = @((int)NonProjectTimeDatesCheckResultModel.ViolationType.NptOutBothResourceDates);
var notMemberOfAnyTeam = @((int)NonProjectTimeDatesCheckResultModel.ViolationType.NotMemberOfAnyTeam);
var isInactive = @((int)NonProjectTimeDatesCheckResultModel.ViolationType.IsInactive);
var isTeamMode = $('#@Html.IdFor(x=> x.IsTeamAssignmentMode)').is(':checked');
if (data && data.HasViolation && data.Teams && (data.Teams.length > 0)) {
var publishTeamNames = data.Teams.length > 1;
var message = "";
if (isTeamMode) {
message = "The scheduled Non-Project Time does not occur between the Start Date and End Date for the following resource(s), and will only be applied to the active date range for each resource";
}
else {
message = "The scheduled Non-Project Time does not occur between the Start Date and End Date for this resource, and will only be applied to their active date range";
}
var resources = "";
for(var tIndex = 0; tIndex < data.Teams.length; tIndex++) {
var currentTeam = data.Teams[tIndex];
if (publishTeamNames)
resources += ("<br/></br/>Team '" + currentTeam.Name + "':");
else
resources += ("<br/>");
for(var rIndex = 0; rIndex < currentTeam.Resources.length; rIndex++) {
var currResource = currentTeam.Resources[rIndex];
var currResourceText = '<span style="margin-left: {2}px"><strong>{0}</strong> ({1})</span>';
var violationText = "";
switch (currResource.ViolationType) {
case nptCompletlyBeforeResourceStartDate:
violationText = "NPT occurs before the resource Start Date";
break;
case nptCompletlyAfterResourceEndDate:
violationText = "NPT occurs after the resource End Date";
break;
case nptPartiallyBeforeResourceStartDate:
violationText = "NPT occurs before the resource Start Date";
break;
case nptPartiallyAfterResourceEndDate:
violationText = "NPT occurs after the resource End Date";
break;
case nptOutBothResourceDates:
violationText = "NPT occurs before and finishes after the resource dates";
break;
case notMemberOfAnyTeam:
violationText = "resource is not member of any team";
break;
case isInactive:
violationText = "resource is inactive";
break;
}
currResourceText = currResourceText.replace("{0}", currResource.Name).replace("{1}", violationText)
.replace("{2}", publishTeamNames ? "18": "0");
resources += ("<br/>" + currResourceText);
}
}
message += resources;
bootbox.alert(message, function () {
finishNptSavingAction();
});
}
else{
finishNptSavingAction();
}
}
function finishNptSavingAction() {
// SA: reloadPage() performs redirect to url, stored in ContentLocker. ContentLocker gets url,
// when lock is set. But for new items no any lock is set, so ContentLocker doesn't have url
// to reload page. So, for newly created NPT we should simply reload browser window
if (!isNew)
reloadPage();
else
window.location.reload();
}
function fillNonProjectTimeWeeksGrid(needToRecalculate, needToApplyAllocations) {
if (needToRecalculate){
currentDistribution = {};
}
if (needToRecalculate || needToApplyAllocations) {
var isPermanent = $('#@Html.IdFor(x=> x.Permanent)').is(':checked'),
startDate = new Date($('#@Html.IdFor(model => model.NonProjectTimeStartDate)').val()),
startDateUTC = getUTC(startDate),
endDate = new Date($('#@Html.IdFor(model => model.NonProjectTimeEndDate)').val()),
endDateUTC = getUTC(endDate);
if (!isPermanent) {
if (!startDateUTC || !endDateUTC) {
$("#nptime-allocations").html("<tr><td colspan=\"2\">Please select Start and End dates for Non-Project Time</td></tr>");
return;
}
} else {
if (!startDateUTC) {
$("#nptime-allocations").html("<tr><td colspan=\"2\">Please select Start date for Non-Project Time</td></tr>");
return;
}
}
blockUI();
var isTeamMode = $('#@Html.IdFor(x=> x.IsTeamAssignmentMode)').is(':checked');
var resourceIds, teamIds;
if (isTeamMode)
teamIds = $("#@Html.IdFor(t=>t.TeamsAsText)").select2('val')
else
resourceIds = $("#@Html.IdFor(t=>t.ResourcesAsText)").select2('val')
$.post('/PeopleResource/RecalculateNPTimeAllocations', {
startDate: startDateUTC,
endDate: endDateUTC,
resourceIds: resourceIds,
teamIds: teamIds
}).done(function(data, textStatus, jqXHR){
currentDistribution = data || {};
if (needToApplyAllocations)
{
for(var key in currentDistribution)
{
var alloc = nptAllocations[key];
if (alloc)
{
if (alloc.HoursOff == 0)
{
currentDistribution[key].PercentOff = currentDistribution[key].HoursOff = 0;
} else {
if (currentDistribution[key].UOMValue == 0)
currentDistribution[key].PercentOff = currentDistribution[key].HoursOff = 0;
else
{
currentDistribution[key].PercentOff = Math.round(alloc.HoursOff * 100 / currentDistribution[key].UOMValue);
currentDistribution[key].HoursOff = alloc.HoursOff;
}
}
}
}
}
redrawNPTimeGrid();
}).always(unblockUI);
}
else{
redrawNPTimeGrid();
}
}
function redrawNPTimeGrid(){
if (!currentDistribution)
return;
var isPercentMode = $('#@Html.IdFor(x=> x.IsPercentsMode)').is(':checked');
var isPermanent = $('#@Html.IdFor(x=> x.Permanent)').is(':checked');
var keys = Object.keys(currentDistribution);
var content = "";
for (var i = 0; i<keys.length; i++) {
var weekDistribution = currentDistribution[keys[i]];
var row = drawNonProjectTimeWeeksGridRow(isPercentMode, isPermanent, i+1, weekDistribution.StartDate, weekDistribution.EndDate, weekDistribution.HoursOff || 0, weekDistribution.UOMValue);
if (row){
content+=row.prop('outerHTML');
}
}
$("#nptime-allocations").html(content);
if (isPercentMode) {
$('.slider-container').each(function (i, e) {
var weekEnding = new Date($(e).parent("td").children(".weekending-hidden").val()),
weekEndingUtc = weekEnding.getTime();
$(e).slider({
'range': 'min',
'min': 0,
'max': currentDistribution[weekEndingUtc].UOMValue || 0,
'value': $(e).children('.sliderValue').val(),
change: onDependentChange,
slide: onDependentChange
});
if(("@Model.IsHistory" == "True") || !((currentDistribution[weekEndingUtc].UOMValue || 0) > 0))
$(e).slider("disable");
});
updateSliderTitles();
}
}
function drawNonProjectTimeWeeksGridRow(isPercentMode, isPermanent, weekNo, startDate, endDate, hoursValue, uomValue) {
var localWeekStart = new Date(getLocal(startDate));
var localWeekEnding = new Date(getLocal(endDate));
var weekStartStrUTC = formatUniversalDate(localWeekStart);
var weekEndingStrUTC = formatUniversalDate(localWeekEnding);
var disabled = ("@Model.IsHistory" == "True" || !((uomValue || 0) > 0)) ? "disabled" : "";
var hoursOffTD = $("<td style='width: 235px; max-width: 235px; height: 43px; max-height: 43px;'></td>");
hoursOffTD.append("<input type=\"hidden\" name=\"NonProjectTimeAllocations.Index\" value=\"" + weekNo + "\"/>");
hoursOffTD.append("<input type=\"hidden\" name=\"NonProjectTimeAllocations[" + weekNo + "].WeekStartDate\" value=\"" + weekStartStrUTC + "\"/>");
hoursOffTD.append("<input type=\"hidden\" class=\"weekending-hidden\" name=\"NonProjectTimeAllocations[" + weekNo + "].WeekEndingDate\" value=\"" + weekEndingStrUTC + "\"/>");
if (isPercentMode) {
var sliderDiv = $("<div class=\"ui-slider-compacttablecell slider-container\" id=\"sldrcntr_" + weekNo + "\"></div>");
sliderDiv.append("<input type=\"hidden\" name=\"NonProjectTimeAllocations[" + weekNo + "].HoursOff\" class=\"sliderValue\" value=\"" + hoursValue + "\" /><span class=\"sliderTitle\"></span>");
hoursOffTD.append(sliderDiv);
}
else {
var hoursInput = '<input ' + disabled + ' class="form-control txtHoursOff" onchange="hoursChanged(this);" data-val="true" data-val-range="The field Time Off must be more than 0." data-val-range-max="2147483647" data-val-range-min="0" data-val-number="The field Time Off must be a number." data-val-required="The field Time Off is required." name="NonProjectTimeAllocations[' + weekNo + '].HoursOff" type="text" value="' + hoursValue + '">'
hoursOffTD.append(hoursInput);
}
// create row and append content to it
var startStr = (localWeekStart.getMonth() + 1) + '/' + localWeekStart.getDate() + '/' + localWeekStart.getFullYear(),
endStr = !isPermanent ? (localWeekEnding.getMonth() + 1) + '/' + localWeekEnding.getDate() + '/' + localWeekEnding.getFullYear() : "";
var row = $('<tr></tr>').append($("<td></td>").text(weekNo + ": " + startStr + ' - ' + endStr))
.append(hoursOffTD);
return row;
}
function updateDistributionValue(key, value) {
// cehck that value is number
if (!key || isNaN(value = parseInt(value)))
return;
currentDistribution[key].HoursOff = value;
currentDistribution[key].PercentOff = getPercentValue(value, currentDistribution[key].UOMValue);
};
function getUTC(date){
date = new Date(date);
if (isNaN(date.getTime()) || date.getTime() <= 0)
return null;
return date.getTime() - (date.getTimezoneOffset() * 60 * 1000);
};
function getLocal(date){
date = new Date(date);
if (isNaN(date.getTime()) || date.getTime() <= 0)
return null;
return date.getTime() + (date.getTimezoneOffset() * 60 * 1000);
};
function getPercentValue(hoursValue, maxUOM)
{
return Math.min(Math.round(hoursValue * 100.0 / maxUOM), 100);
}
</script>
@using (Ajax.BeginForm("ScheduleNonProjectTime", "PeopleResource", null, new AjaxOptions { HttpMethod = "Post", OnBegin = "blockUI", OnSuccess = "onSuccess", OnFailure = "onFailure(xhr)", OnComplete = "unblockUI" }, new { id = "npTimeForm" }))
{
@Html.AntiForgeryToken()
@Html.HiddenFor(model => model.Id)
@Html.HiddenFor(t => t.NonProjectTimeCategoryName)
@Html.HiddenFor(t => t.TeamId)
@Html.HiddenFor(x => x.NonProjectTimeStartDateOld)
@Html.HiddenFor(x => x.PermanentOld)
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">x</button>
<h4 class="modal-title">Schedule Non-Project Time</h4>
</div>
<div class="modal-body">
<div class="form-group">
@Html.LabelFor(model => model.NonProjectTimeName, new { @class = "col-sm-3 control-label" })
<div class="col-sm-9">
@Html.TextBoxFor(model => model.NonProjectTimeName, new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.NonProjectTimeName)
</div>
</div>
<div class="form-group">
<div class="col-sm-9">
@Html.CheckBoxFor(model => model.Permanent, new { @class = "form-control" })
</div>
</div>
<div class="col-sm-6">
<label class="control-label">Dates</label>
<div style="overflow: hidden;">
<div class="input-daterange input-group" id="bs-datepicker-np-time-range">
@Html.EditorFor(model => model.NonProjectTimeStartDate)
<div class="input-group-addon">to</div>
@Html.EditorFor(model => model.NonProjectTimeEndDate)
</div>
@Html.ValidationMessageFor(model => model.NonProjectTimeStartDate)
@Html.ValidationMessageFor(model => model.NonProjectTimeEndDate)
</div>
</div>
<div class="col-sm-2">
</div>
<div class="col-sm-4">
<div class="form-group">
@Html.LabelFor(model => model.NonProjectTimeDuration, new { @class = "col-sm-3 control-label" })
@Html.TextBoxFor(model => model.NonProjectTimeDuration, new { @class = "form-control", Readonly = "true" })
</div>
</div>
<div class="form-group">
<div class="col-sm-6">
<div class="form-group">
<div>
@Html.LabelFor(model => model.NonProjectTimeCategoryId, new { @class = "control-label" })
@Html.EditorFor(model => model.NonProjectTimeCategoryId)
@Html.ValidationMessageFor(model => model.NonProjectTimeCategoryId)
</div>
</div>
</div>
<div class="col-sm-2">
</div>
<div class="form-group col-sm-4">
@Html.LabelFor(model => model.NonProjectTimeCost, new { @class = "control-label" })
<div class="input-group">
<span class="input-group-addon">$</span>
@Html.TextBox("NonProjectTimeCost", Model.NonProjectTimeCost.ToCurrencyString(), new { @class = "form-control", style = "width:125px;" })
@Html.ValidationMessageFor(model => model.NonProjectTimeCost)
</div>
</div>
</div>
<div class="form-group">
@Html.LabelFor(x => x.IsTeamAssignmentMode, new { @class = "col-sm-3 control-label" })
<div class="col-sm-9">
@Html.CheckBoxFor(model => model.IsTeamAssignmentMode, new { @class = "form-control" })
</div>
</div>
<div class="form-group" id="nptResourcesBlock">
@Html.LabelFor(model => model.ResourcesAsText, new { @class = "col-sm-3 control-label" })
<div class="col-sm-9 select2-primary">
@Html.TextBoxFor(model => model.ResourcesAsText, new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.ResourcesAsText)
</div>
</div>
<div class="form-group" id="nptTeamsBlock">
@Html.LabelFor(model => model.TeamsAsText, new { @class = "col-sm-3 control-label" })
<div class="col-sm-9 select2-primary">
@Html.TextBoxFor(model => model.TeamsAsText, new { @class = "form-control" })
@Html.ValidationMessageFor(model => model.TeamsAsText)
</div>
</div>
<div class="form-group">
@Html.LabelFor(x => x.IsPercentsMode, new { @class = "col-sm-3 control-label" })
<div class="col-sm-9">
@Html.CheckBoxFor(model => model.IsPercentsMode, new { @class = "form-control" })
</div>
</div>
@if (Model != null && Model.Id != Guid.Empty && Model.Permanent)
{
<div class="form-group" id="effective-date-container">
@Html.LabelFor(x => x.EffectiveDateOfChange, new { @class = "col-sm-3 control-label" })
<div class="col-sm-9">
@Html.EditorFor(model => model.EffectiveDateOfChange)
@Html.ValidationMessageFor(model => model.EffectiveDateOfChange)
</div>
</div>
}
@* TODO: uncomment this section after Advanced Mode is completed *@
@*@if (Model != null && Model.Permanent && !Model.IsCurveConstant)
{
<div class="form-group">
<div class="alert alert-warning">
Changes have been made to the Non-Project time curve and it is now not evenly distributed - please use Advanced Mode for precise settings
</div>
</div>
}*@
<div id="hours-mode-message" class="form-group @(Model != null && !Model.IsPercentsMode ? "" : "hidden")">
<div class="alert alert-info">
If number of hours exceeds Resource's available hours, total available hours will be used as Non Project Time for a given Week and Resource
</div>
</div>
<div class="form-group">
<table id="npTimeWeeksGrid" class="table table-light table-striped dataTable-tight col-sm-12">
<thead>
<tr>
<th class="autowidth">Week</th>
<th class="hugeCol">Time off</th>
</tr>
</thead>
<tbody id="nptime-allocations"></tbody>
</table>
</div>
<div class="form-group">
@Html.LabelFor(model => model.Details, new { @class = "col-sm-3 control-label" })
<div class="col-sm-9 select2-primary">
@Html.TextAreaFor(model => model.Details, new { @class = "form-control", rows = 4 })
@Html.ValidationMessageFor(model => model.Details)
</div>
</div>
@Html.ValidationSummary(false, "The non-project time schedule could not be saved due to the following errors:")
</div> <!-- / .modal-body -->
<div class="modal-footer">
<button id="btnSaveNPTime" type="submit" class="btn btn-success">Save</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
}