inspection record template editor

This commit is contained in:
DESKTOP-T0O5CDB\DESK-555BD 2025-10-31 20:00:51 -06:00
parent 20365a549a
commit 042985df54
9 changed files with 861 additions and 4 deletions

View File

@ -0,0 +1,151 @@
using CarCareTracker.Filter;
using CarCareTracker.Helper;
using CarCareTracker.Models;
using Microsoft.AspNetCore.Mvc;
namespace CarCareTracker.Controllers
{
public partial class VehicleController
{
[TypeFilter(typeof(CollaboratorFilter))]
[HttpGet]
public IActionResult GetInspectionRecordTemplatesByVehicleId(int vehicleId)
{
var result = _inspectionRecordTemplateDataAccess.GetInspectionRecordTemplatesByVehicleId(vehicleId);
return PartialView("Inspection/_InspectionRecordTemplateSelector", result);
}
[TypeFilter(typeof(CollaboratorFilter))]
[HttpGet]
public IActionResult GetInspectionRecordsByVehicleId(int vehicleId)
{
var result = _inspectionRecordDataAccess.GetInspectionRecordsByVehicleId(vehicleId);
bool _useDescending = _config.GetUserConfig(User).UseDescending;
if (_useDescending)
{
result = result.OrderByDescending(x => x.Date).ThenByDescending(x => x.Mileage).ToList();
}
else
{
result = result.OrderBy(x => x.Date).ThenBy(x => x.Mileage).ToList();
}
return PartialView("Inspection/_InspectionRecords", result);
}
[HttpGet]
public IActionResult GetAddInspectionRecordTemplatePartialView()
{
return PartialView("Inspection/_InspectionRecordTemplateEditModal", new InspectionRecordInput());
}
[HttpGet]
public IActionResult GetEditInspectionRecordTemplatePartialView(int inspectionRecordTemplateId)
{
var result = _inspectionRecordTemplateDataAccess.GetInspectionRecordTemplateById(inspectionRecordTemplateId);
//security check.
if (!_userLogic.UserCanEditVehicle(GetUserID(), result.VehicleId))
{
return Redirect("/Error/Unauthorized");
}
return PartialView("Inspection/_InspectionRecordTemplateEditModal", result);
}
[HttpGet]
public IActionResult GetAddInspectionRecordFieldPartialView()
{
return PartialView("Inspection/_InspectionRecordField", new InspectionRecordTemplateField());
}
public IActionResult GetAddInspectionRecordFieldOptionsPartialView()
{
return PartialView("Inspection/_InspectionRecordFieldOptions", new List<InspectionRecordTemplateFieldOption>());
}
public IActionResult GetAddInspectionRecordFieldOptionPartialView()
{
return PartialView("Inspection/_InspectionRecordFieldOption", new InspectionRecordTemplateFieldOption());
}
[HttpPost]
public IActionResult SaveInspectionRecordTemplateToVehicleId(InspectionRecordInput inspectionRecordTemplate)
{
//security check.
if (!_userLogic.UserCanEditVehicle(GetUserID(), inspectionRecordTemplate.VehicleId))
{
return Json(false);
}
var result = _inspectionRecordTemplateDataAccess.SaveInspectionReportTemplateToVehicle(inspectionRecordTemplate);
return Json(result);
}
[HttpGet]
public IActionResult GetAddInspectionRecordPartialView(int inspectionRecordTemplateId)
{
var result = _inspectionRecordTemplateDataAccess.GetInspectionRecordTemplateById(inspectionRecordTemplateId);
//security check.
if (!_userLogic.UserCanEditVehicle(GetUserID(), result.VehicleId))
{
return Redirect("/Error/Unauthorized");
}
return PartialView("Inspection/_InspectionRecordModal", result);
}
//[HttpGet]
//public IActionResult GetAddInspectionRecordPartialView()
//{
// var extraFields = _extraFieldDataAccess.GetExtraFieldsById((int)ImportMode.NoteRecord).ExtraFields;
// return PartialView("Note/_NoteModal", new Note() { ExtraFields = extraFields });
//}
//[HttpGet]
//public IActionResult GetAddInspectionRecordTemplatePartialView()
//{
// var extraFields = _extraFieldDataAccess.GetExtraFieldsById((int)ImportMode.NoteRecord).ExtraFields;
// return PartialView("Note/_NoteModal", new Note() { ExtraFields = extraFields });
//}
//[HttpGet]
//public IActionResult GetInspectionRecordTemplateForEditById(int noteId)
//{
// var result = _noteDataAccess.GetNoteById(noteId);
// result.ExtraFields = StaticHelper.AddExtraFields(result.ExtraFields, _extraFieldDataAccess.GetExtraFieldsById((int)ImportMode.NoteRecord).ExtraFields);
// //security check.
// if (!_userLogic.UserCanEditVehicle(GetUserID(), result.VehicleId))
// {
// return Redirect("/Error/Unauthorized");
// }
// return PartialView("Note/_NoteModal", result);
//}
//private bool DeleteInspectionRecordWithChecks(int noteId)
//{
// var existingRecord = _noteDataAccess.GetNoteById(noteId);
// //security check.
// if (!_userLogic.UserCanEditVehicle(GetUserID(), existingRecord.VehicleId))
// {
// return false;
// }
// var result = _noteDataAccess.DeleteNoteById(existingRecord.Id);
// if (result)
// {
// StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromNoteRecord(existingRecord, "noterecord.delete", User.Identity.Name));
// }
// return result;
//}
//[HttpPost]
//public IActionResult DeleteInspectionRecordById(int noteId)
//{
// var result = DeleteInspectionRecordWithChecks(noteId);
// return Json(result);
//}
//private bool DeleteInspectionRecordTemplateWithChecks(int noteId)
//{
// var existingRecord = _noteDataAccess.GetNoteById(noteId);
// //security check.
// if (!_userLogic.UserCanEditVehicle(GetUserID(), existingRecord.VehicleId))
// {
// return false;
// }
// var result = _noteDataAccess.DeleteNoteById(existingRecord.Id);
// if (result)
// {
// StaticHelper.NotifyAsync(_config.GetWebHookUrl(), WebHookPayload.FromNoteRecord(existingRecord, "noterecord.delete", User.Identity.Name));
// }
// return result;
//}
//[HttpPost]
//public IActionResult DeleteInspectionRecordTemplateById(int noteId)
//{
// var result = DeleteInspectionRecordTemplateWithChecks(noteId);
// return Json(result);
//}
}
}

View File

@ -3,12 +3,13 @@
public class InspectionRecordTemplateField
{
public string Description { get; set; }
public InspectionFieldType FieldType { get; set; }
public InspectionFieldType FieldType { get; set; } = InspectionFieldType.Text;
public List<InspectionRecordTemplateFieldOption> Options { get; set; } = new List<InspectionRecordTemplateFieldOption>();
public ImportMode ActionItemType { get; set; }
public ImportMode ActionItemType { get; set; } = ImportMode.ServiceRecord;
public string ActionItemDescription { get; set; }
public PlanPriority ActionItemPriority { get; set; }
public PlanPriority ActionItemPriority { get; set; } = PlanPriority.Normal;
public bool HasNotes { get; set; }
public bool HasActionItem { get; set; }
public string Notes { get; set; }
public InspectionRecordResult ToInspectionRecordResult()
{
@ -16,7 +17,7 @@
{
Description = Description,
Values = Options.Where(x => x.IsSelected).Select(y => y.Description).ToList(),
Failed = Options.Any(x => x.IsSelected && x.IsFail),
Failed = (FieldType == InspectionFieldType.Radio && Options.Any(x => x.IsSelected && x.IsFail)) || (FieldType == InspectionFieldType.Check && Options.Any(x=> !x.IsSelected && x.IsFail)),
Notes = HasNotes ? Notes : string.Empty
};
}

View File

@ -0,0 +1,61 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model InspectionRecordTemplateField
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div data-type="field">
<label>@translator.Translate(userLanguage, "Description")</label>
<div class="d-flex justify-content-between align-items-center">
<input type="text" data-type="fieldDescription" class="form-control" placeholder="@translator.Translate(userLanguage, "Description of Inspected Item")" value="@Model.Description">
<button type="button" class="btn btn-outline-secondary btn-sm ms-2" onclick="duplicateInspectionRecordField(this)"><i class="bi bi-copy"></i></button>
<button type="button" class="btn btn-outline-danger btn-sm ms-2" onclick="deleteInspectionRecordField(this)"><i class="bi bi-trash"></i></button>
</div>
<label>@translator.Translate(userLanguage, "Type")</label>
<select class="form-select" data-type="fieldType" onchange="handleInspectionRecordFieldTypeChange(this)">
<!option value="Text" @(Model.FieldType == InspectionFieldType.Text ? "selected" : "")>@translator.Translate(userLanguage, "Text")</!option>
<!option value="Check" @(Model.FieldType == InspectionFieldType.Check ? "selected" : "")>@translator.Translate(userLanguage, "Check")</!option>
<!option value="Radio" @(Model.FieldType == InspectionFieldType.Radio ? "selected" : "")>@translator.Translate(userLanguage, "Radio")</!option>
</select>
<div data-type="fieldOptions">
@if (Model.Options.Any())
{
@switch (Model.FieldType)
{
case InspectionFieldType.Radio:
case InspectionFieldType.Check:
@await Html.PartialAsync("Inspection/_InspectionRecordFieldOptions", Model.Options)
break;
}
}
</div>
<div data-type="fieldActionItem" style="@(Model.FieldType == InspectionFieldType.Text ? "display:none;" : "")">
<div class="form-check form-check-inline">
<input class="form-check-input" data-type="fieldHasNotes" type="checkbox" role="switch" @(Model.HasNotes ? "checked" : "")>
<label class="form-check-label">@translator.Translate(userLanguage, "Has Notes")</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" data-type="fieldHasActionItem" type="checkbox" role="switch" onchange="handleInspectionRecordFieldHasActionItemChange(this)" @(Model.HasActionItem ? "checked" : "")>
<label class="form-check-label">@translator.Translate(userLanguage, "Has Action Item")</label>
</div>
<div class="collapse @(Model.HasActionItem ? "show" : "")" data-type="fieldActionItemContainer">
<label>@translator.Translate(userLanguage, "Action Item Description")</label>
<input type="text" data-type="fieldActionItemDescription" class="form-control" placeholder="@translator.Translate(userLanguage, "Description of Action Item")" value="@Model.ActionItemDescription">
<label>@translator.Translate(userLanguage, "Action Item Type")</label>
<select class="form-select" data-type="fieldActionItemType">
<!option value="ServiceRecord" @(Model.ActionItemType == ImportMode.ServiceRecord ? "selected" : "")>@translator.Translate(userLanguage, "Service")</!option>
<!option value="RepairRecord" @(Model.ActionItemType == ImportMode.RepairRecord ? "selected" : "")>@translator.Translate(userLanguage, "Repair")</!option>
<!option value="UpgradeRecord" @(Model.ActionItemType == ImportMode.UpgradeRecord ? "selected" : "")>@translator.Translate(userLanguage, "Upgrade")</!option>
</select>
<label>@translator.Translate(userLanguage, "Action Item Priority")</label>
<select class="form-select" data-type="fieldActionItemPriority">
<!option value="Critical" @(Model.ActionItemPriority == PlanPriority.Critical ? "selected" : "")>@translator.Translate(userLanguage, "Critical")</!option>
<!option value="Normal" @(Model.ActionItemPriority == PlanPriority.Normal ? "selected" : "")>@translator.Translate(userLanguage, "Normal")</!option>
<!option value="Low" @(Model.ActionItemPriority == PlanPriority.Low ? "selected" : "")>@translator.Translate(userLanguage, "Low")</!option>
</select>
</div>
</div>
<hr />
</div>

View File

@ -0,0 +1,16 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model InspectionRecordTemplateFieldOption
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div data-type="fieldOption" class="d-flex justify-content-between align-items-center mt-2">
<input type="text" data-type="fieldOptionText" class="form-control" value="@Model.Description">
<div class="ms-2 form-check form-check-inline">
<input class="form-check-input" data-type="fieldOptionIsFail" type="checkbox" role="switch" @(Model.IsFail ? "checked" : "")>
<label class="form-check-label">@translator.Translate(userLanguage, "Fail")</label>
</div>
<button type="button" class="btn btn-outline-danger btn-sm" onclick="deleteInspectionRecordFieldOption(this)"><i class="bi bi-trash"></i></button>
</div>

View File

@ -0,0 +1,15 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model List<InspectionRecordTemplateFieldOption>
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="d-grid p-2">
<button type="button" class="btn btn-outline-warning" onclick="addInspectionRecordFieldOption(this)">@translator.Translate(userLanguage, "Add Option")</button>
</div>
@foreach (InspectionRecordTemplateFieldOption fieldOption in Model)
{
@await Html.PartialAsync("Inspection/_InspectionRecordFieldOption", fieldOption)
}

View File

@ -0,0 +1,72 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model InspectionRecordInput
@{
var isNew = Model.Id == 0;
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="modal-header">
<h5 class="modal-title">@(isNew? translator.Translate(userLanguage, "Add New Inspection Record Template") : translator.Translate(userLanguage, "Edit Inspection Record Template"))<small style="display:none; @(isNew ? "" : "cursor:pointer;")" class="cached-banner ms-2 text-warning" onclick='@(isNew ? "" : $"showEditInspectionRecordTemplateModal({Model.Id}, true)")'>@translator.Translate(userLanguage, "Unsaved Changes")</small></h5>
<button type="button" class="btn-close" onclick="hideInspectionRecordTemplateModal()" aria-label="Close"></button>
</div>
<div class="modal-body" onkeydown="handleEnter(this)">
<form>
<div class="form-group">
<div class="row">
<div class="col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="inspectionRecordDescription">@translator.Translate(userLanguage, "Description")</label>
<input type="text" id="inspectionRecordDescription" class="form-control" placeholder="@translator.Translate(userLanguage, "Description of Inspection")" value="@Model.Description">
<div class="row">
<div class="col-12">
<a onclick="showRecurringReminderSelector('inspectionRecordDescription', 'inspectionRecordNotes')" class="btn btn-link">@translator.Translate(userLanguage, "Select Reminder")</a>
</div>
</div>
<div id="inspectionRecordFields" style="overflow-y:auto;">
@foreach (InspectionRecordTemplateField inspectionRecordField in Model.Fields)
{
@await Html.PartialAsync("Inspection/_InspectionRecordField", inspectionRecordField)
}
</div>
<div class="d-grid p-2">
<button type="button" class="btn btn-warning" onclick="addInspectionRecordField()">@translator.Translate(userLanguage, "Add Field")</button>
</div>
<label for="inspectionRecordTag">@translator.Translate(userLanguage, "Tags(optional)")</label>
<select multiple class="form-select" id="inspectionRecordTag">
@foreach (string tag in Model.Tags)
{
<!option value="@tag">@tag</!option>
}
</select>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
@if (!isNew)
{
<button type="button" class="btn btn-danger" onclick="deleteInspectionRecord(@Model.Id)">@translator.Translate(userLanguage, "Delete")</button>
}
<button type="button" class="btn btn-secondary" onclick="hideInspectionRecordTemplateModal()">@translator.Translate(userLanguage, "Cancel")</button>
@if (isNew)
{
<button type="button" class="btn btn-primary" onclick="saveInspectionRecordTemplateToVehicle()">@translator.Translate(userLanguage, "Add")</button>
}
else if (!isNew)
{
<button type="button" class="btn btn-primary" onclick="saveInspectionRecordTemplateToVehicle(true)">@translator.Translate(userLanguage, "Edit")</button>
}
</div>
<script>
var recurringReminderRecordId = [];
function getInspectionRecordModelData() {
return { id: @Model.Id}
}
@foreach (int reminderRecordId in Model.ReminderRecordId)
{
@:recurringReminderRecordId.push(@reminderRecordId);
}
</script>

View File

@ -0,0 +1,63 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model List<InspectionRecordInput>
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="modal-header">
<h5 class="modal-title">@(translator.Translate(userLanguage, "Select Inspection Record Template"))</h5>
<button type="button" class="btn-close" onclick="hideInspectionRecordTemplateSelectorModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
@if (Model.Any())
{
<div class="row">
<div class="col-12" style="max-height:50vh; overflow-y:auto;">
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-8">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Use")</th>
<th scope="col" class="col-2">@translator.Translate(userLanguage, "Edit")</th>
</tr>
</thead>
<tbody>
@foreach (InspectionRecordInput inspectionRecordTemplate in Model)
{
<tr class="d-flex" id="supplyRows">
<td class="col-8 text-truncate">
@StaticHelper.TruncateStrings(inspectionRecordTemplate.Description)
@if (inspectionRecordTemplate.ReminderRecordId.Any())
{
<i class="bi bi-bell ms-2"></i>
}
@if (inspectionRecordTemplate.Fields.Any(x=>x.HasActionItem))
{
<i class="bi bi-paperclip ms-2"></i>
}
</td>
<td class="col-2"><button type="button" class="btn btn-primary" onclick="useInspectionRecordTemplate(@inspectionRecordTemplate.Id)"><i class="bi bi-plus-square"></i></button></td>
<td class="col-2"><button type="button" class="btn btn-warning" onclick="showEditInspectionRecordTemplateModal(@inspectionRecordTemplate.Id)"><i class="bi bi-pencil-square"></i></button></td>
</tr>
}
</tbody>
</table>
</div>
</div>
}
else
{
<div class="row">
<div class="col-12">
<div class="text-center">
<h4>@translator.Translate(userLanguage, "No templates are found.")</h4>
</div>
</div>
</div>
}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" onclick="showAddInspectionRecordTemplateModal()">@translator.Translate(userLanguage, "Add New Inspection Template")</button>
</div>

View File

@ -0,0 +1,160 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model List<InspectionRecord>
@{
var recordTags = Model.SelectMany(x => x.Tags).Distinct();
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
var enableCsvImports = userConfig.EnableCsvImports;
var hideZero = userConfig.HideZero;
var userColumnPreferences = userConfig.UserColumnPreferences.Where(x => x.Tab == ImportMode.InspectionRecord);
}
<div class="row">
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap">
<span class="ms-2 badge bg-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage,"# of Inspection Records")}: {Model.Count()}")</span>
<span class="ms-2 badge bg-primary" data-aggregate-type="sum">@($"{translator.Translate(userLanguage, "Total")}: {Model.Sum(x => x.Cost).ToString("C")}")</span>
@foreach (string recordTag in recordTags)
{
<span onclick="filterTable('inspection-tab-pane', this)" class="user-select-none ms-2 rounded-pill badge bg-secondary tagfilter" style="cursor:pointer;">@recordTag</span>
}
<datalist id="tagList">
@foreach (string recordTag in recordTags)
{
<!option value="@recordTag"></!option>
}
</datalist>
</div>
<div>
@if (enableCsvImports)
{
<div class="btn-group">
<button onclick="showInspectionRecordTemplateSelectorModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage, "Add Inspection Record")</button>
<button type="button" class="btn btn-md btn-primary btn-md mt-1 mb-1 dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Toggle Dropdown</span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" onclick="printTab()">@translator.Translate(userLanguage, "Print")</a></li>
<li><a class="dropdown-item" href="#" onclick="searchTableRows('inspection-tab-pane')">@translator.Translate(userLanguage, "Search")</a></li>
<li><hr class="dropdown-divider"></li>
<li>
<div class="list-group-item">
<input class="btn-check" type="checkbox" id="chkSelectMode">
<label class="dropdown-item" for="chkSelectMode">@translator.Translate(userLanguage, "Select Mode")</label>
</div>
</li>
<li><hr class="dropdown-divider"></li>
<li><h6 class="dropdown-header">@translator.Translate(userLanguage, "Visible Columns")</h6></li>
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('InspectionRecord')">
<div class="list-group-item">
<input class="form-check-input col-visible-toggle" data-column-toggle='date' onChange="showTableColumns(this, 'InspectionRecord')" type="checkbox" id="chkCol_Date" checked>
<label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label>
</div>
</li>
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('InspectionRecord')">
<div class="list-group-item">
<input class="form-check-input col-visible-toggle" data-column-toggle='odometer' onChange="showTableColumns(this, 'InspectionRecord')" type="checkbox" id="chkCol_Odometer" checked>
<label class="form-check-label stretched-link" for="chkCol_Odometer">@translator.Translate(userLanguage, "Odometer")</label>
</div>
</li>
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('InspectionRecord')">
<div class="list-group-item">
<input class="form-check-input col-visible-toggle" data-column-toggle='description' onChange="showTableColumns(this, 'InspectionRecord')" type="checkbox" id="chkCol_Description" checked>
<label class="form-check-label stretched-link" for="chkCol_Description">@translator.Translate(userLanguage, "Description")</label>
</div>
</li>
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('InspectionRecord')">
<div class="list-group-item">
<input class="form-check-input col-visible-toggle" data-column-toggle='cost' onChange="showTableColumns(this, 'InspectionRecord')" type="checkbox" id="chkCol_Cost" checked>
<label class="form-check-label stretched-link" for="chkCol_Cost">@translator.Translate(userLanguage, "Cost")</label>
</div>
</li>
<li class="dropdown-item" draggable="true" ondragstart="handleTableColumnDragStart(event)" ondragover="handleTableColumnDragOver(event)" ondragend="handleTableColumnDragEnd('InspectionRecord')">
<div class="list-group-item">
<input class="form-check-input col-visible-toggle" data-column-toggle='attachments' onChange="showTableColumns(this, 'InspectionRecord')" type="checkbox" id="chkCol_Attachment">
<label class="form-check-label stretched-link" for="chkCol_Attachment">@translator.Translate(userLanguage, "Attachments")</label>
</div>
</li>
</ul>
</div>
}
else
{
<button onclick="showInspectionRecordTemplateSelectorModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>@translator.Translate(userLanguage, "Add Inspection Record")</button>
}
</div>
</div>
</div>
<div class="row vehicleDetailTabContainer">
<div class="col-12">
<div class="row mt-2 showOnPrint">
<div class="d-flex">
<img src="@config.GetLogoUrl()" class="lubelogger-logo" />
</div>
</div>
<table class="table table-hover">
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2 col-xl-1 flex-grow-1 text-truncate" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1 text-truncate" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 col-xl-4 flex-grow-1 flex-shrink-1 text-truncate" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1 text-truncate" data-column="cost" onclick="toggleSort('inspection-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-1 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="attachments">@translator.Translate(userLanguage, "Attachments")</th>
</tr>
</thead>
<tbody>
@foreach (InspectionRecord inspectionRecord in Model)
{
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@inspectionRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditInspectionRecordModal,@inspectionRecord.Id)" data-tags='@string.Join(" ", inspectionRecord.Tags)'>
<td class="col-2 col-xl-1 flex-grow-1 text-truncate" data-column="date" data-date="@StaticHelper.GetEpochFromDateTime(inspectionRecord.Date)">@inspectionRecord.Date.ToShortDateString()</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" data-column="odometer">@(inspectionRecord.Mileage == default ? "---" : inspectionRecord.Mileage.ToString())</td>
<td class="col-3 col-xl-4 flex-grow-1 flex-shrink-1 text-truncate" data-column="description">@inspectionRecord.Description</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" data-column="cost" data-record-type="cost">@(StaticHelper.HideZeroCost(inspectionRecord.Cost, hideZero))</td>
<td class="col-1 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="attachments">@await Html.PartialAsync("_AttachmentColumn", inspectionRecord.Files)</td>
</tr>
}
</tbody>
<tfoot>
<tr class="d-flex">
<td class="col-12 showOnPrint lubelogger-report-banner">
@StaticHelper.ReportNote
</td>
</tr>
</tfoot>
</table>
</div>
</div>
<div class="modal fade" data-bs-focus="false" id="inspectionRecordModal" tabindex="-1" role="dialog" aria-hidden="true" onpaste="handleModalPaste(event)">
<div class="modal-dialog" role="document">
<div class="modal-content" id="inspectionRecordModalContent">
</div>
</div>
</div>
<div class="modal fade" data-bs-focus="false" id="inspectionRecordTemplateModal" tabindex="-1" role="dialog" aria-hidden="true" onpaste="handleModalPaste(event)">
<div class="modal-dialog" role="document">
<div class="modal-content" id="inspectionRecordTemplateModalContent">
</div>
</div>
</div>
<div class="modal fade" data-bs-focus="false" id="inspectionRecordTemplateEditModal" tabindex="-1" role="dialog" aria-hidden="true" onpaste="handleModalPaste(event)">
<div class="modal-dialog" role="document">
<div class="modal-content" id="inspectionRecordTemplateEditModalContent">
</div>
</div>
</div>
<ul class="table-context-menu dropdown-menu" style="display:none;">
<li><a class="context-menu-multiple context-menu-select-all dropdown-item" href="#" onclick="selectAllRows()"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Select All")</span><i class="bi bi-check-square"></i></div></a></li>
<li><a class="context-menu-multiple context-menu-deselect-all dropdown-item" href="#" onclick="clearSelectedRows()"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Deselect All")</span><i class="bi bi-x-square"></i></div></a></li>
<li><hr class="context-menu-multiple dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="printTabStickers(selectedRow, 'InspectionRecord')"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Print")</span><i class="bi bi-printer"></i></div></a></li>
<li><hr class="dropdown-divider"></li>
<li><a class="dropdown-item text-danger" href="#" onclick="deleteRecords(selectedRow, 'InspectionRecord')"><div class="d-flex justify-content-between"><span class="me-5">@translator.Translate(userLanguage, "Delete")</span><i class="bi bi-trash"></i></div></a></li>
</ul>
@if (userColumnPreferences.Any())
{
@await Html.PartialAsync("_UserColumnPreferences", userColumnPreferences)
}

View File

@ -0,0 +1,318 @@
function showInspectionRecordTemplateSelectorModal() {
var vehicleId = GetVehicleId().vehicleId;
$.get(`/Vehicle/GetInspectionRecordTemplatesByVehicleId?vehicleId=${vehicleId}`, function (data) {
if (data) {
$("#inspectionRecordTemplateModalContent").html(data);
$('#inspectionRecordTemplateModal').modal('show');
}
});
}
function hideInspectionRecordTemplateSelectorModal() {
$('#inspectionRecordTemplateModal').modal('hide');
}
function showAddInspectionRecordTemplateModal() {
$.get('/Vehicle/GetAddInspectionRecordTemplatePartialView', function (data) {
if (data) {
$("#inspectionRecordTemplateEditModalContent").html(data);
//initiate datepicker
initDatePicker($('#inspectionRecordDate'));
initTagSelector($("#inspectionRecordTag"));
hideInspectionRecordTemplateSelectorModal();
$('#inspectionRecordTemplateEditModal').modal('show');
}
});
}
function showEditInspectionRecordTemplateModal(inspectionRecordTemplateId) {
$.get(`/Vehicle/GetEditInspectionRecordTemplatePartialView?inspectionRecordTemplateId=${inspectionRecordTemplateId}`, function (data) {
if (data) {
$("#inspectionRecordTemplateEditModalContent").html(data);
//initiate datepicker
initDatePicker($('#inspectionRecordDate'));
initTagSelector($("#inspectionRecordTag"));
hideInspectionRecordTemplateSelectorModal();
$('#inspectionRecordTemplateEditModal').modal('show');
}
});
}
function hideInspectionRecordTemplateModal() {
$('#inspectionRecordTemplateEditModal').modal('hide');
showInspectionRecordTemplateSelectorModal();
}
function addInspectionRecordField() {
$.get('/Vehicle/GetAddInspectionRecordFieldPartialView', function (data) {
$("#inspectionRecordFields").append(data);
});
}
function deleteInspectionRecordField(e) {
$(e).closest('[data-type="field"]').remove();
}
function duplicateInspectionRecordField(e) {
let clonedField = $(e).closest('[data-type="field"]').clone();
$("#inspectionRecordFields").append(clonedField);
}
function handleInspectionRecordFieldTypeChange(e) {
let selectedVal = $(e).val();
switch (selectedVal) {
case 'Radio':
case 'Check':
$.get('/Vehicle/GetAddInspectionRecordFieldOptionsPartialView', function (data) {
$(e).closest('[data-type="field"]').find('[data-type="fieldOptions"]').html(data);
});
$(e).closest('[data-type="field"]').find('[data-type="fieldActionItem"]').show();
break;
case 'Text':
$(e).closest('[data-type="field"]').find('[data-type="fieldOptions"]').html("");
$(e).closest('[data-type="field"]').find('[data-type="fieldActionItem"]').hide();
break;
}
}
function handleInspectionRecordFieldHasActionItemChange(e) {
if ($(e).is(":checked")) {
$(e).closest('[data-type="field"]').find('[data-type="fieldActionItemContainer"]').collapse('show');
} else {
$(e).closest('[data-type="field"]').find('[data-type="fieldActionItemContainer"]').collapse('hide');
}
}
function addInspectionRecordFieldOption(e) {
$.get('/Vehicle/GetAddInspectionRecordFieldOptionPartialView', function (data) {
$(e).closest('[data-type="field"]').find('[data-type="fieldOptions"]').append(data);
});
}
function deleteInspectionRecordFieldOption(e) {
$(e).closest('[data-type="fieldOption"]').remove();
}
function getAndValidateInspectionRecordTemplate() {
let hasError = false;
let inspectionDescription = $("#inspectionRecordDescription").val();
if (inspectionDescription.trim() == '') {
hasError = true;
$("#inspectionRecordDescription").addClass("is-invalid");
} else {
$("#inspectionRecordDescription").removeClass("is-invalid");
}
let inspectionTags = $("#inspectionRecordTag").val();
let inspectionRecordId = getInspectionRecordModelData().id;
let vehicleId = GetVehicleId().vehicleId;
let inspectionRecordTemplateData = {
id: inspectionRecordId,
vehicleId: vehicleId,
description: inspectionDescription,
tags: inspectionTags,
reminderRecordId: recurringReminderRecordId
}
let templateFields = [];
//process fields
$('#inspectionRecordFields > [data-type="field"]').map((index, elem) => {
let fieldElem = $(elem);
let hasActionItem = fieldElem.find('[data-type="fieldHasActionItem"]').is(":checked");
let fieldType = fieldElem.find('[data-type="fieldType"]').val();
let fieldDescriptionElem = fieldElem.find('[data-type="fieldDescription"]');
if (fieldDescriptionElem.val().trim() == '') {
hasError = true;
fieldDescriptionElem.addClass('is-invalid');
} else {
fieldDescriptionElem.removeClass('is-invalid');
}
let fieldData = {
description: fieldDescriptionElem.val(),
fieldType: fieldType,
hasNotes: fieldElem.find('[data-type="fieldHasNotes"]').is(":checked"),
hasActionItem: hasActionItem
};
if (hasActionItem) {
fieldData["actionItemType"] = fieldElem.find('[data-type="fieldActionItemType"]').val();
fieldData["actionItemPriority"] = fieldElem.find('[data-type="fieldActionItemPriority"]').val();
let actionItemDescriptionElem = fieldElem.find('[data-type="fieldActionItemDescription"]');
fieldData["actionItemDescription"] = actionItemDescriptionElem.val();
if (actionItemDescriptionElem.val().trim() == '') {
hasError = true;
actionItemDescriptionElem.addClass('is-invalid');
} else {
actionItemDescriptionElem.removeClass('is-invalid');
}
}
if (fieldType != 'Text') {
let fieldOptions = [];
fieldElem.find('[data-type="fieldOptions"]').find('[data-type="fieldOption"]').map((optionIndex, optionElem) => {
let fieldOptionElem = $(optionElem);
let fieldOptionTextElem = fieldOptionElem.find('[data-type="fieldOptionText"]');
if (fieldOptionTextElem.val().trim() == '') {
hasError = true;
fieldOptionTextElem.addClass('is-invalid');
} else {
fieldOptionTextElem.removeClass('is-invalid');
}
fieldOptions.push({
description: fieldOptionTextElem.val(),
isFail: fieldOptionElem.find('[data-type="fieldOptionIsFail"]').is(":checked")
});
});
fieldData["options"] = fieldOptions;
}
templateFields.push(fieldData);
});
inspectionRecordTemplateData["fields"] = templateFields;
inspectionRecordTemplateData["hasError"] = hasError;
return inspectionRecordTemplateData;
}
function saveInspectionRecordTemplateToVehicle(isEdit) {
let formValues = getAndValidateInspectionRecordTemplate();
if (formValues.hasError) {
errorToast("Please check the form data");
return;
}
$.post('/Vehicle/SaveInspectionRecordTemplateToVehicleId', { inspectionRecordTemplate: formValues }, function (data) {
if (data) {
successToast(isEdit ? "Inspection Record Template Updated" : "Inspection Record Template Added.");
hideInspectionRecordTemplateModal();
getVehicleCollisionRecords(formValues.vehicleId);
} else {
errorToast(genericErrorMessage());
}
})
}
//function showEditCollisionRecordModal(collisionRecordId, nocache) {
// if (!nocache) {
// var existingContent = $("#collisionRecordModalContent").html();
// if (existingContent.trim() != '') {
// //check if id is same.
// var existingId = getCollisionRecordModelData().id;
// if (existingId == collisionRecordId && $('[data-changed=true]').length > 0) {
// $('#collisionRecordModal').modal('show');
// $('.cached-banner').show();
// return;
// }
// }
// }
// $.get(`/Vehicle/GetCollisionRecordForEditById?collisionRecordId=${collisionRecordId}`, function (data) {
// if (data) {
// $("#collisionRecordModalContent").html(data);
// //initiate datepicker
// initDatePicker($('#collisionRecordDate'));
// initTagSelector($("#collisionRecordTag"));
// $('#collisionRecordModal').modal('show');
// bindModalInputChanges('collisionRecordModal');
// $('#collisionRecordModal').off('shown.bs.modal').on('shown.bs.modal', function () {
// if (getGlobalConfig().useMarkDown) {
// toggleMarkDownOverlay("collisionRecordNotes");
// }
// });
// }
// });
//}
//function hideAddCollisionRecordModal() {
// $('#collisionRecordModal').modal('hide');
//}
//function deleteCollisionRecord(collisionRecordId) {
// $("#workAroundInput").show();
// Swal.fire({
// title: "Confirm Deletion?",
// text: "Deleted Repair Records cannot be restored.",
// showCancelButton: true,
// confirmButtonText: "Delete",
// confirmButtonColor: "#dc3545"
// }).then((result) => {
// if (result.isConfirmed) {
// $.post(`/Vehicle/DeleteCollisionRecordById?collisionRecordId=${collisionRecordId}`, function (data) {
// if (data) {
// hideAddCollisionRecordModal();
// successToast("Repair Record Deleted");
// var vehicleId = GetVehicleId().vehicleId;
// getVehicleCollisionRecords(vehicleId);
// } else {
// errorToast(genericErrorMessage());
// }
// });
// } else {
// $("#workAroundInput").hide();
// }
// });
//}
//function saveCollisionRecordToVehicle(isEdit) {
// //get values
// var formValues = getAndValidateCollisionRecordValues();
// //validate
// if (formValues.hasError) {
// errorToast("Please check the form data");
// return;
// }
// //save to db.
// $.post('/Vehicle/SaveCollisionRecordToVehicleId', { collisionRecord: formValues }, function (data) {
// if (data) {
// successToast(isEdit ? "Repair Record Updated" : "Repair Record Added.");
// hideAddCollisionRecordModal();
// saveScrollPosition();
// getVehicleCollisionRecords(formValues.vehicleId);
// if (formValues.addReminderRecord) {
// setTimeout(function () { showAddReminderModal(formValues); }, 500);
// }
// } else {
// errorToast(genericErrorMessage());
// }
// })
//}
//function getAndValidateCollisionRecordValues() {
// var collisionDate = $("#collisionRecordDate").val();
// var collisionMileage = parseInt(globalParseFloat($("#collisionRecordMileage").val())).toString();
// var collisionDescription = $("#collisionRecordDescription").val();
// var collisionCost = $("#collisionRecordCost").val();
// var collisionNotes = $("#collisionRecordNotes").val();
// var collisionTags = $("#collisionRecordTag").val();
// var vehicleId = GetVehicleId().vehicleId;
// var collisionRecordId = getCollisionRecordModelData().id;
// var addReminderRecord = $("#addReminderCheck").is(":checked");
// //Odometer Adjustments
// if (isNaN(collisionMileage) && GetVehicleId().odometerOptional) {
// collisionMileage = '0';
// }
// collisionMileage = GetAdjustedOdometer(collisionRecordId, collisionMileage);
// //validation
// var hasError = false;
// var extraFields = getAndValidateExtraFields();
// if (extraFields.hasError) {
// hasError = true;
// }
// if (collisionDate.trim() == '') { //eliminates whitespace.
// hasError = true;
// $("#collisionRecordDate").addClass("is-invalid");
// } else {
// $("#collisionRecordDate").removeClass("is-invalid");
// }
// if (collisionMileage.trim() == '' || isNaN(collisionMileage) || parseInt(collisionMileage) < 0) {
// hasError = true;
// $("#collisionRecordMileage").addClass("is-invalid");
// } else {
// $("#collisionRecordMileage").removeClass("is-invalid");
// }
// if (collisionDescription.trim() == '') {
// hasError = true;
// $("#collisionRecordDescription").addClass("is-invalid");
// } else {
// $("#collisionRecordDescription").removeClass("is-invalid");
// }
// if (collisionCost.trim() == '' || !isValidMoney(collisionCost)) {
// hasError = true;
// $("#collisionRecordCost").addClass("is-invalid");
// } else {
// $("#collisionRecordCost").removeClass("is-invalid");
// }
// return {
// id: collisionRecordId,
// hasError: hasError,
// vehicleId: vehicleId,
// date: collisionDate,
// mileage: collisionMileage,
// description: collisionDescription,
// cost: collisionCost,
// notes: collisionNotes,
// files: uploadedFiles,
// supplies: selectedSupplies,
// tags: collisionTags,
// addReminderRecord: addReminderRecord,
// extraFields: extraFields.extraFields,
// requisitionHistory: supplyUsageHistory,
// deletedRequisitionHistory: deletedSupplyUsageHistory,
// reminderRecordId: recurringReminderRecordId,
// copySuppliesAttachment: copySuppliesAttachments
// }
//}