Added loader and potential scaffolding for adding file attachments

This commit is contained in:
ivancheahhh
2024-01-02 12:21:08 -07:00
parent e20a376a6c
commit 30df01ba96
10 changed files with 201 additions and 155 deletions

View File

@@ -74,9 +74,15 @@ namespace CarCareTracker.Controllers
return Json(result);
}
[HttpGet]
public IActionResult GetServiceRecordById(int serviceRecordId)
public IActionResult GetAddServiceRecordPartialView()
{
return PartialView("_ServiceRecordModal", new ServiceRecordInput());
}
[HttpGet]
public IActionResult GetServiceRecordForEditById(int serviceRecordId)
{
var result = _serviceRecordDataAccess.GetServiceRecordById(serviceRecordId);
//retrieve uploaded files.
//convert to Input object.
var convertedResult = new ServiceRecordInput { Id = result.Id,
Cost = result.Cost,
@@ -86,7 +92,7 @@ namespace CarCareTracker.Controllers
Notes = result.Notes,
VehicleId = result.VehicleId
};
return Json(convertedResult);
return PartialView("_ServiceRecordModal", convertedResult);
}
[HttpPost]
public IActionResult DeleteServiceRecordById(int serviceRecordId)

View File

@@ -1,14 +1,13 @@
namespace CarCareTracker.Models
{
public class ServiceRecordInput
public class ServiceRecord
{
public int Id { get; set; }
public int VehicleId { get; set; }
public string Date { get; set; }
public DateTime Date { get; set; }
public int Mileage { get; set; }
public string Description { get; set; }
public decimal Cost { get; set; }
public string Notes { get; set; }
public ServiceRecord ToServiceRecord() { return new ServiceRecord { Id = Id, VehicleId = VehicleId, Date = DateTime.Parse(Date), Cost = Cost, Mileage = Mileage, Description = Description, Notes = Notes }; }
}
}

View File

@@ -1,13 +1,15 @@
namespace CarCareTracker.Models
{
public class ServiceRecord
public class ServiceRecordInput
{
public int Id { get; set; }
public int VehicleId { get; set; }
public DateTime Date { get; set; }
public string Date { get; set; }
public int Mileage { get; set; }
public string Description { get; set; }
public decimal Cost { get; set; }
public string Notes { get; set; }
public List<string> Files { get; set; } = new List<string>();
public ServiceRecord ToServiceRecord() { return new ServiceRecord { Id = Id, VehicleId = VehicleId, Date = DateTime.Parse(Date), Cost = Cost, Mileage = Mileage, Description = Description, Notes = Notes }; }
}
}

View File

@@ -8,12 +8,14 @@
<link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap-icons.css" />
<link rel="stylesheet" href="~/lib/bootstrap-datepicker/css/bootstrap-datepicker.min.css" />
<link rel="stylesheet" href="~/css/site.css" asp-append-version="true" />
<link rel="stylesheet" href="~/css/loader.css" asp-append-version="true" />
<link rel="stylesheet" href="~/sweetalert/sweetalert2.min.css" asp-append-version="true" />
<script src="~/lib/jquery/dist/jquery.min.js"></script>
<script src="~/js/shared.js"></script>
<script src="~/lib/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script src="~/lib/bootstrap-datepicker/js/bootstrap-datepicker.min.js"></script>
<script src="~/sweetalert/sweetalert2.all.min.js"></script>
<script src="~/js/loader.js"></script>
@await RenderSectionAsync("Scripts", required: false)
</head>
<body>

View File

@@ -0,0 +1,89 @@
@model ServiceRecordInput
<div class="modal-header">
<h5 class="modal-title" id="addServiceRecordModalLabel">@(Model.Id == 0 ? "Add New Service Record" : "Edit Service Record")</h5>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<div class="row">
<div class="col-md-6 col-12">
<label for="serviceRecordDate">Date</label>
<div class="input-group">
<input type="text" id="serviceRecordDate" class="form-control" value="@Model.Date">
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div>
<label for="serviceRecordMileage">Mileage</label>
<input type="number" id="serviceRecordMileage" class="form-control" value="@Model.Mileage">
<label for="serviceRecordDescription">Description</label>
<input type="text" id="serviceRecordDescription" class="form-control" value="@Model.Description">
<label for="serviceRecordCost">Cost</label>
<input type="number" id="serviceRecordCost" class="form-control" value="@Model.Cost">
</div>
<div class="col-md-6 col-12">
<label for="serviceRecordNotes">Notes(optional)</label>
<textarea id="serviceRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
@if (Model.Id > 0)
{
<button type="button" class="btn btn-danger" onclick="deleteServiceRecord(@Model.Id)" style="margin-right:auto;">Delete</button>
}
<button type="button" class="btn btn-secondary" onclick="hideAddServiceRecordModal()">Cancel</button>
@if (Model.Id == 0)
{
<button type="button" id="addServiceRecordButton" class="btn btn-primary" onclick="saveServiceRecordToVehicle()">Add New Service Record</button>
} else if (Model.Id > 0)
{
<button type="button" id="editServiceRecordButton" class="btn btn-primary" onclick="saveServiceRecordToVehicle(true)">Edit Service Record</button>
}
</div>
<script>
function getAndValidateServiceRecordValues() {
var serviceDate = $("#serviceRecordDate").val();
var serviceMileage = $("#serviceRecordMileage").val();
var serviceDescription = $("#serviceRecordDescription").val();
var serviceCost = $("#serviceRecordCost").val();
var serviceNotes = $("#serviceRecordNotes").val();
var vehicleId = GetVehicleId().vehicleId;
//validation
var hasError = false;
if (serviceDate.trim() == '' || !isDate(serviceDate)) { //eliminates whitespace.
hasError = true;
$("#serviceRecordDate").addClass("is-invalid");
} else {
$("#serviceRecordDate").removeClass("is-invalid");
}
if (serviceMileage.trim() == '' || parseInt(serviceMileage) < 0) {
hasError = true;
$("#serviceRecordMileage").addClass("is-invalid");
} else {
$("#serviceRecordMileage").removeClass("is-invalid");
}
if (serviceDescription.trim() == '') {
hasError = true;
$("#serviceRecordDescription").addClass("is-invalid");
} else {
$("#serviceRecordDescription").removeClass("is-invalid");
}
if (serviceCost.trim() == '') {
hasError = true;
$("#serviceRecordCost").addClass("is-invalid");
} else {
$("#serviceRecordCost").removeClass("is-invalid");
}
return {
id: @Model.Id,
hasError: hasError,
vehicleId: vehicleId,
date: serviceDate,
mileage: serviceMileage,
description: serviceDescription,
cost: serviceCost,
notes: serviceNotes
}
}
</script>

View File

@@ -8,32 +8,25 @@
</div>
<div class="row vehicleDetailTabContainer">
<div class="col-12">
<table class="table">
<table class="table table-hover">
<thead>
<tr class="d-flex">
<th scope="col" class="col-1">Date</th>
<th scope="col" class="col-2">Mileage</th>
<th scope="col" class="col-4">Description</th>
<th scope="col" class="col-2">Cost</th>
<th scope="col" class="col-2">Notes</th>
<th scope="col" class="col-1">Actions</th>
<th scope="col" class="col-3">Notes</th>
</tr>
</thead>
<tbody>
@foreach (ServiceRecord serviceRecord in Model)
{
<tr class="d-flex">
<tr class="d-flex" style="cursor:pointer;" onclick="showEditServiceRecordModal(@serviceRecord.Id)">
<td class="col-1">@serviceRecord.Date.ToShortDateString()</td>
<td class="col-2">@serviceRecord.Mileage</td>
<td class="col-4">@serviceRecord.Description</td>
<td class="col-2">@serviceRecord.Cost.ToString("C")</td>
<td class="col-2 text-truncate" onclick="showServiceRecordNotes('@serviceRecord.Notes')" style="cursor:alias;">@serviceRecord.Notes</td>
<td class="col-1">
<div class="btn-group">
<button onclick="showEditServiceRecordModal(@serviceRecord.Id)" class="btn btn-warning btn-sm"><i class="bi bi-pencil-square"></i></button>
<button onclick="deleteServiceRecord(@serviceRecord.Id)" class="btn btn-danger btn-sm"><i class="bi bi-trash"></i></button>
</div>
</td>
<td class="col-3 text-truncate">@serviceRecord.Notes</td>
</tr>
}
</tbody>
@@ -42,38 +35,10 @@
</div>
<div class="modal fade" id="addServiceRecordModal" tabindex="-1" role="dialog" aria-labelledby="addServiceRecordModalLabel" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<form>
<div class="modal-header">
<h5 class="modal-title" id="addServiceRecordModalLabel">Add New Service Record</h5>
</div>
<div class="modal-body">
<div class="form-group">
<label for="serviceRecordDate">Date</label>
<div class="input-group">
<input type="text" id="serviceRecordDate" class="form-control">
<div class="input-group-append">
<span class="input-group-text"><i class="bi bi-grid-3x3-gap"></i></span>
</div>
</div>
<label for="serviceRecordMileage">Mileage</label>
<input type="number" id="serviceRecordMileage" class="form-control">
<label for="serviceRecordDescription">Description</label>
<input type="text" id="serviceRecordDescription" class="form-control">
<label for="serviceRecordCost">Cost</label>
<input type="number" id="serviceRecordCost" class="form-control">
<label for="serviceRecordNotes">Notes(optional)</label>
<textarea id="serviceRecordNotes" class="form-control" rows="5"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="hideAddServiceRecordModal()">Cancel</button>
<button type="button" id="addServiceRecordButton" class="btn btn-primary" onclick="addServiceRecordToVehicle()">Add New Service Record</button>
<button type="button" id="editServiceRecordButton" class="btn btn-primary" onclick="editServiceRecordToVehicle()" style="display:none;">Edit Service Record</button>
</div>
</form>
<div class="modal fade" id="serviceRecordModal" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content" id="serviceRecordModalContent">
</div>
</div>
</div>

55
wwwroot/css/loader.css Normal file
View File

@@ -0,0 +1,55 @@
.sloader {
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height: 100%;
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(255,255,255,0.5);
}
.loader {
display: block;
width: 100px;
height: 100px;
border: 3px solid white;
border-radius: 50%;
animation: spin 7s ease-in-out;
animation-iteration-count: infinite;
transition-duration: 0.1s;
}
.loader:hover {
scale: 0.95;
/*Loader on hover effect*/
}
.loader:active {
scale: 2.5;
/*Loader on click effect*/
}
@keyframes spin {
0% {
transform: rotate(0deg);
border-bottom: solid 3px transparent;
border-top: solid 3px transparent;
}
50% {
transform: rotate(1800deg);
border: 3px solid white;
border-left: solid 3px transparent;
border-right: solid 3px transparent;
}
100% {
/*Reversed spinning*/
transform: rotate(0deg);
border-bottom: solid 3px transparent;
border-top: solid 3px transparent;
}
}

10
wwwroot/js/loader.js Normal file
View File

@@ -0,0 +1,10 @@
const sloader = {
show: function () {
var sLoaderElement = `<div class='sloader'><div class='loader'></div></div>`
$("body").append(sLoaderElement);
},
hide: function () {
$(".sloader").remove();
}
}

View File

@@ -27,12 +27,4 @@ function errorToast(message) {
toast.onmouseleave = Swal.resumeTimer;
}
})
}
function genericSwal(title, message) {
Swal.fire({
showConfirmButton: true,
title: title,
text: message,
icon: "info"
})
}

View File

@@ -43,10 +43,6 @@ function getVehicleServiceRecords(vehicleId) {
$.get(`/Vehicle/GetServiceRecordsByVehicleId?vehicleId=${vehicleId}`, function (data) {
if (data) {
$("#servicerecord-tab-pane").html(data);
//initiate datepicker
$('#serviceRecordDate').datepicker({
endDate: "+0d"
});
}
})
}
@@ -57,53 +53,32 @@ function DeleteVehicle(vehicleId) {
}
})
}
var serviceRecordEditId = 0;
function showEditServiceRecordModal(serviceRecordId) {
//retrieve service record object.
$.get(`/Vehicle/GetServiceRecordById?serviceRecordId=${serviceRecordId}`, function (data) {
function showAddServiceRecordModal() {
$.get('/Vehicle/GetAddServiceRecordPartialView', function (data) {
if (data) {
//UI elements.
$("#addServiceRecordButton").hide();
$("#editServiceRecordButton").show();
//pre-populate fields.
$("#serviceRecordDate").val(data.date);
$("#serviceRecordMileage").val(data.mileage);
$("#serviceRecordDescription").val(data.description);
$("#serviceRecordCost").val(data.cost);
$("#serviceRecordNotes").val(data.notes);
serviceRecordEditId = serviceRecordId; //set global var.
$('#addServiceRecordModal').modal('show');
$("#serviceRecordModalContent").html(data);
//initiate datepicker
$('#serviceRecordDate').datepicker({
endDate: "+0d"
});
$('#serviceRecordModal').modal('show');
}
});
}
function showAddServiceRecordModal() {
serviceRecordEditId = 0;
$("#addServiceRecordButton").show();
$("#editServiceRecordButton").hide();
$('#addServiceRecordModal').modal('show');
function showEditServiceRecordModal(serviceRecordId) {
$.get(`/Vehicle/GetServiceRecordForEditById?serviceRecordId=${serviceRecordId}`, function (data) {
if (data) {
$("#serviceRecordModalContent").html(data);
//initiate datepicker
$('#serviceRecordDate').datepicker({
endDate: "+0d"
});
$('#serviceRecordModal').modal('show');
}
});
}
function hideAddServiceRecordModal() {
serviceRecordEditId = 0;
$('#addServiceRecordModal').modal('hide');
}
function editServiceRecordToVehicle() {
var formValues = getAndValidateServiceRecordValues();
//validate
if (formValues.hasError) {
errorToast("Please check the form data");
return;
}
//save to db.
$.post('/Vehicle/SaveServiceRecordToVehicleId', { serviceRecord: formValues }, function (data) {
if (data) {
successToast("Service Record updated.");
hideAddServiceRecordModal();
getVehicleServiceRecords(formValues.vehicleId);
serviceRecordEditId = 0; //reset global var.
} else {
errorToast("An error has occurred, please try again later.");
}
})
$('#serviceRecordModal').modal('hide');
}
function deleteServiceRecord(serviceRecordId) {
Swal.fire({
@@ -116,6 +91,7 @@ function deleteServiceRecord(serviceRecordId) {
if (result.isConfirmed) {
$.post(`/Vehicle/DeleteServiceRecordById?serviceRecordId=${serviceRecordId}`, function (data) {
if (data) {
hideAddServiceRecordModal();
successToast("Service Record deleted");
var vehicleId = GetVehicleId().vehicleId;
getVehicleServiceRecords(vehicleId);
@@ -126,7 +102,7 @@ function deleteServiceRecord(serviceRecordId) {
}
});
}
function addServiceRecordToVehicle() {
function saveServiceRecordToVehicle(isEdit) {
//get values
var formValues = getAndValidateServiceRecordValues();
//validate
@@ -137,61 +113,11 @@ function addServiceRecordToVehicle() {
//save to db.
$.post('/Vehicle/SaveServiceRecordToVehicleId', { serviceRecord: formValues }, function (data) {
if (data) {
successToast("Service Record added.");
successToast(isEdit ? "Service Record Updated" : "Service Record Added.");
hideAddServiceRecordModal();
getVehicleServiceRecords(formValues.vehicleId);
} else {
errorToast("An error has occurred, please try again later.");
}
})
}
function getAndValidateServiceRecordValues() {
var serviceDate = $("#serviceRecordDate").val();
var serviceMileage = $("#serviceRecordMileage").val();
var serviceDescription = $("#serviceRecordDescription").val();
var serviceCost = $("#serviceRecordCost").val();
var serviceNotes = $("#serviceRecordNotes").val();
var vehicleId = GetVehicleId().vehicleId;
//validation
var hasError = false;
if (serviceDate.trim() == '') { //eliminates whitespace.
hasError = true;
$("#serviceRecordDate").addClass("is-invalid");
} else {
$("#serviceRecordDate").removeClass("is-invalid");
}
if (serviceMileage.trim() == '' || parseInt(serviceMileage) < 0) {
hasError = true;
$("#serviceRecordMileage").addClass("is-invalid");
} else {
$("#serviceRecordMileage").removeClass("is-invalid");
}
if (serviceDescription.trim() == '') {
hasError = true;
$("#serviceRecordDescription").addClass("is-invalid");
} else {
$("#serviceRecordDescription").removeClass("is-invalid");
}
if (serviceCost.trim() == '') {
hasError = true;
$("#serviceRecordCost").addClass("is-invalid");
} else {
$("#serviceRecordCost").removeClass("is-invalid");
}
return {
id: serviceRecordEditId,
hasError: hasError,
vehicleId: vehicleId,
date: serviceDate,
mileage: serviceMileage,
description: serviceDescription,
cost: serviceCost,
notes: serviceNotes
}
}
function showServiceRecordNotes(note) {
if (note.trim() == '') {
return;
}
genericSwal("Note", note);
}