1085 lines
54 KiB
Plaintext
1085 lines
54 KiB
Plaintext
@model Disco.Web.Areas.Config.Models.DeviceBatch.ShowModel
|
|
@using Disco.Services.Devices.ManagedGroups;
|
|
@using Disco.Web.Areas.Config.Models.Shared;
|
|
@{
|
|
Authorization.Require(Claims.Config.DeviceBatch.Show);
|
|
|
|
ViewBag.Title = Html.ToBreadcrumb("Configuration", MVC.Config.Config.Index(), "Device Batches", MVC.Config.DeviceBatch.Index(null), Model.DeviceBatch.ToString());
|
|
|
|
var canConfig = Authorization.Has(Claims.Config.DeviceBatch.Configure);
|
|
var canDeviceModelShow = Authorization.Has(Claims.Config.DeviceModel.Show);
|
|
|
|
var hideAdvanced =
|
|
Model.DeviceBatch.AssignedUsersLinkedGroup == null &&
|
|
Model.DeviceBatch.DevicesLinkedGroup == null;
|
|
|
|
Html.BundleDeferred("~/Style/Shadowbox");
|
|
Html.BundleDeferred("~/ClientScripts/Modules/Shadowbox");
|
|
Html.BundleDeferred("~/ClientScripts/Modules/jQuery-SignalR");
|
|
|
|
if (canConfig)
|
|
{
|
|
Html.BundleDeferred("~/ClientScripts/Modules/Disco-PropertyChangeHelpers");
|
|
Html.BundleDeferred("~/ClientScripts/Modules/Disco-AjaxHelperIcons");
|
|
Html.BundleDeferred("~/ClientScripts/Modules/tinymce");
|
|
Html.BundleDeferred("~/ClientScripts/Modules/Disco-AttachmentUploader");
|
|
}
|
|
}
|
|
<div class="form deviceBatches@(hideAdvanced ? " Config_HideAdvanced" : null)" style="width: 730px">
|
|
<table>
|
|
<tr>
|
|
<th style="width: 150px">
|
|
Id:
|
|
</th>
|
|
<td>
|
|
@Html.DisplayFor(model => model.DeviceBatch.Id)
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>
|
|
Name:
|
|
</th>
|
|
<td>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.Name)
|
|
@AjaxHelpers.AjaxSave()
|
|
@AjaxHelpers.AjaxLoader()
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
document.DiscoFunctions.PropertyChangeHelper(
|
|
$('#DeviceBatch_Name'),
|
|
'Invalid Name',
|
|
'@(Url.Action(MVC.API.DeviceBatch.UpdateName(Model.DeviceBatch.Id)))',
|
|
'BatchName'
|
|
);
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
@Model.DeviceBatch.Name
|
|
}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>
|
|
Default Device Model:
|
|
</th>
|
|
<td>
|
|
@if (canConfig)
|
|
{
|
|
@Html.DropDownListFor(model => model.DeviceBatch.DefaultDeviceModelId, Model.DeviceModels.ToSelectListItems(null, true))
|
|
@AjaxHelpers.AjaxSave()
|
|
@AjaxHelpers.AjaxLoader()
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
document.DiscoFunctions.PropertyChangeHelper(
|
|
$('#DeviceBatch_DefaultDeviceModelId'),
|
|
null,
|
|
'@(Url.Action(MVC.API.DeviceBatch.UpdateDefaultDeviceModelId(Model.DeviceBatch.Id)))',
|
|
'DefaultDeviceModelId'
|
|
);
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
if (Model.DefaultDeviceModel == null)
|
|
{<span class="smallMessage"><None Specified></span>}
|
|
else
|
|
{@Model.DefaultDeviceModel.ToString();
|
|
}
|
|
}
|
|
<br />
|
|
<span class="smallMessage">
|
|
Devices added offline will default to this Device Model.
|
|
Once a device enrols the Device Model will be accurately represented.
|
|
</span>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Devices</th>
|
|
<td>
|
|
@if (Model.DeviceModelMembers.Count > 0)
|
|
{
|
|
<table class="tableData smallTable">
|
|
<thead>
|
|
<tr>
|
|
<th>Model</th>
|
|
<th>Device Count</th>
|
|
<th>Decommissioned</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach (var membership in Model.DeviceModelMembers.OrderByDescending(dmm => dmm.DeviceCount))
|
|
{
|
|
<tr>
|
|
<td>
|
|
@if (canDeviceModelShow)
|
|
{
|
|
@Html.ActionLink(membership.DeviceModel.ToString(), MVC.Config.DeviceModel.Index(membership.DeviceModel.Id))
|
|
}
|
|
else
|
|
{
|
|
@membership.DeviceModel.ToString()
|
|
}
|
|
</td>
|
|
<td>
|
|
@membership.DeviceCount.ToString("n0")
|
|
</td>
|
|
<td>
|
|
@membership.DeviceDecommissionedCount.ToString("n0")
|
|
</td>
|
|
</tr>
|
|
}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr>
|
|
<th>Total Models: @Model.DeviceModelMembers.Count.ToString("n0")</th>
|
|
<th>@Model.DeviceCount.ToString("n0")</th>
|
|
<th>@Model.DeviceDecommissionedCount.ToString("n0")</th>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
}
|
|
else
|
|
{
|
|
<div class="smallMessage">No device models are referenced in this batch.</div>
|
|
}
|
|
@if (Model.DeviceBatch.UnitQuantity.HasValue && Model.DeviceBatch.UnitQuantity.Value > Model.DeviceCount)
|
|
{
|
|
var missingCount = Model.DeviceBatch.UnitQuantity.Value - Model.DeviceCount;
|
|
<div class="info-box">
|
|
<p class="fa-p">
|
|
<i class="fa fa-info-circle information"></i>
|
|
@Model.DeviceCount.ToString("n0") of @(Model.DeviceBatch.UnitQuantity.Value.ToString("n0")) purchased devices are managed by Disco. <strong>@missingCount.ToString("n0") @(missingCount == 1 ? "is" : "are") not managed</strong>.
|
|
</p>
|
|
</div>
|
|
}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>
|
|
Purchase:
|
|
</th>
|
|
<td class="details">
|
|
<table class="sub">
|
|
<tr>
|
|
<th class="name" style="width: 100px">
|
|
Purchase Date:
|
|
</th>
|
|
<td>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.PurchaseDate)
|
|
@AjaxHelpers.AjaxLoader()
|
|
<script>
|
|
$(function () {
|
|
var dateField = $('#DeviceBatch_PurchaseDate');
|
|
document.DiscoFunctions.DateChangeHelper(
|
|
dateField,
|
|
'Invalid Date',
|
|
'@(Url.Action(MVC.API.DeviceBatch.UpdatePurchaseDate(Model.DeviceBatch.Id)))',
|
|
'PurchaseDate',
|
|
null,
|
|
true
|
|
);
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
@CommonHelpers.FriendlyDate(Model.DeviceBatch.PurchaseDate)
|
|
}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>
|
|
Supplier:
|
|
</th>
|
|
<td>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.Supplier)
|
|
@AjaxHelpers.AjaxSave()
|
|
@AjaxHelpers.AjaxLoader()
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
document.DiscoFunctions.PropertyChangeHelper(
|
|
$('#DeviceBatch_Supplier'),
|
|
'Batch Supplier',
|
|
'@(Url.Action(MVC.API.DeviceBatch.UpdateSupplier(Model.DeviceBatch.Id)))',
|
|
'Supplier'
|
|
);
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
if (string.IsNullOrEmpty(Model.DeviceBatch.Supplier))
|
|
{<span class="smallMessage"><None Specified></span>}
|
|
else
|
|
{@Model.DeviceBatch.Supplier}
|
|
}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>
|
|
Unit Cost:
|
|
</th>
|
|
<td>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.UnitCost)
|
|
@AjaxHelpers.AjaxSave()
|
|
@AjaxHelpers.AjaxLoader()
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
document.DiscoFunctions.PropertyChangeHelper(
|
|
$('#DeviceBatch_UnitCost'),
|
|
'Unit Cost',
|
|
'@(Url.Action(MVC.API.DeviceBatch.UpdateUnitCost(Model.DeviceBatch.Id)))',
|
|
'UnitCost'
|
|
);
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
if (!Model.DeviceBatch.UnitCost.HasValue)
|
|
{<span class="smallMessage"><None Specified></span>}
|
|
else
|
|
{@Model.DeviceBatch.UnitCost.Value.ToString("C")}
|
|
}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>
|
|
Quantity:
|
|
</th>
|
|
<td>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.UnitQuantity)
|
|
@AjaxHelpers.AjaxSave()
|
|
@AjaxHelpers.AjaxLoader()
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
document.DiscoFunctions.PropertyChangeHelper(
|
|
$('#DeviceBatch_UnitQuantity'),
|
|
'Quantity',
|
|
'@(Url.Action(MVC.API.DeviceBatch.UpdateUnitQuantity(Model.DeviceBatch.Id)))',
|
|
'UnitQuantity'
|
|
);
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
if (!Model.DeviceBatch.UnitQuantity.HasValue)
|
|
{<span class="smallMessage"><None Specified></span>}
|
|
else
|
|
{@Model.DeviceBatch.UnitQuantity.Value.ToString("n0")}
|
|
}
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<div id="DeviceBatch_PurchaseDetails_Container" data-updateurl="@(Url.Action(MVC.API.DeviceBatch.UpdatePurchaseDetails(Model.DeviceBatch.Id)))">
|
|
<div>
|
|
Details @AjaxHelpers.AjaxLoader("ajaxPurchaseDetails")
|
|
</div>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.PurchaseDetails)
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
const $field = $('#DeviceBatch_PurchaseDetails');
|
|
|
|
async function updated() {
|
|
$('#ajaxPurchaseDetails_loading').show();
|
|
try {
|
|
const body = new FormData();
|
|
body.append('__RequestVerificationToken', document.body.dataset.antiforgery);
|
|
body.append('purchaseDetails', $field.tinymce().getContent())
|
|
|
|
const response = await fetch($('#DeviceBatch_PurchaseDetails_Container').attr('data-updateurl'), {
|
|
method: 'POST',
|
|
body: body
|
|
});
|
|
|
|
if (response.ok) {
|
|
$('#ajaxPurchaseDetails_ok').show().delay('fast').fadeOut('slow');
|
|
} else {
|
|
alert('Unable to update purchase details: ' + response.statusText);
|
|
}
|
|
} catch (e) {
|
|
alert('Unable to update purchase details: ' + e);
|
|
}
|
|
$('#ajaxPurchaseDetails_loading').hide();
|
|
}
|
|
|
|
$field.tinymce({
|
|
theme: 'simple',
|
|
add_unload_trigger: false,
|
|
schema: "html5",
|
|
statusbar: false,
|
|
setup: function (ed) {
|
|
ed.on('init', function () {
|
|
$(ed.getWin()).blur(function () { updated(); });
|
|
});
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
if (string.IsNullOrEmpty(Model.DeviceBatch.PurchaseDetails))
|
|
{<span class="smallMessage"><None Specified></span>}
|
|
else
|
|
{@(new HtmlString(Model.DeviceBatch.PurchaseDetails))}
|
|
}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>
|
|
Warranty:
|
|
</th>
|
|
<td class="details">
|
|
<table class="sub">
|
|
<tr>
|
|
<th class="name" style="width: 100px">
|
|
Valid Until:
|
|
</th>
|
|
<td>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.WarrantyValidUntil)
|
|
@AjaxHelpers.AjaxLoader()
|
|
<script>
|
|
$(function () {
|
|
var dateField = $('#DeviceBatch_WarrantyValidUntil');
|
|
document.DiscoFunctions.DateChangeHelper(
|
|
dateField,
|
|
'Warranty Valid Until',
|
|
'@(Url.Action(MVC.API.DeviceBatch.UpdateWarrantyValidUntil(Model.DeviceBatch.Id)))',
|
|
'WarrantyValidUntil',
|
|
null,
|
|
true
|
|
);
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
@CommonHelpers.FriendlyDate(Model.DeviceBatch.WarrantyValidUntil, "Unknown")
|
|
}
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<div id="DeviceBatch_WarrantyDetails_Container" data-updateurl="@(Url.Action(MVC.API.DeviceBatch.UpdateWarrantyDetails(Model.DeviceBatch.Id)))">
|
|
<div>
|
|
Details @AjaxHelpers.AjaxLoader("ajaxWarrantyDetails")
|
|
</div>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.WarrantyDetails)
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
const $field = $('#DeviceBatch_WarrantyDetails');
|
|
|
|
async function updated() {
|
|
$('#ajaxWarrantyDetails_loading').show();
|
|
try {
|
|
const body = new FormData();
|
|
body.append('__RequestVerificationToken', document.body.dataset.antiforgery);
|
|
body.append('warrantyDetails', $field.tinymce().getContent())
|
|
|
|
const response = await fetch($('#DeviceBatch_WarrantyDetails_Container').attr('data-updateurl'), {
|
|
method: 'POST',
|
|
body: body
|
|
});
|
|
|
|
if (response.ok) {
|
|
$('#ajaxWarrantyDetails_ok').show().delay('fast').fadeOut('slow');
|
|
} else {
|
|
alert('Unable to update warranty details: ' + response.statusText);
|
|
}
|
|
} catch (e) {
|
|
alert('Unable to update warranty details: ' + e);
|
|
}
|
|
$('#ajaxWarrantyDetails_loading').hide();
|
|
}
|
|
|
|
$field.tinymce({
|
|
theme: 'simple',
|
|
add_unload_trigger: false,
|
|
schema: "html5",
|
|
statusbar: false,
|
|
setup: function (ed) {
|
|
ed.on('init', function () {
|
|
$(ed.getWin()).blur(function () { updated(); });
|
|
});
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
if (string.IsNullOrEmpty(Model.DeviceBatch.WarrantyDetails))
|
|
{<span class="smallMessage"><None Specified></span>}
|
|
else
|
|
{@(new HtmlString(Model.DeviceBatch.WarrantyDetails))}
|
|
}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>
|
|
Insurance:
|
|
</th>
|
|
<td class="details">
|
|
<table class="sub">
|
|
<tr>
|
|
<th class="name" style="width: 100px">
|
|
Supplier:
|
|
</th>
|
|
<td>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.InsuranceSupplier)
|
|
@AjaxHelpers.AjaxSave()
|
|
@AjaxHelpers.AjaxLoader()
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
document.DiscoFunctions.PropertyChangeHelper(
|
|
$('#DeviceBatch_InsuranceSupplier'),
|
|
'Insurance Supplier',
|
|
'@(Url.Action(MVC.API.DeviceBatch.UpdateInsuranceSupplier(Model.DeviceBatch.Id)))',
|
|
'InsuranceSupplier'
|
|
);
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
if (string.IsNullOrEmpty(Model.DeviceBatch.InsuranceSupplier))
|
|
{<span class="smallMessage"><None Specified></span>}
|
|
else
|
|
{@Model.DeviceBatch.InsuranceSupplier;
|
|
}
|
|
}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th class="name">
|
|
Insured Date:
|
|
</th>
|
|
<td>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.InsuredDate)
|
|
@AjaxHelpers.AjaxLoader()
|
|
<script>
|
|
$(function () {
|
|
var dateField = $('#DeviceBatch_InsuredDate');
|
|
document.DiscoFunctions.DateChangeHelper(
|
|
dateField,
|
|
'Insured Date',
|
|
'@(Url.Action(MVC.API.DeviceBatch.UpdateInsuredDate(Model.DeviceBatch.Id)))',
|
|
'InsuredDate',
|
|
null,
|
|
true
|
|
);
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
@CommonHelpers.FriendlyDate(Model.DeviceBatch.InsuredDate, "Unknown")
|
|
}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th class="name">
|
|
Insured Until:
|
|
</th>
|
|
<td>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.InsuredUntil)
|
|
@AjaxHelpers.AjaxLoader()
|
|
<script>
|
|
$(function () {
|
|
var dateField = $('#DeviceBatch_InsuredUntil');
|
|
document.DiscoFunctions.DateChangeHelper(
|
|
dateField,
|
|
'Insured Until',
|
|
'@(Url.Action(MVC.API.DeviceBatch.UpdateInsuredUntil(Model.DeviceBatch.Id)))',
|
|
'InsuredUntil',
|
|
null,
|
|
true
|
|
);
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
@CommonHelpers.FriendlyDate(Model.DeviceBatch.InsuredUntil, "Unknown")
|
|
}
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
<div id="DeviceBatch_InsuranceDetails_Container" data-updateurl="@(Url.Action(MVC.API.DeviceBatch.UpdateInsuranceDetails(Model.DeviceBatch.Id)))">
|
|
<div>
|
|
Details @AjaxHelpers.AjaxLoader("ajaxInsuranceDetails")
|
|
</div>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.InsuranceDetails)
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
const $field = $('#DeviceBatch_InsuranceDetails');
|
|
async function updated() {
|
|
$('#ajaxInsuranceDetails_loading').show();
|
|
try {
|
|
const body = new FormData();
|
|
body.append('__RequestVerificationToken', document.body.dataset.antiforgery);
|
|
body.append('insuranceDetails', $field.tinymce().getContent())
|
|
|
|
const response = await fetch($('#DeviceBatch_InsuranceDetails_Container').attr('data-updateurl'), {
|
|
method: 'POST',
|
|
body: body
|
|
});
|
|
|
|
if (response.ok) {
|
|
$('#ajaxInsuranceDetails_ok').show().delay('fast').fadeOut('slow');
|
|
} else {
|
|
alert('Unable to update insurance details: ' + response.statusText);
|
|
}
|
|
} catch (e) {
|
|
alert('Unable to update insurance details: ' + e);
|
|
}
|
|
$('#ajaxInsuranceDetails_loading').hide();
|
|
}
|
|
|
|
$field.tinymce({
|
|
theme: 'simple',
|
|
add_unload_trigger: false,
|
|
schema: "html5",
|
|
statusbar: false,
|
|
setup: function (ed) {
|
|
ed.on('init', function () {
|
|
$(ed.getWin()).blur(function () { updated(); });
|
|
});
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
if (string.IsNullOrEmpty(Model.DeviceBatch.InsuranceDetails))
|
|
{<span class="smallMessage"><None Specified></span>}
|
|
else
|
|
{@(new HtmlString(Model.DeviceBatch.InsuranceDetails))}
|
|
}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<tr id="DeviceBatch_Comments_Container" data-updateurl="@(Url.Action(MVC.API.DeviceBatch.UpdateComments(Model.DeviceBatch.Id)))">
|
|
<th>
|
|
Comments:<br />
|
|
@AjaxHelpers.AjaxLoader("ajaxComments")
|
|
</th>
|
|
<td>
|
|
@if (canConfig)
|
|
{
|
|
@Html.EditorFor(model => model.DeviceBatch.Comments)
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
const $field = $('#DeviceBatch_Comments');
|
|
async function updated() {
|
|
$('#ajaxComments_loading').show();
|
|
try {
|
|
const body = new FormData();
|
|
body.append('__RequestVerificationToken', document.body.dataset.antiforgery);
|
|
body.append('comments', $field.tinymce().getContent())
|
|
|
|
const response = await fetch($('#DeviceBatch_Comments_Container').attr('data-updateurl'), {
|
|
method: 'POST',
|
|
body: body
|
|
});
|
|
|
|
if (response.ok) {
|
|
$('#ajaxComments_ok').show().delay('fast').fadeOut('slow');
|
|
} else {
|
|
alert('Unable to update comments: ' + response.statusText);
|
|
}
|
|
} catch (e) {
|
|
alert('Unable to update comments: ' + e);
|
|
}
|
|
$('#ajaxComments_loading').hide();
|
|
}
|
|
|
|
$field.tinymce({
|
|
theme: 'simple',
|
|
add_unload_trigger: false,
|
|
schema: "html5",
|
|
statusbar: false,
|
|
setup: function (ed) {
|
|
ed.on('init', function () {
|
|
$(ed.getWin()).blur(function () { updated(); });
|
|
});
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
}
|
|
else
|
|
{
|
|
if (string.IsNullOrEmpty(Model.DeviceBatch.Comments))
|
|
{<span class="smallMessage"><None Specified></span>}
|
|
else
|
|
{@(new HtmlString(Model.DeviceBatch.Comments))}
|
|
}
|
|
</td>
|
|
</tr>
|
|
<tr>
|
|
<th>Attachments:</th>
|
|
<td>
|
|
<div id="DeviceBatch_Attachments" class="@(canConfig ? "canAddAttachments" : "cannotAddAttachments")" data-uploadurl="@(Url.Action(MVC.API.DeviceBatch.AttachmentUpload(Model.DeviceBatch.Id, null)))" data-removeurl="@Url.Action(MVC.API.DeviceBatch.AttachmentRemove())">
|
|
<div class="Disco-AttachmentUpload-DropTarget">
|
|
<h2>Drop Attachments Here</h2>
|
|
</div>
|
|
<div class="attachmentOutput">
|
|
@if (Model.DeviceBatch.DeviceBatchAttachments != null)
|
|
{
|
|
foreach (var attachment in Model.DeviceBatch.DeviceBatchAttachments)
|
|
{
|
|
<a href="@Url.Action(MVC.API.DeviceBatch.AttachmentDownload(attachment.Id))" data-attachmentid="@attachment.Id" data-mimetype="@attachment.MimeType">
|
|
<span class="icon" title="@attachment.Filename">
|
|
<img alt="Attachment Thumbnail" src="@(Url.Action(MVC.API.DeviceBatch.AttachmentThumbnail(attachment.Id)))" />
|
|
</span>
|
|
<span class="comments" title="@attachment.Comments">
|
|
@(attachment.Comments ?? attachment.Filename)
|
|
</span><span class="author">@attachment.TechUser.ToString()</span>@if (canConfig)
|
|
{<text><span class="remove fa fa-times-circle"></span></text>}<span class="timestamp" title="@attachment.Timestamp.ToFullDateTime()" data-livestamp="@attachment.Timestamp.ToUnixEpoc()">@attachment.Timestamp.ToFullDateTime()</span>
|
|
</a>
|
|
}
|
|
}
|
|
</div>
|
|
@if (canConfig)
|
|
{
|
|
<div class="Disco-AttachmentUpload-Progress"></div>
|
|
<div class="attachmentInput clearfix">
|
|
<span class="action enabled upload fa fa-upload disabled" title="Attach File"></span><span class="action enabled photo fa fa-camera disabled" title="Capture Image"></span>
|
|
</div>
|
|
<div id="dialogRemoveAttachment" class="dialog" title="Remove this Attachment?">
|
|
<p>
|
|
<i class="fa fa-exclamation-triangle fa-lg"></i> Are you sure?
|
|
</p>
|
|
</div>
|
|
}
|
|
<script type="text/javascript">
|
|
Shadowbox.init({
|
|
skipSetup: true,
|
|
modal: true
|
|
});
|
|
$(function () {
|
|
var $Attachments = $('#DeviceBatch_Attachments');
|
|
var $attachmentOutput = $Attachments.find('.attachmentOutput');
|
|
var $attachmentDownloadHost;
|
|
|
|
var $dialogRemoveAttachment = null;
|
|
|
|
// Connect to Hub
|
|
var hub = $.connection.deviceBatchUpdates;
|
|
|
|
// Map Functions
|
|
hub.client.addAttachment = onAddAttachment;
|
|
hub.client.removeAttachment = onRemoveAttachment;
|
|
|
|
$.connection.hub.qs = { DeviceBatchId: '@(Model.DeviceBatch.Id)' };
|
|
|
|
$.connection.hub.reconnecting(function () {
|
|
$Attachments.find('span.action.enabled').addClass('disabled');
|
|
});
|
|
$.connection.hub.reconnected(function () {
|
|
$Attachments.find('span.action.enabled').removeClass('disabled');
|
|
});
|
|
$.connection.hub.error(function (error) {
|
|
console.log('Server connection error: ' + error);
|
|
});
|
|
$.connection.hub.disconnected(function () {
|
|
// Disable UI
|
|
$Attachments.find('span.action.enabled').addClass('disabled');
|
|
});
|
|
|
|
// Start Connection
|
|
$.connection.hub.start(function () {
|
|
$Attachments.find('span.action.enabled').removeClass('disabled');
|
|
});
|
|
|
|
function onAddAttachment(id, quick) {
|
|
var data = { id: id };
|
|
$.ajax({
|
|
url: '@Url.Action(MVC.API.DeviceBatch.Attachment())',
|
|
dataType: 'json',
|
|
data: data,
|
|
success: function (d) {
|
|
if (d.Result == 'OK') {
|
|
var a = d.Attachment;
|
|
@if (canConfig)
|
|
{
|
|
<text>buildAttachment(a, true, quick);</text>
|
|
}
|
|
else
|
|
{
|
|
<text>buildAttachment(a, false, quick);</text>
|
|
}
|
|
} else {
|
|
alert('Unable to add attachment: ' + d.Result);
|
|
}
|
|
},
|
|
error: function (jqXHR, textStatus, errorThrown) {
|
|
alert('Unable to add attachment: ' + textStatus);
|
|
}
|
|
});
|
|
}
|
|
|
|
function buildAttachment(a, canRemove, quick) {
|
|
var t = '<a><span class="icon"><img alt="Attachment Thumbnail" /></span><span class="comments"></span><span class="author"></span>';
|
|
if (canRemove)
|
|
t += '<span class="remove fa fa-times-circle"></span>';
|
|
t += '<span class="timestamp"></span></a>';
|
|
|
|
var e = $(t);
|
|
|
|
e.attr('data-attachmentid', a.Id).attr('data-mimetype', a.MimeType).attr('href', '@(Url.Action(MVC.API.DeviceBatch.AttachmentDownload()))/' + a.Id);
|
|
e.find('.comments').text(a.Description);
|
|
e.find('.author').text(a.Author);
|
|
e.find('.timestamp').text(a.TimestampFull).attr('title', a.TimestampFull).livestamp(a.TimestampUnixEpoc);
|
|
if (canRemove)
|
|
e.find('.remove').click(removeAttachment);
|
|
if (!quick)
|
|
e.hide();
|
|
$attachmentOutput.append(e);
|
|
if (!quick)
|
|
e.show('slow');
|
|
if (a.MimeType.toLowerCase().indexOf('image/') == 0)
|
|
e.shadowbox({ gallery: 'attachments', player: 'img', title: a.Description });
|
|
else
|
|
e.click(onDownload);
|
|
|
|
// Add Thumbnail
|
|
var buildThumbnail = function () {
|
|
var retryCount = 0;
|
|
var img = e.find('.icon img');
|
|
|
|
var setThumbnailUrl = function () {
|
|
img.attr('src', '@(Url.Action(MVC.API.DeviceBatch.AttachmentThumbnail()))/' + a.Id + '?v=' + retryCount);
|
|
};
|
|
img.on('error', function () {
|
|
img.addClass('loading');
|
|
retryCount++;
|
|
if (retryCount < 6)
|
|
window.setTimeout(setThumbnailUrl, retryCount * 250);
|
|
});
|
|
img.on('load', function () {
|
|
img.removeClass('loading');
|
|
});
|
|
window.setTimeout(setThumbnailUrl, 100);
|
|
};
|
|
buildThumbnail();
|
|
}
|
|
|
|
function onRemoveAttachment(id) {
|
|
var a = $attachmentOutput.find('a[data-attachmentid=' + id + ']');
|
|
|
|
a.hide(300).delay(300).queue(function () {
|
|
var $this = $(this);
|
|
if ($this.attr('data-mimetype').toLowerCase().indexOf('image/') == 0)
|
|
Shadowbox.removeCache(this);
|
|
$this.find('.timestamp').livestamp('destroy');
|
|
$this.remove();
|
|
});
|
|
}
|
|
|
|
function onDownload() {
|
|
var $this = $(this);
|
|
var url = $this.attr('href');
|
|
|
|
if ($.connection && $.connection.hub && $.connection.hub.transport &&
|
|
$.connection.hub.transport.name == 'foreverFrame') {
|
|
// SignalR active with foreverFrame transport - use popup window
|
|
window.open(url, '_blank', 'height=150,width=250,location=no,menubar=no,resizable=no,scrollbars=no,status=no,toolbar=no');
|
|
} else {
|
|
// use iFrame
|
|
if (!$attachmentDownloadHost) {
|
|
$attachmentDownloadHost = $('<iframe>')
|
|
.attr({ 'src': url, 'title': 'Attachment Download Host' })
|
|
.addClass('hidden')
|
|
.appendTo('body')
|
|
.contents();
|
|
} else {
|
|
$attachmentDownloadHost[0].location.href = url;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@if (canConfig)
|
|
{<text>
|
|
//#region Add Attachments
|
|
var attachmentUploader = new document.Disco.AttachmentUploader($Attachments);
|
|
|
|
var $attachmentInput = $Attachments.find('.attachmentInput');
|
|
if (window.location.protocol != 'https:') {
|
|
$attachmentInput.find('.photo')
|
|
.removeClass('enabled')
|
|
.addClass('disabled')
|
|
.attr('title', 'Capture Image: this functionality is only available over a HTTPS connection');
|
|
}
|
|
$attachmentInput.find('.photo').click(function () {
|
|
if (!$(this).hasClass('enabled'))
|
|
alert('This functionality is only available over a HTTPS connection');
|
|
else if ($(this).hasClass('disabled'))
|
|
alert('Disconnected from the Disco ICT Server, please refresh this page and try again');
|
|
else
|
|
attachmentUploader.uploadImage();
|
|
});
|
|
$attachmentInput.find('.upload').click(function () {
|
|
if ($(this).hasClass('disabled'))
|
|
alert('Disconnected from the Disco ICT Server, please refresh this page and try again');
|
|
else
|
|
attachmentUploader.uploadFiles();
|
|
});
|
|
//#endregion
|
|
//#region Remove Attachments
|
|
$attachmentOutput.find('span.remove').on('click', removeAttachment);
|
|
|
|
function removeAttachment() {
|
|
const attachmentId = $(this).closest('a').attr('data-attachmentid');
|
|
|
|
if (!$dialogRemoveAttachment) {
|
|
$dialogRemoveAttachment = $('#dialogRemoveAttachment').dialog({
|
|
resizable: false,
|
|
height: 140,
|
|
modal: true,
|
|
autoOpen: false
|
|
});
|
|
}
|
|
|
|
$dialogRemoveAttachment.dialog('option', 'buttons', {
|
|
"Remove": function () {
|
|
$dialogRemoveAttachment.dialog("option", "buttons", null);
|
|
|
|
const body = new FormData();
|
|
body.append('__RequestVerificationToken', document.body.dataset.antiforgery);
|
|
body.append('id', attachmentId);
|
|
fetch($Attachments.attr('data-removeurl'), {
|
|
method: 'POST',
|
|
body: body
|
|
}).then(r => {
|
|
if (!r.ok) {
|
|
alert('Unable to remove attachment: ' + r.statusText);
|
|
}
|
|
$dialogRemoveAttachment.dialog('close');
|
|
}).catch(e => {
|
|
alert('Unable to remove attachment: ' + e);
|
|
$dialogRemoveAttachment.dialog('close');
|
|
});
|
|
},
|
|
Cancel: function () {
|
|
$(this).dialog("close");
|
|
}
|
|
});
|
|
|
|
$dialogRemoveAttachment.dialog('open');
|
|
|
|
return false;
|
|
}
|
|
//#endregion
|
|
</text>}
|
|
|
|
$attachmentOutput.children('a').each(function () {
|
|
$this = $(this);
|
|
if ($this.attr('data-mimetype').toLowerCase().indexOf('image/') == 0)
|
|
$this.shadowbox({ gallery: 'attachments', player: 'img', title: $this.find('.comments').text() });
|
|
else
|
|
$this.click(onDownload);
|
|
});
|
|
});
|
|
</script>
|
|
</div>
|
|
|
|
</td>
|
|
</tr>
|
|
@if (hideAdvanced)
|
|
{
|
|
<tr>
|
|
<td colspan="2" style="text-align: right;">
|
|
<button id="Config_HideAdvanced_Show" class="button small">Show Advanced Options</button>
|
|
<script>
|
|
$(function () {
|
|
$('#Config_HideAdvanced_Show').click(function () {
|
|
var $this = $(this);
|
|
$this.closest('.Config_HideAdvanced').removeClass('Config_HideAdvanced');
|
|
$this.closest('tr').remove();
|
|
});
|
|
});
|
|
</script>
|
|
</td>
|
|
</tr>
|
|
}
|
|
<tr class="Config_HideAdvanced_Item">
|
|
<th>
|
|
Linked Groups:
|
|
</th>
|
|
<td>
|
|
<div>
|
|
@Html.Partial(MVC.Config.Shared.Views.LinkedGroupInstance, new LinkedGroupModel()
|
|
{
|
|
CanConfigure = canConfig,
|
|
CategoryDescription = DeviceBatchDevicesManagedGroup.GetCategoryDescription(Model.DeviceBatch),
|
|
Description = DeviceBatchDevicesManagedGroup.GetDescription(Model.DeviceBatch),
|
|
ManagedGroup = Model.DevicesLinkedGroup,
|
|
UpdateUrl = Url.Action(MVC.API.DeviceBatch.UpdateDevicesLinkedGroup(Model.DeviceBatch.Id, redirect: true))
|
|
})
|
|
@Html.Partial(MVC.Config.Shared.Views.LinkedGroupInstance, new LinkedGroupModel()
|
|
{
|
|
CanConfigure = canConfig,
|
|
CategoryDescription = DeviceBatchAssignedUsersManagedGroup.GetCategoryDescription(Model.DeviceBatch),
|
|
Description = DeviceBatchAssignedUsersManagedGroup.GetDescription(Model.DeviceBatch),
|
|
ManagedGroup = Model.AssignedUsersLinkedGroup,
|
|
UpdateUrl = Url.Action(MVC.API.DeviceBatch.UpdateAssignedUsersLinkedGroup(Model.DeviceBatch.Id, redirect: true))
|
|
})
|
|
@if (canConfig)
|
|
{
|
|
@Html.Partial(MVC.Config.Shared.Views.LinkedGroupShared)
|
|
}
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
</div>
|
|
@Html.Partial(MVC.Config.Shared.Views._DeviceGroupDocumentBulkGenerate, Model)
|
|
<div class="actionBar">
|
|
@if (Model.CanDecommission)
|
|
{
|
|
<button id="DeviceBatch_Decommission" class="button">Decommission All Devices</button>
|
|
<div id="DeviceBatch_Decommission_Dialog" class="dialog" title="Batch Device Decommission">
|
|
@using (Html.BeginForm(MVC.API.Device.DeviceBatchDecommission(Model.DeviceBatch.Id)))
|
|
{
|
|
@Html.AntiForgeryToken()
|
|
<div class="clearfix" style="margin-bottom: 10px;">
|
|
<i class="fa fa-question-circle fa-lg information"></i> Why are these devices to be decommissioned?
|
|
</div>
|
|
<div>
|
|
<ul class="none">
|
|
@foreach (DecommissionReasons decommissionReason in Enum.GetValues(typeof(DecommissionReasons)).Cast<DecommissionReasons>().OrderBy(r => r.ToString()))
|
|
{
|
|
<li>
|
|
<input type="radio" id="DeviceBatch_Decommission_Dialog_Reason_@((int)decommissionReason)"
|
|
name="decommissionReason" value="@((int)decommissionReason)" @((decommissionReason == DecommissionReasons.EndOfLife) ? "checked=\"checked\"" : string.Empty) />
|
|
<label for="DeviceBatch_Decommission_Dialog_Reason_@((int)decommissionReason)">@(decommissionReason.ReasonMessage())</label>
|
|
</li>
|
|
}
|
|
</ul>
|
|
<br />
|
|
<label>
|
|
<input type="checkbox" value="true" name="unassignUsers" />
|
|
Unassign devices users
|
|
</label>
|
|
</div>
|
|
}
|
|
</div>
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
let buttonDialog = null;
|
|
$('#DeviceBatch_Decommission').click(function () {
|
|
if (!buttonDialog) {
|
|
buttonDialog = $('#DeviceBatch_Decommission_Dialog')
|
|
.dialog({
|
|
resizable: false,
|
|
modal: true,
|
|
autoOpen: false,
|
|
buttons: {
|
|
"Decommission": function () {
|
|
const $this = $(this);
|
|
$this.find('form').trigger('submit');
|
|
$this.dialog("disable");
|
|
$this.dialog("option", "buttons", null);
|
|
},
|
|
Cancel: function () {
|
|
$(this).dialog("close");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
buttonDialog.dialog('open');
|
|
return false;
|
|
});
|
|
|
|
});
|
|
</script>
|
|
}
|
|
@if (Model.CanDelete)
|
|
{
|
|
<button id="buttonDelete" type="button" class="button">Delete</button>
|
|
<div id="dialogConfirmDelete" class="dialog" title="Delete this Device Batch?">
|
|
@using (Html.BeginForm(MVC.API.DeviceBatch.Delete(Model.DeviceBatch.Id, true)))
|
|
{
|
|
@Html.AntiForgeryToken()
|
|
}
|
|
<p>
|
|
<i class="fa fa-exclamation-triangle fa-lg warning"></i>
|
|
This item will be permanently deleted and cannot be recovered. Are you sure?
|
|
</p>
|
|
</div>
|
|
<script type="text/javascript">
|
|
$(function () {
|
|
let dialog = null;
|
|
$('#buttonDelete').on('click', function () {
|
|
if (!dialog) {
|
|
dialog = $("#dialogConfirmDelete").dialog({
|
|
resizable: false,
|
|
width: 300,
|
|
modal: true,
|
|
autoOpen: false,
|
|
buttons: {
|
|
"Delete": function () {
|
|
$(this)
|
|
.dialog("option", "buttons", null)
|
|
.find('form').trigger('submit');
|
|
},
|
|
Cancel: function () {
|
|
$(this).dialog("close");
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
dialog.dialog('open');
|
|
});
|
|
});
|
|
</script>
|
|
}
|
|
@if (Model.DeviceCount > 0)
|
|
{
|
|
if (Authorization.Has(Claims.Device.Actions.Export))
|
|
{
|
|
@Html.ActionLinkButton("Export Devices", MVC.Device.Export(null, Disco.Models.Services.Devices.DeviceExportTypes.Batch, Model.DeviceBatch.Id))
|
|
}
|
|
if (Authorization.Has(Claims.Device.Search) && Model.DeviceCount > 0)
|
|
{
|
|
@Html.ActionLinkButton(string.Format("View {0} Device{1}", Model.DeviceCount, (Model.DeviceCount != 1 ? "s" : null)), MVC.Search.Query(Model.DeviceBatch.Id.ToString(), "DeviceBatch"))
|
|
}
|
|
}
|
|
</div>
|