415 lines
17 KiB
Plaintext
415 lines
17 KiB
Plaintext
@using EnVisage.Code
|
||
@model string
|
||
@{
|
||
ViewBag.Title = "Roadmap Optimizer";
|
||
Layout = "~/Views/Shared/_Layout.cshtml";
|
||
}
|
||
|
||
@section stylesheets
|
||
{
|
||
<link href="~/Content/stylesheets/xeditable.css" rel="stylesheet" type="text/css" />
|
||
<link href="~/Content/stylesheets/select.min.css" rel="stylesheet" type="text/css" />
|
||
<link href="~/Content/stylesheets/angular-dnd.css" rel="stylesheet" type="text/css" />
|
||
<link href="~/Content/stylesheets/mix-grid.css" rel="stylesheet" type="text/css" />
|
||
<link href="~/Content/stylesheets/bootstrap-modal-bs3patch.css" rel="stylesheet" />
|
||
<link href="~/Content/stylesheets/bootstrap-modal.css" rel="stylesheet" />
|
||
}
|
||
@section Scripts
|
||
{
|
||
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")"></script>
|
||
<script src="@Url.Content("~/Scripts/URIjs.min.js")"></script>
|
||
<script src="@Url.Content("~/Scripts/CustomValidation.js")" type="text/javascript"></script>
|
||
<script src="@Url.Content("~/Scripts/sliders.js")" type="text/javascript"></script>
|
||
<script src="@Url.Content("~/Scripts/calendarMethods.js")" type="text/javascript"></script>
|
||
<script src="@Url.Content("~/Scripts/Plugins/ScenarioDetailsGrid.js")" type="text/javascript"></script>
|
||
@Scripts.Render("~/bundles/angular-app-modules")
|
||
<script src="@Url.Content("~/Scripts/Angular/app.js")"></script>
|
||
<script src="@Url.Content("~/Scripts/Angular/Types/GridHeader.min.js")"></script>
|
||
<script src="@Url.Content("~/Scripts/Angular/Controllers/MixControllers/mixHeaderController.js")"></script>
|
||
<script src="@Url.Content("~/Scripts/Angular/Controllers/MixControllers/mixProjectController.js")"></script>
|
||
<script src="@Url.Content("~/Scripts/Angular/Controllers/teamInfoController.js")"></script>
|
||
<script src="@Url.Content("~/Scripts/Angular/Controllers/scenarioDetailController.js")"></script>
|
||
<script src="@Url.Content("~/Scripts/Angular/Controllers/costSavingController.js")"></script>
|
||
<script src="@Url.Content("~/Scripts/Angular/Controllers/slidersGroupController.js")"></script>
|
||
<script src="@Url.Content("~/Scripts/Plugins/CapacityPlanning.js")" type="text/javascript"></script>
|
||
<script src="@Url.Content("~/Scripts/bootstrap-modalmanager.js")" type="text/javascript"></script>
|
||
<script src="@Url.Content("~/Scripts/bootstrap-modal.js")" type="text/javascript"></script>
|
||
<script>
|
||
function initMixForm() {
|
||
$("div#main-wrapper").attr("data-section", "mixCalendar");
|
||
var datePickerOptions = {
|
||
format: 'm/d/yyyy',
|
||
autoclose: true,
|
||
orientation: $('body').hasClass('right-to-left') ? "auto right" : 'auto auto',
|
||
startDate: '@(Constants.MIN_SELECTABLE_DATE)', // SA. ENV-1235
|
||
endDate: '@(Constants.MAX_SELECTABLE_DATE)' // SA. ENV-1235
|
||
};
|
||
|
||
$('#bs-datepicker-mix-range').datepicker(datePickerOptions).on('changeDate', function (evt) {
|
||
if (evt.target.id === 'MixStartDate' && !$('#MixEndDate').val()) {
|
||
// SA. ENV-1235
|
||
$('#bs-datepicker-mix-range').data('datepicker').pickers[1].prepopulate(evt.date);
|
||
}
|
||
});
|
||
|
||
$('#mixTeamsAndViews').select2({
|
||
allowClear: true
|
||
});
|
||
$('#mixContributors').select2({
|
||
allowClear: true
|
||
});
|
||
$('#availableMixes').select2({
|
||
allowClear: true
|
||
});
|
||
$('#selectedTeamViews').select2({
|
||
allowClear: true
|
||
});
|
||
$('#selProjects2Add').select2({
|
||
allowClear: true
|
||
});
|
||
$('#mix-edit-scenario-teams-dialog select').select2({
|
||
allowClear: true
|
||
});
|
||
$('#copierProjectId').select2();
|
||
$('#btnSaveMix').on('click', function () {
|
||
showSaveMixDialog();
|
||
});
|
||
|
||
$('#btnActivateMix').on('click', function () {
|
||
showSaveMixDialog();
|
||
});
|
||
var menu = $('#mixMenu');
|
||
$.each($('#mixMenuTemplate').children(), function (i, obj) {
|
||
menu.append($(obj));
|
||
});
|
||
menu.on('click', function (event) {
|
||
event.stopPropagation();
|
||
});
|
||
$('#divMixMenuTemplate').remove();
|
||
}
|
||
|
||
function showSaveMixDialog() {
|
||
if (!$('#mix-header').valid())
|
||
return;
|
||
|
||
$('#saveMixModal').modal();
|
||
}
|
||
|
||
function canSaveMix() {
|
||
return $('#mix-header').valid() && $('#saveMixForm').valid();
|
||
}
|
||
|
||
function isValidHeader() {
|
||
if ($('#mix-header').valid()) {
|
||
$('#mix-header .validation-summary-errors')
|
||
.addClass('validation-summary-valid')
|
||
.removeClass('validation-summary-errors');
|
||
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
function printPDF(a) {
|
||
if ("@Request["id"]" == "") {
|
||
alert("Please save the mix first or open an existing mix, only saved mixes can be saved to PDF");
|
||
return false;
|
||
}
|
||
$(a).parent().parent().parent().removeClass('open');
|
||
return true;
|
||
}
|
||
|
||
function loadTeam(teamId) {
|
||
blockUI();
|
||
|
||
if (teamId != null)
|
||
StartEdit('Team', teamId, null, null, 'erorMsgPlaceholder');
|
||
|
||
var itemUrl = teamId != null ? "?Id=" + teamId : "";
|
||
var url = '@Url.Action("EditTeam", "Mix")' + itemUrl;
|
||
|
||
// SA. ENV-1065. Rewrited to $.Ajax to force turn off request caching
|
||
$('#editReload').html("");
|
||
$.ajax({
|
||
url: url,
|
||
cache: false,
|
||
dataType: "html",
|
||
success: function (data) {
|
||
$('#editReload').html(data);
|
||
$('#editTeam')
|
||
.on('hidden.bs.modal', function () {
|
||
if (teamId != null) {
|
||
StopEdit();
|
||
RemoveLock('Team', teamId);
|
||
}
|
||
})
|
||
.on('shown.bs.modal', function () {
|
||
// No actions
|
||
})
|
||
.modal('show');
|
||
|
||
initTeam();
|
||
unblockUI();
|
||
}
|
||
});
|
||
}
|
||
|
||
function editTeam(Id) {
|
||
loadTeam(Id);
|
||
return true;
|
||
}
|
||
|
||
//fix modal force focus
|
||
$.fn.modal.Constructor.prototype.enforceFocus = function () {
|
||
var that = this;
|
||
$(document).on('focusin.modal', function (e) {
|
||
if ($(e.target).hasClass('select2-input')) {
|
||
return true;
|
||
}
|
||
|
||
if (that.$element[0] !== e.target && !that.$element.has(e.target).length) {
|
||
that.$element.focus();
|
||
}
|
||
});
|
||
};
|
||
|
||
function getAntiXSRFRequest(url, data) {
|
||
// it needs for sending XSRF-Token in the form to the server
|
||
return {
|
||
method: 'POST',
|
||
url: url,
|
||
contentType: 'application/json; charset=utf-8',
|
||
data: JSON.stringify({ model: data }),
|
||
dataType: 'JSON',
|
||
headers: {
|
||
"__RequestVerificationToken": $('input[name=__RequestVerificationToken]').val()
|
||
}
|
||
};
|
||
}
|
||
|
||
init.push(function () {
|
||
$(document).on('hide.bs.modal', '#editTeam', function (e) {
|
||
// skip modal hide event from datepickers
|
||
if ($(e.target).attr('id') != 'editTeam')
|
||
return true; // close modal form
|
||
// check that form has been changed
|
||
if (typeof isTeamDataChanged === 'function')
|
||
// if form has been changed
|
||
if (isTeamDataChanged()) {
|
||
// ask user for confirmation of form close
|
||
if (confirm("Adding Team form contains unsaved changes, do you really want to close the form?")) {
|
||
// reset change indicator
|
||
if (typeof resetTeamDataChanged === 'function') {
|
||
resetTeamDataChanged();
|
||
}
|
||
return true; // close modal form
|
||
};
|
||
return false; // DO NOT close modal form
|
||
}
|
||
return true; // close modal form
|
||
});
|
||
});
|
||
</script>
|
||
}
|
||
@section pagemenu
|
||
{
|
||
<ul class="nav navbar-nav navbar-right">
|
||
<li class="dropdown" id="divMixMenu">
|
||
<a href="#" class="dropdown-toggle user-menu" data-toggle="dropdown">
|
||
<i class="fa fa-bars"></i><span>Page Options</span> <i class="fa fa-caret-down"></i>
|
||
</a>
|
||
<ul class="dropdown-menu dropdown-menu-right" id="mixMenu">
|
||
<li><a onclick="_printDoc();$(this).parent().parent().parent().removeClass('open');"><i class="dropdown-icon fa fa-print"></i> Print Page</a></li>
|
||
<li><a href="@Url.Action("ExportToPDF", new {@id = Request["id"]})" onclick="return printPDF(this);"><i class="dropdown-icon fa fa-file-text-o"></i> Save Page as PDF</a></li>
|
||
<li class="divider"></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
}
|
||
@{
|
||
var user = (new EnVisage.Code.Cache.UsersCache()).Value.FirstOrDefault(x => x.Id == new Guid(User.Identity.GetID()));
|
||
var pagePreferences = user.GetPreferences(Url.Action("Index", "Mix"), "mixCalendar");
|
||
|
||
var json = Newtonsoft.Json.JsonConvert.SerializeObject(new
|
||
{
|
||
mixId = Model,
|
||
showAvgTotals = user.PreferredTotalsDisplaying,
|
||
prefs = pagePreferences
|
||
});
|
||
}
|
||
<div ng-app="app">
|
||
<div id="erorMsgPlaceholder"></div>
|
||
<div class="panel">
|
||
<div class="panel-body" style="border: 0;">
|
||
<fieldset class="form-group-margin">
|
||
<div id="mixHeader" ng-controller="mixHeaderController" ng-init="init(@json)" init-form ng-cloak>
|
||
@Html.AntiForgeryToken()
|
||
<form style="border: 0;" id="mix-header">
|
||
<div class="row" ng-if="data.AvailableMixes != null && data.AvailableMixes.length > 0">
|
||
<div class="col-sm-6">
|
||
<div class="form-group no-margin-hr">
|
||
<label class="control-label noprint" for="availableMixes">Select existing Mix</label>
|
||
<span class="nodisplay-inline">Mix: </span><select id="availableMixes" name="availableMixes" class="form-control"
|
||
ng-model="data.SelectedMix"
|
||
ng-options="mix.Value as mix.Text for mix in data.AvailableMixes | orderBy:'Text'">
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="col-sm-6">
|
||
<div class="form-group no-margin-hr">
|
||
<label class="control-label"> </label><br />
|
||
@* Need to show only if selected mix is exists and it is not current opened mix *@
|
||
<button type="button" class="btn btn-primary" ng-click="navigateToMix()" ng-show="data.ShowOpen">Open Mix</button>
|
||
@* SA. ENV-1138. Delete selected, but unopened mix *@
|
||
<button type="button" class="btn btn-danger" ng-click="deleteUnopenedMix()" ng-show="data.ShowOpen">Delete Mix</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="row" ng-if="data.AvailableTeamsAndViews != null && data.AvailableTeamsAndViews.length > 0">
|
||
<div class="col-sm-6">
|
||
<div class="form-group select2-primary no-margin-hr">
|
||
<label class="control-label">Team(s) or View(s)</label>
|
||
<select id="selectedTeamViews" name="selectedTeamViews" class="form-control" multiple="multiple" selected-data-changed
|
||
ng-model="data.SelectedTeamsAndViews"
|
||
ng-options="team.TVName group by team.Group.Name for team in data.AvailableTeamsAndViews | orderBy:['Group.Name','TVName']">
|
||
</select>
|
||
<span class="help-block field-validation-valid" data-valmsg-for="mixTeamsAndViews" data-valmsg-replace="true"></span>
|
||
</div>
|
||
</div>
|
||
<div class="col-sm-6">
|
||
<div class="form-group no-margin-hr">
|
||
<label class="control-label">Dates</label>
|
||
<div class="input-daterange input-group" id="bs-datepicker-mix-range">
|
||
<div class="input-daterange input-group date floatdaterange" id="mix-dateStart">
|
||
<input type="text" id="MixStartDate" name="MixStartDate" class="form-control"
|
||
data-val="true" data-val-date="The field Start Date must be a date."
|
||
data-val-required="The Start Date field is required."
|
||
ng-model="data.StartDate"
|
||
ng-disabled="!data.IsEditable" />
|
||
</div>
|
||
<div class="pull-left floatdaterange-addon">To</div>
|
||
<div class="input-daterange input-group date floatdaterange" id="mix-dateEnd">
|
||
<input type="text" id="MixEndDate" name="MixEndDate" class="form-control"
|
||
data-val="true" data-val-date="The field End Date must be a date."
|
||
data-val-required="The End Date field is required."
|
||
data-val-dategreaterthanorequal="Mix End Date should not be less than Start Date"
|
||
data-val-dategreaterthanorequal-otherpropertyname="MixStartDate"
|
||
ng-model="data.EndDate"
|
||
ng-disabled="!data.IsEditable" />
|
||
</div>
|
||
</div>
|
||
<span class="help-block field-validation-valid" data-valmsg-for="MixStartDate" data-valmsg-replace="true"></span>
|
||
<span class="help-block field-validation-valid" data-valmsg-for="MixEndDate" data-valmsg-replace="true"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@Html.ValidationSummary(false, "The Mix could not be saved due to the following errors:")
|
||
<div class="row">
|
||
<div class="col-sm-12">
|
||
<div class="form-group select2-primary no-margin-hr">
|
||
<button type="button" class="btn btn-primary" id="btnApplyFilter" ng-show="data.IsEditable" ng-disabled="!canApplyFilter()" ng-click="applyFilter()">
|
||
<i class="fa fa-search"></i> Apply Filter
|
||
</button>
|
||
<a class="btn btn-success noprint" ng-show="data.IsEditable" onclick="return editTeam(null);">
|
||
<i class="fa fa-plus"></i> Create Team
|
||
</a>
|
||
<button type="button" class="btn btn-success" id="btnSaveMix" ng-show="canSaveMix()" ng-disabled="!data.DataChanged" ng-click="requestMixSaving()">
|
||
<i class="fa fa-save"></i> Save this Mix
|
||
</button>
|
||
<button type="button" class="btn btn-warning" id="btnActivateMix" ng-show="canActivateMix()" ng-click="requestMixActivation()">
|
||
<i class="fa fa-check-circle-o"></i> Activate Mix
|
||
</button>
|
||
<button type="button" class="btn btn-danger" id="btnDeleteMix" ng-show="canDeleteMix()" ng-disabled="data.DisableDelete" ng-click="deleteCurrentMix()">
|
||
<i class="fa fa-trash-o"></i> Delete Mix
|
||
</button>
|
||
<button type="button" class="btn btn-danger" ng-show="canClearMix()" ng-click="navigateToNewMix()" >
|
||
<i class="fa fa-times"></i> Clear
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="row" ng-if="data.AvailableUsers != null && data.AvailableUsers.length > 0">
|
||
<div class="col-sm-6">
|
||
<div class="select2-primary no-margin-hr" ng-show="canSaveMix()">
|
||
<label class="control-label">Contributors</label>
|
||
<select id="mixContributors" name="mixContributors" class="form-control" data-val="true" data-val-required="You need to select at least one contributor"
|
||
ng-disabled="!data.IsEditable"
|
||
ng-model="data.Users"
|
||
ng-options="user.Value as user.Text for user in data.AvailableUsers | orderBy: 'Text'"
|
||
multiple="multiple">
|
||
</select>
|
||
<span class="help-block field-validation-valid" data-valmsg-for="mixContributors" data-valmsg-replace="true"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
<div id="saveMixModal" class="modal fade" tabindex="-1" role="dialog" style="display: none;" data-backdrop="static">
|
||
<form id="saveMixForm">
|
||
<div class="modal-dialog">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||
<h4 class="modal-title" ng-show="data.SaveMixHasBeenRequested">Save Mix</h4>
|
||
<h4 class="modal-title" ng-show="data.ActivateMixHasBeenRequested">Activate Mix</h4>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="panel-body form-horizontal" ng-show="data.ShowSaveForm">
|
||
<div class="row">
|
||
<div class="col-sm-12">
|
||
<div class="form-group select2-primary no-margin-hr">
|
||
<label class="control-label">Mix Name</label>
|
||
<input type="text" id="Name" name="Name" class="form-control"
|
||
data-val="true" data-val-required="The Name field is required."
|
||
data-val-maxlength="The field Name must be a string or array type with a maximum length of '64'."
|
||
data-val-maxlength-max="64"
|
||
ng-model="data.Name"
|
||
ng-disabled="!data.IsEditable" />
|
||
<span class="help-block field-validation-valid" data-valmsg-for="Name" data-valmsg-replace="true"></span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@Html.ValidationSummary(false, "The Mix could not be saved due to the following errors:")
|
||
</div>
|
||
<div class="panel-body form-horizontal" ng-show="!data.ShowSaveForm">
|
||
<div class="row">
|
||
Are you sure you want to activate this mix? This action cannot be undone.
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-success" ng-show="data.SaveMixHasBeenRequested" ng-disabled="data.DisableScreen" ng-click="saveChanges()">
|
||
<i class="fa fa-save"></i> Save
|
||
</button>
|
||
<button type="button" class="btn btn-warning" ng-show="data.ActivateMixHasBeenRequested" ng-disabled="data.DisableScreen" ng-click="activateMix()">
|
||
<i class="fa fa-check-circle-o"></i> Activate
|
||
</button>
|
||
<button type="button" class="btn btn-default" data-dismiss="modal" ng-disabled="data.DisableScreen">Cancel</button>
|
||
</div>
|
||
</div>
|
||
<!-- / .modal-content -->
|
||
</div>
|
||
<!-- / .modal-dialog -->
|
||
</form>
|
||
</div>
|
||
<!-- /.modal -->
|
||
</div>
|
||
</fieldset>
|
||
<div>
|
||
@Html.Partial("_mixCalendar", json)
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="editTeam" class="modal fade" data-width="1070" tabindex="-1" role="dialog" data-backdrop="static">
|
||
<div class="modal-content" id="editReload">
|
||
</div>
|
||
<!-- / .modal-content -->
|
||
</div>
|
||
<div id="create-scenario-dialog" class="modal fade" data-width="900" tabindex="-1" role="dialog" data-backdrop="static">
|
||
<div class="modal-content" id="create-scenario-dialog-content">
|
||
</div>
|
||
<!-- / .modal-content -->
|
||
</div>
|
||
<!-- /.modal -->
|