added sfloader for file uploads.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -5,3 +5,4 @@ wwwroot/images/
|
||||
cartracker.db
|
||||
wwwroot/documents/
|
||||
wwwroot/temp/
|
||||
wwwroot/imports/
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="CsvHelper" Version="30.0.1" />
|
||||
<PackageReference Include="LiteDB" Version="5.0.17" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -9,6 +9,9 @@ using System.Linq.Expressions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using CarCareTracker.External.Implementations;
|
||||
using CarCareTracker.Helper;
|
||||
using CsvHelper;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace CarCareTracker.Controllers
|
||||
{
|
||||
@@ -157,6 +160,56 @@ namespace CarCareTracker.Controllers
|
||||
}
|
||||
return PartialView("_Gas", computedResults);
|
||||
}
|
||||
[HttpGet]
|
||||
public IActionResult GetBulkImportModalPartialView(string mode)
|
||||
{
|
||||
return PartialView("_BulkDataImporter", mode);
|
||||
}
|
||||
[HttpPost]
|
||||
public IActionResult ImportToVehicleIdFromCsv(int vehicleId, string mode, string fileName)
|
||||
{
|
||||
var fullFileName = _fileHelper.GetFullFilePath(fileName);
|
||||
if (vehicleId == default || string.IsNullOrWhiteSpace(fullFileName))
|
||||
{
|
||||
return Json(false);
|
||||
}
|
||||
try
|
||||
{
|
||||
using (var reader = new StreamReader(fullFileName))
|
||||
{
|
||||
var config = new CsvHelper.Configuration.CsvConfiguration(System.Globalization.CultureInfo.InvariantCulture);
|
||||
config.MissingFieldFound = null;
|
||||
config.HeaderValidated = null;
|
||||
using (var csv = new CsvReader(reader, config))
|
||||
{
|
||||
if (mode == "gas")
|
||||
{
|
||||
var records = csv.GetRecords<GasRecordImport>().ToList();
|
||||
if (records.Any())
|
||||
{
|
||||
foreach (GasRecordImport gasRecordToInsert in records)
|
||||
{
|
||||
var convertedGasRecord = new GasRecord()
|
||||
{
|
||||
VehicleId = vehicleId,
|
||||
Date = gasRecordToInsert.Date,
|
||||
Mileage = gasRecordToInsert.Odometer,
|
||||
Gallons = gasRecordToInsert.FuelConsumed,
|
||||
Cost = gasRecordToInsert.Cost
|
||||
};
|
||||
_gasRecordDataAccess.SaveGasRecordToVehicle(convertedGasRecord);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Json(true);
|
||||
} catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error Occurred While Bulk Inserting");
|
||||
return Json(false);
|
||||
}
|
||||
}
|
||||
[HttpPost]
|
||||
public IActionResult SaveGasRecordToVehicleId(GasRecordInput gasRecord)
|
||||
{
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace CarCareTracker.External.Implementations
|
||||
using (var db = new LiteDatabase(dbName))
|
||||
{
|
||||
var table = db.GetCollection<GasRecord>(tableName);
|
||||
var gasRecords = table.Find(Query.EQ(nameof(GasRecord.VehicleId), vehicleId)).OrderBy(x => x.Date);
|
||||
var gasRecords = table.Find(Query.EQ(nameof(GasRecord.VehicleId), vehicleId)).OrderBy(x => x.Date).ThenBy(x=>x.Mileage);
|
||||
return gasRecords.ToList() ?? new List<GasRecord>();
|
||||
};
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
{
|
||||
public interface IFileHelper
|
||||
{
|
||||
string GetFullFilePath(string currentFilePath);
|
||||
public string MoveFileFromTemp(string currentFilePath, string newFolder);
|
||||
public bool DeleteFile(string currentFilePath);
|
||||
}
|
||||
@@ -12,6 +13,21 @@
|
||||
{
|
||||
_webEnv = webEnv;
|
||||
}
|
||||
public string GetFullFilePath(string currentFilePath)
|
||||
{
|
||||
if (currentFilePath.StartsWith("/"))
|
||||
{
|
||||
currentFilePath = currentFilePath.Substring(1);
|
||||
}
|
||||
string oldFilePath = Path.Combine(_webEnv.WebRootPath, currentFilePath);
|
||||
if (File.Exists(oldFilePath))
|
||||
{
|
||||
return oldFilePath;
|
||||
} else
|
||||
{
|
||||
return string.Empty;
|
||||
}
|
||||
}
|
||||
public string MoveFileFromTemp(string currentFilePath, string newFolder)
|
||||
{
|
||||
string tempPath = "temp/";
|
||||
|
||||
10
Models/GasRecord/GasRecordImport.cs
Normal file
10
Models/GasRecord/GasRecordImport.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace CarCareTracker.Models
|
||||
{
|
||||
public class GasRecordImport
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
public int Odometer { get; set; }
|
||||
public decimal FuelConsumed { get; set; }
|
||||
public decimal Cost { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
<button class="nav-link active" id="garage-tab" data-bs-toggle="tab" data-bs-target="#garage-tab-pane" type="button" role="tab" aria-selected="true"><i class="bi bi-car-front me-2"></i>Garage</button>
|
||||
</li>
|
||||
<li class="nav-item ms-auto" role="presentation">
|
||||
<button class="nav-link" id="help-tab" data-bs-toggle="tab" data-bs-target="#help-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-gear me-2"></i>Help</button>
|
||||
<button class="nav-link" id="help-tab" data-bs-toggle="tab" data-bs-target="#help-tab-pane" type="button" role="tab" aria-selected="false"><i class="bi bi-question-circle me-2"></i>Help</button>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="tab-content" id="homeTab">
|
||||
|
||||
@@ -71,6 +71,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal fade" id="bulkImportModal" tabindex="-1" role="dialog">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content" id="bulkImportModalContent">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
function GetVehicleId() {
|
||||
return { vehicleId: @Model.Id};
|
||||
|
||||
55
Views/Vehicle/_BulkDataImporter.cshtml
Normal file
55
Views/Vehicle/_BulkDataImporter.cshtml
Normal file
@@ -0,0 +1,55 @@
|
||||
@model string
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">Import Data from CSV</h5>
|
||||
<button type="button" class="btn-close" onclick="hideBulkImportModal()" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<div class="form-group">
|
||||
<div class="col-12">
|
||||
<div class="alert alert-warning" role="alert">
|
||||
In order for this utility to function properly, your CSV file MUST be formatted exactly like the provided sample.
|
||||
Dates must be supplied in a string.
|
||||
Numbers must be supplied as numbers without currency formatting.
|
||||
</div>
|
||||
<div class="alert alert-danger" role="alert">
|
||||
Failure to format the data correctly can cause data corruption. Please make sure you make a copy of the local database before proceeding.
|
||||
</div>
|
||||
@if (Model == "gas")
|
||||
{
|
||||
<a class="btn btn-link" href="/defaults/gassample.csv" target="_blank">Download Sample</a>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="col-12">
|
||||
<label for="csvFileUploader">Upload CSV File</label>
|
||||
<input onChange="uploadFileAsync(this)" type="file" multiple accept=".csv" class="form-control-file" id="csvFileUploader">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="hideBulkImportModal()">Cancel</button>
|
||||
<button type="button" onclick="importFromCsv()" class="btn btn-primary">Import</button>
|
||||
</div>
|
||||
<script>
|
||||
var uploadedFile = "";
|
||||
function importFromCsv() {
|
||||
var vehicleId = GetVehicleId().vehicleId;
|
||||
var mode = "@Model";
|
||||
$.post('/Vehicle/ImportToVehicleIdFromCsv', { vehicleId: vehicleId, mode: mode, fileName: uploadedFile }, function (data) {
|
||||
if (data) {
|
||||
successToast("Data Imported Successfully");
|
||||
hideBulkImportModal();
|
||||
if (mode == "gas") {
|
||||
getVehicleGasRecords();
|
||||
}
|
||||
} else {
|
||||
errorToast("An error has occurred, please double check the data and try again.");
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -12,8 +12,14 @@
|
||||
<span class="ms-2 badge bg-success">@($"Total Fuel Consumed: {Model.Sum(x=>x.Gallons)}")</span>
|
||||
<span class="ms-2 badge bg-success">@($"Total Cost: {Model.Sum(x => x.Cost).ToString("C")}")</span>
|
||||
</div>
|
||||
<div>
|
||||
<div class="btn-group">
|
||||
<button onclick="showAddGasRecordModal()" class="btn btn-primary btn-md mt-1 mb-1"><i class="bi bi-pencil-square me-2"></i>Add Gas 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="showBulkImportModal('gas')">Import via CSV</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
@if (!string.IsNullOrWhiteSpace(Model.ImageLocation))
|
||||
{
|
||||
<label for="inputImage">Replace picture(optional)</label>
|
||||
<input onChange="uploadFileAsync()" type="file" accept=".png,.jpg,.jpeg" class="form-control-file" id="inputImage">
|
||||
<input onChange="uploadFileAsync(this)" type="file" accept=".png,.jpg,.jpeg" class="form-control-file" id="inputImage">
|
||||
} else
|
||||
{
|
||||
<label for="inputImage">Upload a picture(optional)</label>
|
||||
<input onChange="uploadFileAsync()" type="file" accept=".png,.jpg,.jpeg" class="form-control-file" id="inputImage">
|
||||
<input onChange="uploadFileAsync(this)" type="file" accept=".png,.jpg,.jpeg" class="form-control-file" id="inputImage">
|
||||
}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
3
wwwroot/defaults/gassample.csv
Normal file
3
wwwroot/defaults/gassample.csv
Normal file
@@ -0,0 +1,3 @@
|
||||
Date,Odometer,FuelConsumed,Cost
|
||||
5/8/2020,204836,8.331,16.24
|
||||
5/30/2020,205056,11.913,25.72
|
||||
|
@@ -90,9 +90,10 @@ function saveVehicle(isEdit) {
|
||||
}
|
||||
});
|
||||
}
|
||||
function uploadFileAsync() {
|
||||
function uploadFileAsync(event) {
|
||||
let formData = new FormData();
|
||||
formData.append("file", $("#inputImage")[0].files[0]);
|
||||
formData.append("file", event.files[0]);
|
||||
sloader.show();
|
||||
$.ajax({
|
||||
url: "/Files/HandleFileUpload",
|
||||
data: formData,
|
||||
@@ -101,6 +102,7 @@ function uploadFileAsync() {
|
||||
contentType: false,
|
||||
type: 'POST',
|
||||
success: function (response) {
|
||||
sloader.hide();
|
||||
if (response.trim() != '') {
|
||||
uploadedFile = response;
|
||||
}
|
||||
|
||||
@@ -96,6 +96,17 @@ function editVehicle(vehicleId) {
|
||||
function hideEditVehicleModal() {
|
||||
$('#editVehicleModal').modal('hide');
|
||||
}
|
||||
function showBulkImportModal(mode) {
|
||||
$.get(`/Vehicle/GetBulkImportModalPartialView?mode=${mode}`, function (data) {
|
||||
if (data) {
|
||||
$("#bulkImportModalContent").html(data);
|
||||
$("#bulkImportModal").modal('show');
|
||||
}
|
||||
})
|
||||
}
|
||||
function hideBulkImportModal(){
|
||||
$("#bulkImportModal").modal('hide');
|
||||
}
|
||||
function deleteVehicle(vehicleId) {
|
||||
Swal.fire({
|
||||
title: "Confirm Deletion?",
|
||||
|
||||
Reference in New Issue
Block a user