Added method to upload and delete documents from service records.

This commit is contained in:
ivancheahhh
2024-01-02 15:15:25 -07:00
parent 30df01ba96
commit 3a3f216ddf
12 changed files with 178 additions and 17 deletions

2
.gitignore vendored
View File

@@ -3,3 +3,5 @@ bin/
obj/
wwwroot/images/
cartracker.db
wwwroot/documents/
wwwroot/temp/

View File

@@ -7,31 +7,52 @@ using static System.Net.Mime.MediaTypeNames;
using System.Drawing;
using System.Linq.Expressions;
using Microsoft.Extensions.Logging;
using CarCareTracker.Helper;
namespace CarCareTracker.Controllers
{
public class FilesController : Controller
{
private readonly ILogger<FilesController> _logger;
private readonly IVehicleDataAccess _dataAccess;
private readonly IWebHostEnvironment _webEnv;
private readonly IFileHelper _fileHelper;
public FilesController(ILogger<FilesController> logger, IWebHostEnvironment webEnv)
public FilesController(ILogger<FilesController> logger, IFileHelper fileHelper, IWebHostEnvironment webEnv)
{
_logger = logger;
_webEnv = webEnv;
_fileHelper = fileHelper;
}
[HttpPost]
public IActionResult HandleFileUpload(IFormFile file)
{
var fileName = UploadImage(file);
var fileName = UploadFile(file);
return Json(fileName);
}
private string UploadImage(IFormFile fileToUpload)
[HttpPost]
public IActionResult HandleMultipleFileUpload(List<IFormFile> file)
{
string uploadDirectory = "images/";
List<UploadedFiles> uploadedFiles = new List<UploadedFiles>();
foreach (IFormFile fileToUpload in file)
{
var fileName = UploadFile(fileToUpload);
uploadedFiles.Add(new UploadedFiles { Name = fileToUpload.FileName, Location = fileName});
}
return Json(uploadedFiles);
}
[HttpPost]
public ActionResult DeleteFiles(string fileLocation)
{
var result = _fileHelper.DeleteFile(fileLocation);
return Json(result);
}
private string UploadFile(IFormFile fileToUpload)
{
string uploadDirectory = "temp/";
string uploadPath = Path.Combine(_webEnv.WebRootPath, uploadDirectory);
if (!Directory.Exists(uploadPath))
Directory.CreateDirectory(uploadPath);

View File

@@ -7,6 +7,7 @@ using static System.Net.Mime.MediaTypeNames;
using System.Drawing;
using System.Linq.Expressions;
using Microsoft.Extensions.Logging;
using CarCareTracker.Helper;
namespace CarCareTracker.Controllers
{
@@ -14,13 +15,13 @@ namespace CarCareTracker.Controllers
{
private readonly ILogger<HomeController> _logger;
private readonly IVehicleDataAccess _dataAccess;
private readonly IWebHostEnvironment _webEnv;
private readonly IFileHelper _fileHelper;
public HomeController(ILogger<HomeController> logger, IVehicleDataAccess dataAccess, IWebHostEnvironment webEnv)
public HomeController(ILogger<HomeController> logger, IVehicleDataAccess dataAccess, IFileHelper fileHelper)
{
_logger = logger;
_dataAccess = dataAccess;
_webEnv = webEnv;
_fileHelper = fileHelper;
}
public IActionResult Index()
@@ -41,6 +42,8 @@ namespace CarCareTracker.Controllers
{
try
{
//move image from temp folder to images folder.
vehicleInput.ImageLocation = _fileHelper.MoveFileFromTemp(vehicleInput.ImageLocation, "images/");
//save vehicle.
var result = _dataAccess.SaveVehicle(vehicleInput);
return Json(result);

View File

@@ -8,6 +8,7 @@ using System.Drawing;
using System.Linq.Expressions;
using Microsoft.Extensions.Logging;
using CarCareTracker.External.Implementations;
using CarCareTracker.Helper;
namespace CarCareTracker.Controllers
{
@@ -18,12 +19,14 @@ namespace CarCareTracker.Controllers
private readonly INoteDataAccess _noteDataAccess;
private readonly IServiceRecordDataAccess _serviceRecordDataAccess;
private readonly IWebHostEnvironment _webEnv;
private readonly IFileHelper _fileHelper;
public VehicleController(ILogger<HomeController> logger, IVehicleDataAccess dataAccess, INoteDataAccess noteDataAccess, IServiceRecordDataAccess serviceRecordDataAccess, IWebHostEnvironment webEnv)
public VehicleController(ILogger<HomeController> logger, IFileHelper fileHelper, IVehicleDataAccess dataAccess, INoteDataAccess noteDataAccess, IServiceRecordDataAccess serviceRecordDataAccess, IWebHostEnvironment webEnv)
{
_logger = logger;
_dataAccess = dataAccess;
_noteDataAccess = noteDataAccess;
_fileHelper = fileHelper;
_serviceRecordDataAccess = serviceRecordDataAccess;
_webEnv = webEnv;
}
@@ -70,6 +73,8 @@ namespace CarCareTracker.Controllers
[HttpPost]
public IActionResult SaveServiceRecordToVehicleId(ServiceRecordInput serviceRecord)
{
//move files from temp.
serviceRecord.Files = serviceRecord.Files.Select(x => { return new UploadedFiles { Name = x.Name, Location = _fileHelper.MoveFileFromTemp(x.Location, "documents/") }; }).ToList();
var result = _serviceRecordDataAccess.SaveServiceRecordToVehicle(serviceRecord.ToServiceRecord());
return Json(result);
}
@@ -82,7 +87,6 @@ namespace CarCareTracker.Controllers
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,
@@ -90,7 +94,8 @@ namespace CarCareTracker.Controllers
Description = result.Description,
Mileage = result.Mileage,
Notes = result.Notes,
VehicleId = result.VehicleId
VehicleId = result.VehicleId,
Files = result.Files
};
return PartialView("_ServiceRecordModal", convertedResult);
}

57
Helper/FileHelper.cs Normal file
View File

@@ -0,0 +1,57 @@
namespace CarCareTracker.Helper
{
public interface IFileHelper
{
public string MoveFileFromTemp(string currentFilePath, string newFolder);
public bool DeleteFile(string currentFilePath);
}
public class FileHelper: IFileHelper
{
private readonly IWebHostEnvironment _webEnv;
public FileHelper(IWebHostEnvironment webEnv)
{
_webEnv = webEnv;
}
public string MoveFileFromTemp(string currentFilePath, string newFolder)
{
string tempPath = "temp/";
if (!currentFilePath.StartsWith("/temp/")) //file is not in temp directory.
{
return currentFilePath;
}
if (currentFilePath.StartsWith("/")) {
currentFilePath = currentFilePath.Substring(1);
}
string uploadPath = Path.Combine(_webEnv.WebRootPath, newFolder);
string oldFilePath = Path.Combine(_webEnv.WebRootPath, currentFilePath);
if (!Directory.Exists(uploadPath))
Directory.CreateDirectory(uploadPath);
string newFileUploadPath = oldFilePath.Replace(tempPath, newFolder);
if (File.Exists(oldFilePath))
{
File.Move(oldFilePath, newFileUploadPath);
}
string newFilePathToReturn = "/" + currentFilePath.Replace(tempPath, newFolder);
return newFilePathToReturn;
}
public bool DeleteFile(string currentFilePath)
{
if (currentFilePath.StartsWith("/"))
{
currentFilePath = currentFilePath.Substring(1);
}
string filePath = Path.Combine(_webEnv.WebRootPath, currentFilePath);
if (File.Exists(filePath))
{
File.Delete(filePath);
}
if (!File.Exists(filePath)) //verify file no longer exists.
{
return true;
} else
{
return false;
}
}
}
}

View File

@@ -9,5 +9,6 @@
public string Description { get; set; }
public decimal Cost { get; set; }
public string Notes { get; set; }
public List<UploadedFiles> Files { get; set; }
}
}

View File

@@ -9,7 +9,7 @@
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 }; }
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
public ServiceRecord ToServiceRecord() { return new ServiceRecord { Id = Id, VehicleId = VehicleId, Date = DateTime.Parse(Date), Cost = Cost, Mileage = Mileage, Description = Description, Notes = Notes, Files = Files }; }
}
}

8
Models/UploadedFiles.cs Normal file
View File

@@ -0,0 +1,8 @@
namespace CarCareTracker.Models
{
public class UploadedFiles
{
public string Name { get; set; }
public string Location { get; set; }
}
}

View File

@@ -1,5 +1,6 @@
using CarCareTracker.External.Implementations;
using CarCareTracker.External.Interfaces;
using CarCareTracker.Helper;
var builder = WebApplication.CreateBuilder(args);
@@ -8,6 +9,7 @@ builder.Services.AddControllersWithViews();
builder.Services.AddSingleton<IVehicleDataAccess, VehicleDataAccess>();
builder.Services.AddSingleton<INoteDataAccess, NoteDataAccess>();
builder.Services.AddSingleton<IServiceRecordDataAccess, ServiceRecordDataAccess>();
builder.Services.AddSingleton<IFileHelper, FileHelper>();
var app = builder.Build();

View File

@@ -7,6 +7,7 @@
<div class="form-group">
<div class="row">
<div class="col-md-6 col-12">
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
<label for="serviceRecordDate">Date</label>
<div class="input-group">
<input type="text" id="serviceRecordDate" class="form-control" value="@Model.Date">
@@ -22,6 +23,26 @@
<div class="col-md-6 col-12">
<label for="serviceRecordNotes">Notes(optional)</label>
<textarea id="serviceRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
@if (Model.Files.Any())
{
<div>
<label>Uploaded Documents</label>
@foreach (UploadedFiles filesUploaded in Model.Files)
{
<div class="d-flex justify-content-between">
<a type="button" class="btn btn-link" href="@filesUploaded.Location" target="_blank">@filesUploaded.Name</a>
<button type="button" class="btn btn-sm btn-outline-danger" onclick="deleteServiceRecordFile('@filesUploaded.Location', this)"><i class="bi bi-trash"></i></button>
</div>
}
<label for="serviceRecordFiles">Upload more documents</label>
<input onChange="uploadServiceRecordFilesAsync()" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="serviceRecordFiles">
</div>
}
else
{
<label for="serviceRecordFiles">Upload documents(optional)</label>
<input onChange="uploadServiceRecordFilesAsync()" type="file" multiple accept=".png,.jpg,.jpeg,.pdf,.xls,.xlsx,.docx" class="form-control-file" id="serviceRecordFiles">
}
</div>
</div>
</div>
@@ -36,12 +57,25 @@
@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)
}
else if (Model.Id > 0)
{
<button type="button" id="editServiceRecordButton" class="btn btn-primary" onclick="saveServiceRecordToVehicle(true)">Edit Service Record</button>
}
</div>
<script>
var uploadedFiles = [];
getUploadedFilesFromModel();
function getUploadedFilesFromModel() {
@foreach (UploadedFiles filesUploaded in Model.Files)
{
@:uploadedFiles.push({ name: "@filesUploaded.Name", location: "@filesUploaded.Location" });
}
}
function deleteServiceRecordFile(fileLocation, event) {
event.parentElement.remove();
uploadedFiles = uploadedFiles.filter(x => x.location != fileLocation);
}
function getAndValidateServiceRecordValues() {
var serviceDate = $("#serviceRecordDate").val();
var serviceMileage = $("#serviceRecordMileage").val();
@@ -51,7 +85,7 @@
var vehicleId = GetVehicleId().vehicleId;
//validation
var hasError = false;
if (serviceDate.trim() == '' || !isDate(serviceDate)) { //eliminates whitespace.
if (serviceDate.trim() == '') { //eliminates whitespace.
hasError = true;
$("#serviceRecordDate").addClass("is-invalid");
} else {
@@ -83,7 +117,8 @@
mileage: serviceMileage,
description: serviceDescription,
cost: serviceCost,
notes: serviceNotes
notes: serviceNotes,
files: uploadedFiles
}
}
</script>

View File

@@ -1,6 +1,10 @@
@model List<ServiceRecord>
<div class="row">
<div class="d-flex flex-row-reverse">
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center">
<span class="badge bg-primary">@($"Total: {Model.Sum(x=>x.Cost).ToString("C")}")</span>
<span class="ms-2 badge bg-success">@($"# of Service Records: {Model.Count()}")</span>
</div>
<div>
<button onclick="showAddServiceRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square"></i>Add Service Record</button>
</div>

View File

@@ -81,6 +81,7 @@ function hideAddServiceRecordModal() {
$('#serviceRecordModal').modal('hide');
}
function deleteServiceRecord(serviceRecordId) {
$("#workAroundInput").show();
Swal.fire({
title: "Confirm Deletion?",
text: "Deleted Service Records cannot be restored.",
@@ -99,6 +100,8 @@ function deleteServiceRecord(serviceRecordId) {
errorToast("An error has occurred, please try again later.");
}
});
} else {
$("#workAroundInput").hide();
}
});
}
@@ -120,4 +123,24 @@ function saveServiceRecordToVehicle(isEdit) {
errorToast("An error has occurred, please try again later.");
}
})
}
function uploadServiceRecordFilesAsync() {
let formData = new FormData();
var files = $("#serviceRecordFiles")[0].files;
for (var x = 0; x < files.length; x++) {
formData.append("file", files[x]);
}
$.ajax({
url: "/Files/HandleMultipleFileUpload",
data: formData,
cache: false,
processData: false,
contentType: false,
type: 'POST',
success: function (response) {
if (response.length > 0) {
uploadedFiles.push.apply(uploadedFiles, response);
}
}
});
}