Merge branch 'main' into Hargata/documentation
This commit is contained in:
@@ -22,10 +22,13 @@ namespace CarCareTracker.Controllers
|
|||||||
private readonly IReminderRecordDataAccess _reminderRecordDataAccess;
|
private readonly IReminderRecordDataAccess _reminderRecordDataAccess;
|
||||||
private readonly IUpgradeRecordDataAccess _upgradeRecordDataAccess;
|
private readonly IUpgradeRecordDataAccess _upgradeRecordDataAccess;
|
||||||
private readonly IOdometerRecordDataAccess _odometerRecordDataAccess;
|
private readonly IOdometerRecordDataAccess _odometerRecordDataAccess;
|
||||||
|
private readonly IUserAccessDataAccess _userAccessDataAccess;
|
||||||
|
private readonly IUserRecordDataAccess _userRecordDataAccess;
|
||||||
private readonly IReminderHelper _reminderHelper;
|
private readonly IReminderHelper _reminderHelper;
|
||||||
private readonly IGasHelper _gasHelper;
|
private readonly IGasHelper _gasHelper;
|
||||||
private readonly IUserLogic _userLogic;
|
private readonly IUserLogic _userLogic;
|
||||||
private readonly IFileHelper _fileHelper;
|
private readonly IFileHelper _fileHelper;
|
||||||
|
private readonly IMailHelper _mailHelper;
|
||||||
public APIController(IVehicleDataAccess dataAccess,
|
public APIController(IVehicleDataAccess dataAccess,
|
||||||
IGasHelper gasHelper,
|
IGasHelper gasHelper,
|
||||||
IReminderHelper reminderHelper,
|
IReminderHelper reminderHelper,
|
||||||
@@ -37,6 +40,9 @@ namespace CarCareTracker.Controllers
|
|||||||
IReminderRecordDataAccess reminderRecordDataAccess,
|
IReminderRecordDataAccess reminderRecordDataAccess,
|
||||||
IUpgradeRecordDataAccess upgradeRecordDataAccess,
|
IUpgradeRecordDataAccess upgradeRecordDataAccess,
|
||||||
IOdometerRecordDataAccess odometerRecordDataAccess,
|
IOdometerRecordDataAccess odometerRecordDataAccess,
|
||||||
|
IUserAccessDataAccess userAccessDataAccess,
|
||||||
|
IUserRecordDataAccess userRecordDataAccess,
|
||||||
|
IMailHelper mailHelper,
|
||||||
IFileHelper fileHelper,
|
IFileHelper fileHelper,
|
||||||
IUserLogic userLogic)
|
IUserLogic userLogic)
|
||||||
{
|
{
|
||||||
@@ -49,6 +55,9 @@ namespace CarCareTracker.Controllers
|
|||||||
_reminderRecordDataAccess = reminderRecordDataAccess;
|
_reminderRecordDataAccess = reminderRecordDataAccess;
|
||||||
_upgradeRecordDataAccess = upgradeRecordDataAccess;
|
_upgradeRecordDataAccess = upgradeRecordDataAccess;
|
||||||
_odometerRecordDataAccess = odometerRecordDataAccess;
|
_odometerRecordDataAccess = odometerRecordDataAccess;
|
||||||
|
_userAccessDataAccess = userAccessDataAccess;
|
||||||
|
_userRecordDataAccess = userRecordDataAccess;
|
||||||
|
_mailHelper = mailHelper;
|
||||||
_gasHelper = gasHelper;
|
_gasHelper = gasHelper;
|
||||||
_reminderHelper = reminderHelper;
|
_reminderHelper = reminderHelper;
|
||||||
_userLogic = userLogic;
|
_userLogic = userLogic;
|
||||||
@@ -428,12 +437,65 @@ namespace CarCareTracker.Controllers
|
|||||||
}
|
}
|
||||||
[Authorize(Roles = nameof(UserData.IsRootUser))]
|
[Authorize(Roles = nameof(UserData.IsRootUser))]
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
|
[Route("/api/vehicle/reminders/send")]
|
||||||
|
public IActionResult SendReminders(List<ReminderUrgency> urgencies)
|
||||||
|
{
|
||||||
|
var vehicles = _dataAccess.GetVehicles();
|
||||||
|
List<OperationResponse> operationResponses = new List<OperationResponse>();
|
||||||
|
foreach(Vehicle vehicle in vehicles)
|
||||||
|
{
|
||||||
|
var vehicleId = vehicle.Id;
|
||||||
|
//get reminders
|
||||||
|
var currentMileage = GetMaxMileage(vehicleId);
|
||||||
|
var reminders = _reminderRecordDataAccess.GetReminderRecordsByVehicleId(vehicleId);
|
||||||
|
var results = _reminderHelper.GetReminderRecordViewModels(reminders, currentMileage, DateTime.Now).OrderByDescending(x => x.Urgency).ToList();
|
||||||
|
results.RemoveAll(x => !urgencies.Contains(x.Urgency));
|
||||||
|
if (!results.Any())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//get list of recipients.
|
||||||
|
var userIds = _userAccessDataAccess.GetUserAccessByVehicleId(vehicleId).Select(x => x.Id.UserId);
|
||||||
|
List<string> emailRecipients = new List<string>();
|
||||||
|
foreach (int userId in userIds)
|
||||||
|
{
|
||||||
|
var userData = _userRecordDataAccess.GetUserRecordById(userId);
|
||||||
|
emailRecipients.Add(userData.EmailAddress);
|
||||||
|
};
|
||||||
|
if (!emailRecipients.Any())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
var result = _mailHelper.NotifyUserForReminders(vehicle, emailRecipients, results);
|
||||||
|
operationResponses.Add(result);
|
||||||
|
}
|
||||||
|
if (operationResponses.All(x => x.Success))
|
||||||
|
{
|
||||||
|
return Json(new OperationResponse { Success = true, Message = "Emails sent" });
|
||||||
|
} else if (operationResponses.All(x => !x.Success))
|
||||||
|
{
|
||||||
|
return Json(new OperationResponse { Success = false, Message = "All emails failed, check SMTP settings" });
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
return Json(new OperationResponse { Success = true, Message = "Some emails sent, some failed, check recipient settings" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[Authorize(Roles = nameof(UserData.IsRootUser))]
|
||||||
|
[HttpGet]
|
||||||
[Route("/api/makebackup")]
|
[Route("/api/makebackup")]
|
||||||
public IActionResult MakeBackup()
|
public IActionResult MakeBackup()
|
||||||
{
|
{
|
||||||
var result = _fileHelper.MakeBackup();
|
var result = _fileHelper.MakeBackup();
|
||||||
return Json(result);
|
return Json(result);
|
||||||
}
|
}
|
||||||
|
[Authorize(Roles = nameof(UserData.IsRootUser))]
|
||||||
|
[HttpGet]
|
||||||
|
[Route("/api/demo/restore")]
|
||||||
|
public IActionResult RestoreDemo()
|
||||||
|
{
|
||||||
|
var result = _fileHelper.RestoreBackup("/defaults/demo_default.zip");
|
||||||
|
return Json(result);
|
||||||
|
}
|
||||||
private int GetMaxMileage(int vehicleId)
|
private int GetMaxMileage(int vehicleId)
|
||||||
{
|
{
|
||||||
var numbersArray = new List<int>();
|
var numbersArray = new List<int>();
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ namespace CarCareTracker.Controllers
|
|||||||
public IActionResult Index(int vehicleId)
|
public IActionResult Index(int vehicleId)
|
||||||
{
|
{
|
||||||
var data = _dataAccess.GetVehicleById(vehicleId);
|
var data = _dataAccess.GetVehicleById(vehicleId);
|
||||||
|
UpdateRecurringTaxes(vehicleId);
|
||||||
return View(data);
|
return View(data);
|
||||||
}
|
}
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
@@ -316,7 +317,6 @@ namespace CarCareTracker.Controllers
|
|||||||
var vehicleRecords = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicleId);
|
var vehicleRecords = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicleId);
|
||||||
bool useMPG = _config.GetUserConfig(User).UseMPG;
|
bool useMPG = _config.GetUserConfig(User).UseMPG;
|
||||||
bool useUKMPG = _config.GetUserConfig(User).UseUKMPG;
|
bool useUKMPG = _config.GetUserConfig(User).UseUKMPG;
|
||||||
vehicleRecords = vehicleRecords.OrderBy(x => x.Date).ThenBy(x => x.Mileage).ToList();
|
|
||||||
var convertedRecords = _gasHelper.GetGasRecordViewModels(vehicleRecords, useMPG, useUKMPG);
|
var convertedRecords = _gasHelper.GetGasRecordViewModels(vehicleRecords, useMPG, useUKMPG);
|
||||||
var exportData = convertedRecords.Select(x => new GasRecordExportModel
|
var exportData = convertedRecords.Select(x => new GasRecordExportModel
|
||||||
{
|
{
|
||||||
@@ -376,7 +376,7 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
VehicleId = vehicleId,
|
VehicleId = vehicleId,
|
||||||
Date = DateTime.Parse(importModel.Date),
|
Date = DateTime.Parse(importModel.Date),
|
||||||
Mileage = int.Parse(importModel.Odometer, NumberStyles.Any),
|
Mileage = decimal.ToInt32(decimal.Parse(importModel.Odometer, NumberStyles.Any)),
|
||||||
Gallons = decimal.Parse(importModel.FuelConsumed, NumberStyles.Any),
|
Gallons = decimal.Parse(importModel.FuelConsumed, NumberStyles.Any),
|
||||||
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes
|
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes
|
||||||
};
|
};
|
||||||
@@ -420,7 +420,7 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
VehicleId = vehicleId,
|
VehicleId = vehicleId,
|
||||||
Date = DateTime.Parse(importModel.Date),
|
Date = DateTime.Parse(importModel.Date),
|
||||||
Mileage = int.Parse(importModel.Odometer, NumberStyles.Any),
|
Mileage = decimal.ToInt32(decimal.Parse(importModel.Odometer, NumberStyles.Any)),
|
||||||
Description = string.IsNullOrWhiteSpace(importModel.Description) ? $"Service Record on {importModel.Date}" : importModel.Description,
|
Description = string.IsNullOrWhiteSpace(importModel.Description) ? $"Service Record on {importModel.Date}" : importModel.Description,
|
||||||
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
|
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
|
||||||
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any)
|
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any)
|
||||||
@@ -433,7 +433,7 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
VehicleId = vehicleId,
|
VehicleId = vehicleId,
|
||||||
Date = DateTime.Parse(importModel.Date),
|
Date = DateTime.Parse(importModel.Date),
|
||||||
Mileage = int.Parse(importModel.Odometer, NumberStyles.Any),
|
Mileage = decimal.ToInt32(decimal.Parse(importModel.Odometer, NumberStyles.Any)),
|
||||||
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes
|
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes
|
||||||
};
|
};
|
||||||
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(convertedRecord);
|
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(convertedRecord);
|
||||||
@@ -463,7 +463,7 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
VehicleId = vehicleId,
|
VehicleId = vehicleId,
|
||||||
Date = DateTime.Parse(importModel.Date),
|
Date = DateTime.Parse(importModel.Date),
|
||||||
Mileage = int.Parse(importModel.Odometer, NumberStyles.Any),
|
Mileage = decimal.ToInt32(decimal.Parse(importModel.Odometer, NumberStyles.Any)),
|
||||||
Description = string.IsNullOrWhiteSpace(importModel.Description) ? $"Repair Record on {importModel.Date}" : importModel.Description,
|
Description = string.IsNullOrWhiteSpace(importModel.Description) ? $"Repair Record on {importModel.Date}" : importModel.Description,
|
||||||
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
|
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
|
||||||
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any)
|
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any)
|
||||||
@@ -476,7 +476,7 @@ namespace CarCareTracker.Controllers
|
|||||||
{
|
{
|
||||||
VehicleId = vehicleId,
|
VehicleId = vehicleId,
|
||||||
Date = DateTime.Parse(importModel.Date),
|
Date = DateTime.Parse(importModel.Date),
|
||||||
Mileage = int.Parse(importModel.Odometer, NumberStyles.Any),
|
Mileage = decimal.ToInt32(decimal.Parse(importModel.Odometer, NumberStyles.Any)),
|
||||||
Description = string.IsNullOrWhiteSpace(importModel.Description) ? $"Upgrade Record on {importModel.Date}" : importModel.Description,
|
Description = string.IsNullOrWhiteSpace(importModel.Description) ? $"Upgrade Record on {importModel.Date}" : importModel.Description,
|
||||||
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
|
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
|
||||||
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any)
|
Cost = decimal.Parse(importModel.Cost, NumberStyles.Any)
|
||||||
@@ -529,8 +529,6 @@ namespace CarCareTracker.Controllers
|
|||||||
public IActionResult GetGasRecordsByVehicleId(int vehicleId)
|
public IActionResult GetGasRecordsByVehicleId(int vehicleId)
|
||||||
{
|
{
|
||||||
var result = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicleId);
|
var result = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicleId);
|
||||||
//need it in ascending order to perform computation.
|
|
||||||
result = result.OrderBy(x => x.Date).ThenBy(x => x.Mileage).ToList();
|
|
||||||
//check if the user uses MPG or Liters per 100km.
|
//check if the user uses MPG or Liters per 100km.
|
||||||
var userConfig = _config.GetUserConfig(User);
|
var userConfig = _config.GetUserConfig(User);
|
||||||
bool useMPG = userConfig.UseMPG;
|
bool useMPG = userConfig.UseMPG;
|
||||||
@@ -757,6 +755,34 @@ namespace CarCareTracker.Controllers
|
|||||||
}
|
}
|
||||||
return PartialView("_TaxRecords", result);
|
return PartialView("_TaxRecords", result);
|
||||||
}
|
}
|
||||||
|
private void UpdateRecurringTaxes(int vehicleId)
|
||||||
|
{
|
||||||
|
var result = _taxRecordDataAccess.GetTaxRecordsByVehicleId(vehicleId);
|
||||||
|
var recurringFees = result.Where(x => x.IsRecurring);
|
||||||
|
if (recurringFees.Any())
|
||||||
|
{
|
||||||
|
foreach(TaxRecord recurringFee in recurringFees)
|
||||||
|
{
|
||||||
|
var newDate = recurringFee.Date.AddMonths((int)recurringFee.RecurringInterval);
|
||||||
|
if (DateTime.Now > newDate){
|
||||||
|
recurringFee.IsRecurring = false;
|
||||||
|
var newRecurringFee = new TaxRecord()
|
||||||
|
{
|
||||||
|
VehicleId = recurringFee.VehicleId,
|
||||||
|
Date = newDate,
|
||||||
|
Description = recurringFee.Description,
|
||||||
|
Cost = recurringFee.Cost,
|
||||||
|
IsRecurring = true,
|
||||||
|
Notes = recurringFee.Notes,
|
||||||
|
RecurringInterval = recurringFee.RecurringInterval,
|
||||||
|
Files = recurringFee.Files
|
||||||
|
};
|
||||||
|
_taxRecordDataAccess.SaveTaxRecordToVehicle(recurringFee);
|
||||||
|
_taxRecordDataAccess.SaveTaxRecordToVehicle(newRecurringFee);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult SaveTaxRecordToVehicleId(TaxRecordInput taxRecord)
|
public IActionResult SaveTaxRecordToVehicleId(TaxRecordInput taxRecord)
|
||||||
{
|
{
|
||||||
@@ -783,6 +809,8 @@ namespace CarCareTracker.Controllers
|
|||||||
Description = result.Description,
|
Description = result.Description,
|
||||||
Notes = result.Notes,
|
Notes = result.Notes,
|
||||||
VehicleId = result.VehicleId,
|
VehicleId = result.VehicleId,
|
||||||
|
IsRecurring = result.IsRecurring,
|
||||||
|
RecurringInterval = result.RecurringInterval,
|
||||||
Files = result.Files
|
Files = result.Files
|
||||||
};
|
};
|
||||||
return PartialView("_TaxRecordModal", convertedResult);
|
return PartialView("_TaxRecordModal", convertedResult);
|
||||||
@@ -816,7 +844,7 @@ namespace CarCareTracker.Controllers
|
|||||||
UpgradeRecordSum = upgradeRecords.Sum(x => x.Cost)
|
UpgradeRecordSum = upgradeRecords.Sum(x => x.Cost)
|
||||||
};
|
};
|
||||||
//get costbymonth
|
//get costbymonth
|
||||||
List<CostForVehicleByMonth> allCosts = new List<CostForVehicleByMonth>();
|
List<CostForVehicleByMonth> allCosts = StaticHelper.GetBaseLineCosts();
|
||||||
allCosts.AddRange(_reportHelper.GetServiceRecordSum(serviceRecords, 0));
|
allCosts.AddRange(_reportHelper.GetServiceRecordSum(serviceRecords, 0));
|
||||||
allCosts.AddRange(_reportHelper.GetRepairRecordSum(collisionRecords, 0));
|
allCosts.AddRange(_reportHelper.GetRepairRecordSum(collisionRecords, 0));
|
||||||
allCosts.AddRange(_reportHelper.GetUpgradeRecordSum(upgradeRecords, 0));
|
allCosts.AddRange(_reportHelper.GetUpgradeRecordSum(upgradeRecords, 0));
|
||||||
@@ -867,10 +895,17 @@ namespace CarCareTracker.Controllers
|
|||||||
var userConfig = _config.GetUserConfig(User);
|
var userConfig = _config.GetUserConfig(User);
|
||||||
var mileageData = _gasHelper.GetGasRecordViewModels(gasRecords, userConfig.UseMPG, userConfig.UseUKMPG);
|
var mileageData = _gasHelper.GetGasRecordViewModels(gasRecords, userConfig.UseMPG, userConfig.UseUKMPG);
|
||||||
mileageData.RemoveAll(x => x.MilesPerGallon == default);
|
mileageData.RemoveAll(x => x.MilesPerGallon == default);
|
||||||
var monthlyMileageData = mileageData.GroupBy(x => x.MonthId).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
var monthlyMileageData = StaticHelper.GetBaseLineCostsNoMonthName();
|
||||||
|
monthlyMileageData.AddRange(mileageData.GroupBy(x => x.MonthId).Select(x => new CostForVehicleByMonth
|
||||||
{
|
{
|
||||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
MonthId = x.Key,
|
||||||
Cost = x.Average(y => y.MilesPerGallon)
|
Cost = x.Average(y => y.MilesPerGallon)
|
||||||
|
}));
|
||||||
|
monthlyMileageData = monthlyMileageData.GroupBy(x => x.MonthId).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
||||||
|
{
|
||||||
|
MonthId = x.Key,
|
||||||
|
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||||
|
Cost = x.Sum(y=>y.Cost)
|
||||||
}).ToList();
|
}).ToList();
|
||||||
viewModel.FuelMileageForVehicleByMonth = monthlyMileageData;
|
viewModel.FuelMileageForVehicleByMonth = monthlyMileageData;
|
||||||
return PartialView("_Report", viewModel);
|
return PartialView("_Report", viewModel);
|
||||||
@@ -956,7 +991,7 @@ namespace CarCareTracker.Controllers
|
|||||||
var gasViewModels = _gasHelper.GetGasRecordViewModels(gasRecords, useMPG, useUKMPG);
|
var gasViewModels = _gasHelper.GetGasRecordViewModels(gasRecords, useMPG, useUKMPG);
|
||||||
if (gasViewModels.Any())
|
if (gasViewModels.Any())
|
||||||
{
|
{
|
||||||
averageMPG = _gasHelper.GetAverageGasMileage(gasViewModels);
|
averageMPG = _gasHelper.GetAverageGasMileage(gasViewModels, useMPG);
|
||||||
}
|
}
|
||||||
vehicleHistory.MPG = averageMPG;
|
vehicleHistory.MPG = averageMPG;
|
||||||
//insert servicerecords
|
//insert servicerecords
|
||||||
@@ -1012,10 +1047,17 @@ namespace CarCareTracker.Controllers
|
|||||||
mileageData.RemoveAll(x => DateTime.Parse(x.Date).Year != year);
|
mileageData.RemoveAll(x => DateTime.Parse(x.Date).Year != year);
|
||||||
}
|
}
|
||||||
mileageData.RemoveAll(x => x.MilesPerGallon == default);
|
mileageData.RemoveAll(x => x.MilesPerGallon == default);
|
||||||
var monthlyMileageData = mileageData.GroupBy(x => x.MonthId).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
var monthlyMileageData = StaticHelper.GetBaseLineCostsNoMonthName();
|
||||||
|
monthlyMileageData.AddRange(mileageData.GroupBy(x => x.MonthId).Select(x => new CostForVehicleByMonth
|
||||||
{
|
{
|
||||||
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
MonthId = x.Key,
|
||||||
Cost = x.Average(y => y.MilesPerGallon)
|
Cost = x.Average(y => y.MilesPerGallon)
|
||||||
|
}));
|
||||||
|
monthlyMileageData = monthlyMileageData.GroupBy(x => x.MonthId).OrderBy(x => x.Key).Select(x => new CostForVehicleByMonth
|
||||||
|
{
|
||||||
|
MonthId = x.Key,
|
||||||
|
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
|
||||||
|
Cost = x.Sum(y => y.Cost)
|
||||||
}).ToList();
|
}).ToList();
|
||||||
return PartialView("_MPGByMonthReport", monthlyMileageData);
|
return PartialView("_MPGByMonthReport", monthlyMileageData);
|
||||||
}
|
}
|
||||||
@@ -1023,7 +1065,7 @@ namespace CarCareTracker.Controllers
|
|||||||
[HttpPost]
|
[HttpPost]
|
||||||
public IActionResult GetCostByMonthByVehicle(int vehicleId, List<ImportMode> selectedMetrics, int year = 0)
|
public IActionResult GetCostByMonthByVehicle(int vehicleId, List<ImportMode> selectedMetrics, int year = 0)
|
||||||
{
|
{
|
||||||
List<CostForVehicleByMonth> allCosts = new List<CostForVehicleByMonth>();
|
List<CostForVehicleByMonth> allCosts = StaticHelper.GetBaseLineCosts();
|
||||||
if (selectedMetrics.Contains(ImportMode.ServiceRecord))
|
if (selectedMetrics.Contains(ImportMode.ServiceRecord))
|
||||||
{
|
{
|
||||||
var serviceRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId);
|
var serviceRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId);
|
||||||
@@ -1266,6 +1308,7 @@ namespace CarCareTracker.Controllers
|
|||||||
public IActionResult GetNotesByVehicleId(int vehicleId)
|
public IActionResult GetNotesByVehicleId(int vehicleId)
|
||||||
{
|
{
|
||||||
var result = _noteDataAccess.GetNotesByVehicleId(vehicleId);
|
var result = _noteDataAccess.GetNotesByVehicleId(vehicleId);
|
||||||
|
result = result.OrderByDescending(x => x.Pinned).ToList();
|
||||||
return PartialView("_Notes", result);
|
return PartialView("_Notes", result);
|
||||||
}
|
}
|
||||||
[HttpPost]
|
[HttpPost]
|
||||||
|
|||||||
@@ -14,7 +14,9 @@
|
|||||||
FifteenThousandMiles = 15000,
|
FifteenThousandMiles = 15000,
|
||||||
TwentyThousandMiles = 20000,
|
TwentyThousandMiles = 20000,
|
||||||
ThirtyThousandMiles = 30000,
|
ThirtyThousandMiles = 30000,
|
||||||
|
FortyThousandMiles = 40000,
|
||||||
FiftyThousandMiles = 50000,
|
FiftyThousandMiles = 50000,
|
||||||
|
SixtyThousandMiles = 60000,
|
||||||
OneHundredThousandMiles = 100000,
|
OneHundredThousandMiles = 100000,
|
||||||
OneHundredFiftyThousandMiles = 150000
|
OneHundredFiftyThousandMiles = 150000
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
{
|
{
|
||||||
public enum ReminderMonthInterval
|
public enum ReminderMonthInterval
|
||||||
{
|
{
|
||||||
|
OneMonth = 1,
|
||||||
ThreeMonths = 3,
|
ThreeMonths = 3,
|
||||||
SixMonths = 6,
|
SixMonths = 6,
|
||||||
OneYear = 12,
|
OneYear = 12,
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ namespace CarCareTracker.Helper
|
|||||||
public interface IGasHelper
|
public interface IGasHelper
|
||||||
{
|
{
|
||||||
List<GasRecordViewModel> GetGasRecordViewModels(List<GasRecord> result, bool useMPG, bool useUKMPG);
|
List<GasRecordViewModel> GetGasRecordViewModels(List<GasRecord> result, bool useMPG, bool useUKMPG);
|
||||||
string GetAverageGasMileage(List<GasRecordViewModel> results);
|
string GetAverageGasMileage(List<GasRecordViewModel> results, bool useMPG);
|
||||||
}
|
}
|
||||||
public class GasHelper : IGasHelper
|
public class GasHelper : IGasHelper
|
||||||
{
|
{
|
||||||
public string GetAverageGasMileage(List<GasRecordViewModel> results)
|
public string GetAverageGasMileage(List<GasRecordViewModel> results, bool useMPG)
|
||||||
{
|
{
|
||||||
var recordWithCalculatedMPG = results.Where(x => x.MilesPerGallon > 0);
|
var recordWithCalculatedMPG = results.Where(x => x.MilesPerGallon > 0);
|
||||||
var minMileage = results.Min(x => x.Mileage);
|
var minMileage = results.Min(x => x.Mileage);
|
||||||
@@ -19,12 +19,18 @@ namespace CarCareTracker.Helper
|
|||||||
var totalGallonsConsumed = recordWithCalculatedMPG.Sum(x => x.Gallons);
|
var totalGallonsConsumed = recordWithCalculatedMPG.Sum(x => x.Gallons);
|
||||||
var deltaMileage = maxMileage - minMileage;
|
var deltaMileage = maxMileage - minMileage;
|
||||||
var averageGasMileage = (maxMileage - minMileage) / totalGallonsConsumed;
|
var averageGasMileage = (maxMileage - minMileage) / totalGallonsConsumed;
|
||||||
|
if (!useMPG)
|
||||||
|
{
|
||||||
|
averageGasMileage = 100 / averageGasMileage;
|
||||||
|
}
|
||||||
return averageGasMileage.ToString("F");
|
return averageGasMileage.ToString("F");
|
||||||
}
|
}
|
||||||
return "0";
|
return "0";
|
||||||
}
|
}
|
||||||
public List<GasRecordViewModel> GetGasRecordViewModels(List<GasRecord> result, bool useMPG, bool useUKMPG)
|
public List<GasRecordViewModel> GetGasRecordViewModels(List<GasRecord> result, bool useMPG, bool useUKMPG)
|
||||||
{
|
{
|
||||||
|
//need to order by to get correct results
|
||||||
|
result = result.OrderBy(x => x.Date).ThenBy(x => x.Mileage).ToList();
|
||||||
var computedResults = new List<GasRecordViewModel>();
|
var computedResults = new List<GasRecordViewModel>();
|
||||||
int previousMileage = 0;
|
int previousMileage = 0;
|
||||||
decimal unFactoredConsumption = 0.00M;
|
decimal unFactoredConsumption = 0.00M;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ namespace CarCareTracker.Helper
|
|||||||
{
|
{
|
||||||
OperationResponse NotifyUserForRegistration(string emailAddress, string token);
|
OperationResponse NotifyUserForRegistration(string emailAddress, string token);
|
||||||
OperationResponse NotifyUserForPasswordReset(string emailAddress, string token);
|
OperationResponse NotifyUserForPasswordReset(string emailAddress, string token);
|
||||||
|
OperationResponse NotifyUserForReminders(Vehicle vehicle, List<string> emailAddresses, List<ReminderRecordViewModel> reminders);
|
||||||
}
|
}
|
||||||
public class MailHelper : IMailHelper
|
public class MailHelper : IMailHelper
|
||||||
{
|
{
|
||||||
@@ -60,20 +61,62 @@ namespace CarCareTracker.Helper
|
|||||||
return new OperationResponse { Success = false, Message = StaticHelper.GenericErrorMessage };
|
return new OperationResponse { Success = false, Message = StaticHelper.GenericErrorMessage };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private bool SendEmail(string emailTo, string emailSubject, string emailBody) {
|
public OperationResponse NotifyUserForReminders(Vehicle vehicle, List<string> emailAddresses, List<ReminderRecordViewModel> reminders)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(mailConfig.EmailServer))
|
||||||
|
{
|
||||||
|
return new OperationResponse { Success = false, Message = "SMTP Server Not Setup" };
|
||||||
|
}
|
||||||
|
if (!emailAddresses.Any())
|
||||||
|
{
|
||||||
|
return new OperationResponse { Success = false, Message = "No recipients could be found" };
|
||||||
|
}
|
||||||
|
if (!reminders.Any())
|
||||||
|
{
|
||||||
|
return new OperationResponse { Success = false, Message = "No reminders could be found" };
|
||||||
|
}
|
||||||
|
string emailSubject = $"Vehicle Reminders From LubeLogger - {DateTime.Now.ToShortDateString()}";
|
||||||
|
//construct html table.
|
||||||
|
string emailBody = $"<h4>{vehicle.Year} {vehicle.Make} {vehicle.Model} #{vehicle.LicensePlate}</h4><br /><table style='width:100%'><tr><th style='padding:8px;'>Urgency</th><th style='padding:8px;'>Description</th></tr>";
|
||||||
|
foreach(ReminderRecordViewModel reminder in reminders)
|
||||||
|
{
|
||||||
|
emailBody += $"<tr><td style='padding:8px; text-align:center;'>{reminder.Urgency}</td><td style='padding:8px; text-align:center;'>{reminder.Description}</td></tr>";
|
||||||
|
}
|
||||||
|
emailBody += "</table>";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (string emailAddress in emailAddresses)
|
||||||
|
{
|
||||||
|
SendEmail(emailAddress, emailSubject, emailBody, true, true);
|
||||||
|
}
|
||||||
|
return new OperationResponse { Success = true, Message = "Email Sent!" };
|
||||||
|
} catch (Exception ex)
|
||||||
|
{
|
||||||
|
return new OperationResponse { Success = false, Message = ex.Message };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private bool SendEmail(string emailTo, string emailSubject, string emailBody, bool isBodyHtml = false, bool useAsync = false) {
|
||||||
string to = emailTo;
|
string to = emailTo;
|
||||||
string from = mailConfig.EmailFrom;
|
string from = mailConfig.EmailFrom;
|
||||||
var server = mailConfig.EmailServer;
|
var server = mailConfig.EmailServer;
|
||||||
MailMessage message = new MailMessage(from, to);
|
MailMessage message = new MailMessage(from, to);
|
||||||
message.Subject = emailSubject;
|
message.Subject = emailSubject;
|
||||||
message.Body = emailBody;
|
message.Body = emailBody;
|
||||||
|
message.IsBodyHtml = isBodyHtml;
|
||||||
SmtpClient client = new SmtpClient(server);
|
SmtpClient client = new SmtpClient(server);
|
||||||
client.EnableSsl = mailConfig.UseSSL;
|
client.EnableSsl = mailConfig.UseSSL;
|
||||||
client.Port = mailConfig.Port;
|
client.Port = mailConfig.Port;
|
||||||
client.Credentials = new NetworkCredential(mailConfig.Username, mailConfig.Password);
|
client.Credentials = new NetworkCredential(mailConfig.Username, mailConfig.Password);
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
client.Send(message);
|
if (useAsync)
|
||||||
|
{
|
||||||
|
client.SendMailAsync(message, new CancellationToken());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
client.Send(message);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using CarCareTracker.Models;
|
using CarCareTracker.Models;
|
||||||
|
using System.Globalization;
|
||||||
|
|
||||||
namespace CarCareTracker.Helper
|
namespace CarCareTracker.Helper
|
||||||
{
|
{
|
||||||
@@ -63,5 +64,41 @@ namespace CarCareTracker.Helper
|
|||||||
}
|
}
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
public static List<CostForVehicleByMonth> GetBaseLineCosts()
|
||||||
|
{
|
||||||
|
return new List<CostForVehicleByMonth>()
|
||||||
|
{
|
||||||
|
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(1), MonthId = 1, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(2), MonthId = 2, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(3), MonthId = 3, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(4), MonthId = 4, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(5), MonthId = 5, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(6), MonthId = 6, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(7), MonthId = 7, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(8), MonthId = 8, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(9), MonthId = 9, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(10), MonthId = 10, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(11), MonthId = 11, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(12), MonthId = 12, Cost = 0M}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
public static List<CostForVehicleByMonth> GetBaseLineCostsNoMonthName()
|
||||||
|
{
|
||||||
|
return new List<CostForVehicleByMonth>()
|
||||||
|
{
|
||||||
|
new CostForVehicleByMonth { MonthId = 1, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthId = 2, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthId = 3, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthId = 4, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthId = 5, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthId = 6, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthId = 7, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthId = 8, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth {MonthId = 9, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth { MonthId = 10, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth { MonthId = 11, Cost = 0M},
|
||||||
|
new CostForVehicleByMonth { MonthId = 12, Cost = 0M}
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,6 @@
|
|||||||
public int VehicleId { get; set; }
|
public int VehicleId { get; set; }
|
||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public string NoteText { get; set; }
|
public string NoteText { get; set; }
|
||||||
|
public bool Pinned { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,8 @@
|
|||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public decimal Cost { get; set; }
|
public decimal Cost { get; set; }
|
||||||
public string Notes { get; set; }
|
public string Notes { get; set; }
|
||||||
|
public bool IsRecurring { get; set; } = false;
|
||||||
|
public ReminderMonthInterval RecurringInterval { get; set; } = ReminderMonthInterval.OneYear;
|
||||||
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,18 @@
|
|||||||
public string Description { get; set; }
|
public string Description { get; set; }
|
||||||
public decimal Cost { get; set; }
|
public decimal Cost { get; set; }
|
||||||
public string Notes { get; set; }
|
public string Notes { get; set; }
|
||||||
|
public bool IsRecurring { get; set; } = false;
|
||||||
|
public ReminderMonthInterval RecurringInterval { get; set; } = ReminderMonthInterval.ThreeMonths;
|
||||||
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
|
||||||
public TaxRecord ToTaxRecord() { return new TaxRecord { Id = Id, VehicleId = VehicleId, Date = DateTime.Parse(Date), Cost = Cost, Description = Description, Notes = Notes, Files = Files }; }
|
public TaxRecord ToTaxRecord() { return new TaxRecord {
|
||||||
|
Id = Id,
|
||||||
|
VehicleId = VehicleId,
|
||||||
|
Date = DateTime.Parse(Date),
|
||||||
|
Cost = Cost,
|
||||||
|
Description = Description,
|
||||||
|
Notes = Notes,
|
||||||
|
IsRecurring = IsRecurring,
|
||||||
|
RecurringInterval = RecurringInterval,
|
||||||
|
Files = Files }; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,6 +241,21 @@
|
|||||||
</div>
|
</div>
|
||||||
@if (User.IsInRole(nameof(UserData.IsRootUser)))
|
@if (User.IsInRole(nameof(UserData.IsRootUser)))
|
||||||
{
|
{
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-1">
|
||||||
|
GET
|
||||||
|
</div>
|
||||||
|
<div class="col-5">
|
||||||
|
<code>/api/vehicle/reminders/send</code>
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
Send reminder emails out to collaborators based on specified urgency.
|
||||||
|
</div>
|
||||||
|
<div class="col-3">
|
||||||
|
(must be root user)<br />
|
||||||
|
urgencies[]=[NotUrgent,Urgent,VeryUrgent,PastDue]
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-1">
|
<div class="col-1">
|
||||||
GET
|
GET
|
||||||
|
|||||||
@@ -154,7 +154,7 @@
|
|||||||
<img src="/defaults/lubelogger_logo.png" />
|
<img src="/defaults/lubelogger_logo.png" />
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-center">
|
<div class="d-flex justify-content-center">
|
||||||
<small class="text-body-secondary">Version 1.0.7</small>
|
<small class="text-body-secondary">Version 1.0.8</small>
|
||||||
</div>
|
</div>
|
||||||
<p class="lead">
|
<p class="lead">
|
||||||
Proudly developed in the rural town of Price, Utah by Hargata Softworks.
|
Proudly developed in the rural town of Price, Utah by Hargata Softworks.
|
||||||
|
|||||||
@@ -12,14 +12,14 @@
|
|||||||
labels: ["Service Records", "Repairs", "Upgrades", "Tax", "Fuel"],
|
labels: ["Service Records", "Repairs", "Upgrades", "Tax", "Fuel"],
|
||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: "Expenses by Category",
|
label: "Expenses by Type",
|
||||||
backgroundColor: ["#003f5c", "#58508d", "#bc5090", "#ff6361", "#ffa600"],
|
backgroundColor: ["#003f5c", "#58508d", "#bc5090", "#ff6361", "#ffa600"],
|
||||||
data: [
|
data: [
|
||||||
@Model.ServiceRecordSum,
|
globalParseFloat('@Model.ServiceRecordSum'),
|
||||||
@Model.CollisionRecordSum,
|
globalParseFloat('@Model.CollisionRecordSum'),
|
||||||
@Model.UpgradeRecordSum,
|
globalParseFloat('@Model.UpgradeRecordSum'),
|
||||||
@Model.TaxRecordSum,
|
globalParseFloat('@Model.TaxRecordSum'),
|
||||||
@Model.GasRecordSum
|
globalParseFloat('@Model.GasRecordSum')
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -41,7 +41,7 @@
|
|||||||
<span class="ms-2 badge bg-success">@($"# of Gas Records: {Model.GasRecords.Count()}")</span>
|
<span class="ms-2 badge bg-success">@($"# of Gas Records: {Model.GasRecords.Count()}")</span>
|
||||||
@if (Model.GasRecords.Where(x => x.MilesPerGallon > 0).Any())
|
@if (Model.GasRecords.Where(x => x.MilesPerGallon > 0).Any())
|
||||||
{
|
{
|
||||||
<span class="ms-2 badge bg-primary">@($"Average Fuel Economy: {gasHelper.GetAverageGasMileage(Model.GasRecords)}")</span>
|
<span class="ms-2 badge bg-primary">@($"Average Fuel Economy: {gasHelper.GetAverageGasMileage(Model.GasRecords, useMPG)}")</span>
|
||||||
<span class="ms-2 badge bg-primary">@($"Min Fuel Economy: {Model.GasRecords.Where(y => y.MilesPerGallon > 0)?.Min(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
<span class="ms-2 badge bg-primary">@($"Min Fuel Economy: {Model.GasRecords.Where(y => y.MilesPerGallon > 0)?.Min(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
||||||
<span class="ms-2 badge bg-primary">@($"Max Fuel Economy: {Model.GasRecords.Max(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
<span class="ms-2 badge bg-primary">@($"Max Fuel Economy: {Model.GasRecords.Max(x => x.MilesPerGallon).ToString("F") ?? "0"}")</span>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
var barGraphColors = new string[] { "#00876c", "#43956e", "#67a371", "#89b177", "#a9be80", "#c8cb8b", "#e6d79b", "#e4c281", "#e3ab6b", "#e2925b", "#e07952", "#db5d4f" };
|
var barGraphColors = new string[] { "#00876c", "#43956e", "#67a371", "#89b177", "#a9be80", "#c8cb8b", "#e6d79b", "#e4c281", "#e3ab6b", "#e2925b", "#e07952", "#db5d4f" };
|
||||||
var sortedByMPG = Model.OrderBy(x => x.Cost).ToList();
|
var sortedByMPG = Model.OrderBy(x => x.Cost).ToList();
|
||||||
}
|
}
|
||||||
@if (Model.Any())
|
@if (Model.Where(x=>x.Cost > 0).Any())
|
||||||
{
|
{
|
||||||
<canvas id="bar-chart"></canvas>
|
<canvas id="bar-chart"></canvas>
|
||||||
<script>
|
<script>
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
@foreach (CostForVehicleByMonth gasCost in Model)
|
@foreach (CostForVehicleByMonth gasCost in Model)
|
||||||
{
|
{
|
||||||
@:barGraphLabels.push("@gasCost.MonthName");
|
@:barGraphLabels.push("@gasCost.MonthName");
|
||||||
@:barGraphData.push(@gasCost.Cost);
|
@:barGraphData.push(globalParseFloat('@gasCost.Cost'));
|
||||||
var index = sortedByMPG.FindIndex(x => x.MonthName == gasCost.MonthName);
|
var index = sortedByMPG.FindIndex(x => x.MonthName == gasCost.MonthName);
|
||||||
@:barGraphColors.push('@barGraphColors[index]');
|
@:barGraphColors.push('@barGraphColors[index]');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
var barGraphColors = new string[] { "#00876c", "#43956e", "#67a371", "#89b177", "#a9be80", "#c8cb8b", "#e6d79b", "#e4c281", "#e3ab6b", "#e2925b", "#e07952", "#db5d4f" };
|
var barGraphColors = new string[] { "#00876c", "#43956e", "#67a371", "#89b177", "#a9be80", "#c8cb8b", "#e6d79b", "#e4c281", "#e3ab6b", "#e2925b", "#e07952", "#db5d4f" };
|
||||||
var sortedByMPG = Model.OrderByDescending(x => x.Cost).ToList();
|
var sortedByMPG = Model.OrderByDescending(x => x.Cost).ToList();
|
||||||
}
|
}
|
||||||
@if (Model.Any())
|
@if (Model.Where(x=>x.Cost > 0).Any())
|
||||||
{
|
{
|
||||||
|
|
||||||
<canvas id="bar-chart-mpg"></canvas>
|
<canvas id="bar-chart-mpg"></canvas>
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
@foreach (CostForVehicleByMonth gasCost in Model)
|
@foreach (CostForVehicleByMonth gasCost in Model)
|
||||||
{
|
{
|
||||||
@:barGraphLabels.push("@gasCost.MonthName");
|
@:barGraphLabels.push("@gasCost.MonthName");
|
||||||
@:barGraphData.push(@gasCost.Cost);
|
@:barGraphData.push(globalParseFloat('@gasCost.Cost'));
|
||||||
var index = sortedByMPG.FindIndex(x => x.MonthName == gasCost.MonthName);
|
var index = sortedByMPG.FindIndex(x => x.MonthName == gasCost.MonthName);
|
||||||
@:barGraphColors.push('@barGraphColors[index]');
|
@:barGraphColors.push('@barGraphColors[index]');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,10 @@
|
|||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
|
<input type="text" id="workAroundInput" style="height:0px; width:0px; display:none;">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" role="switch" id="noteIsPinned" checked="@Model.Pinned">
|
||||||
|
<label class="form-check-label" for="noteIsPinned">Pinned</label>
|
||||||
|
</div>
|
||||||
<label for="noteDescription">Description</label>
|
<label for="noteDescription">Description</label>
|
||||||
<input type="text" id="noteDescription" class="form-control" placeholder="Description of the note" value="@(isNew ? "" : Model.Description)">
|
<input type="text" id="noteDescription" class="form-control" placeholder="Description of the note" value="@(isNew ? "" : Model.Description)">
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -27,7 +27,13 @@
|
|||||||
@foreach (Note note in Model)
|
@foreach (Note note in Model)
|
||||||
{
|
{
|
||||||
<tr class="d-flex" style="cursor:pointer;" onclick="showEditNoteModal(@note.Id)">
|
<tr class="d-flex" style="cursor:pointer;" onclick="showEditNoteModal(@note.Id)">
|
||||||
<td class="col-3">@note.Description</td>
|
@if (note.Pinned)
|
||||||
|
{
|
||||||
|
<td class="col-3"><i class='bi bi-pin-fill me-2'></i>@note.Description"</td>
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
<td class="col-3">@note.Description</td>
|
||||||
|
}
|
||||||
<td class="col-9 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(note.NoteText, 100)</td>
|
<td class="col-9 text-truncate">@CarCareTracker.Helper.StaticHelper.TruncateStrings(note.NoteText, 100)</td>
|
||||||
</tr>
|
</tr>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,9 @@
|
|||||||
<!option value="FifteenThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FifteenThousandMiles ? "selected" : "")>15000 mi. / Km</!option>
|
<!option value="FifteenThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FifteenThousandMiles ? "selected" : "")>15000 mi. / Km</!option>
|
||||||
<!option value="TwentyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.TwentyThousandMiles ? "selected" : "")>20000 mi. / Km</!option>
|
<!option value="TwentyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.TwentyThousandMiles ? "selected" : "")>20000 mi. / Km</!option>
|
||||||
<!option value="ThirtyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.ThirtyThousandMiles ? "selected" : "")>30000 mi. / Km</!option>
|
<!option value="ThirtyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.ThirtyThousandMiles ? "selected" : "")>30000 mi. / Km</!option>
|
||||||
|
<!option value="FortyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FortyThousandMiles ? "selected" : "")>40000 mi. / Km</!option>
|
||||||
<!option value="FiftyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FiftyThousandMiles ? "selected" : "")>50000 mi. / Km</!option>
|
<!option value="FiftyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.FiftyThousandMiles ? "selected" : "")>50000 mi. / Km</!option>
|
||||||
|
<!option value="SixtyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.SixtyThousandMiles ? "selected" : "")>60000 mi. / Km</!option>
|
||||||
<!option value="OneHundredThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.OneHundredThousandMiles ? "selected" : "")>100000 mi. / Km</!option>
|
<!option value="OneHundredThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.OneHundredThousandMiles ? "selected" : "")>100000 mi. / Km</!option>
|
||||||
<!option value="OneHundredFiftyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.OneHundredFiftyThousandMiles ? "selected" : "")>150000 mi. / Km</!option>
|
<!option value="OneHundredFiftyThousandMiles" @(Model.ReminderMileageInterval == ReminderMileageInterval.OneHundredFiftyThousandMiles ? "selected" : "")>150000 mi. / Km</!option>
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -25,6 +25,20 @@
|
|||||||
<div class="col-md-6 col-12">
|
<div class="col-md-6 col-12">
|
||||||
<label for="taxRecordNotes">Notes(optional)</label>
|
<label for="taxRecordNotes">Notes(optional)</label>
|
||||||
<textarea id="taxRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
|
<textarea id="taxRecordNotes" class="form-control" rows="5">@Model.Notes</textarea>
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input class="form-check-input" type="checkbox" onChange="enableTaxRecurring()" role="switch" id="taxIsRecurring" checked="@Model.IsRecurring">
|
||||||
|
<label class="form-check-label" for="taxIsRecurring">Is Recurring</label>
|
||||||
|
</div>
|
||||||
|
<label for="taxRecurringMonth">Month</label>
|
||||||
|
<select class="form-select" id="taxRecurringMonth" @(Model.IsRecurring ? "" : "disabled")>
|
||||||
|
<!option value="OneMonth" @(Model.RecurringInterval == ReminderMonthInterval.OneMonth ? "selected" : "")>1 Month</!option>
|
||||||
|
<!option value="ThreeMonths" @(Model.RecurringInterval == ReminderMonthInterval.ThreeMonths || isNew ? "selected" : "")>3 Months</!option>
|
||||||
|
<!option value="SixMonths" @(Model.RecurringInterval == ReminderMonthInterval.SixMonths ? "selected" : "")>6 Months</!option>
|
||||||
|
<!option value="OneYear" @(Model.RecurringInterval == ReminderMonthInterval.OneYear ? "selected" : "")>1 Year</!option>
|
||||||
|
<!option value="TwoYears" @(Model.RecurringInterval == ReminderMonthInterval.TwoYears ? "selected" : "")>2 Years</!option>
|
||||||
|
<!option value="ThreeYears" @(Model.RecurringInterval == ReminderMonthInterval.ThreeYears ? "selected" : "")>3 Years</!option>
|
||||||
|
<!option value="FiveYears" @(Model.RecurringInterval == ReminderMonthInterval.FiveYears ? "selected" : "")>5 Years</!option>
|
||||||
|
</select>
|
||||||
@if (Model.Files.Any())
|
@if (Model.Files.Any())
|
||||||
{
|
{
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
BIN
docs/dashboard.png
Normal file
BIN
docs/dashboard.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 112 KiB |
@@ -38,6 +38,8 @@
|
|||||||
<button type="button" data-bs-target="#carouselGallery" data-bs-slide-to="1"></button>
|
<button type="button" data-bs-target="#carouselGallery" data-bs-slide-to="1"></button>
|
||||||
<button type="button" data-bs-target="#carouselGallery" data-bs-slide-to="2"></button>
|
<button type="button" data-bs-target="#carouselGallery" data-bs-slide-to="2"></button>
|
||||||
<button type="button" data-bs-target="#carouselGallery" data-bs-slide-to="3"></button>
|
<button type="button" data-bs-target="#carouselGallery" data-bs-slide-to="3"></button>
|
||||||
|
<button type="button" data-bs-target="#carouselGallery" data-bs-slide-to="4"></button>
|
||||||
|
<button type="button" data-bs-target="#carouselGallery" data-bs-slide-to="5"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="carousel-inner">
|
<div class="carousel-inner">
|
||||||
<div class="carousel-item active">
|
<div class="carousel-item active">
|
||||||
@@ -47,6 +49,20 @@
|
|||||||
<p>All of your vehicles conveniently displayed in one place</p>
|
<p>All of your vehicles conveniently displayed in one place</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="carousel-item">
|
||||||
|
<img src="dashboard.png" class="d-block w-100" alt="...">
|
||||||
|
<div class="carousel-caption d-none d-md-block customCarouselCaption">
|
||||||
|
<h5>Dashboard</h5>
|
||||||
|
<p>Get an overview of your vehicle expenses</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="carousel-item">
|
||||||
|
<img src="planner.png" class="d-block w-100" alt="...">
|
||||||
|
<div class="carousel-caption d-none d-md-block customCarouselCaption">
|
||||||
|
<h5>Planner(Kanban Board)</h5>
|
||||||
|
<p>Plan and track the progress of your To-Do's</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="carousel-item">
|
<div class="carousel-item">
|
||||||
<img src="servicerecord.png" class="d-block w-100" alt="...">
|
<img src="servicerecord.png" class="d-block w-100" alt="...">
|
||||||
<div class="carousel-caption d-none d-md-block customCarouselCaption">
|
<div class="carousel-caption d-none d-md-block customCarouselCaption">
|
||||||
@@ -91,6 +107,7 @@
|
|||||||
<li class="list-group-item">Keeps track of all your maintenance, repair, and upgrade records</li>
|
<li class="list-group-item">Keeps track of all your maintenance, repair, and upgrade records</li>
|
||||||
<li class="list-group-item">Keeps track of your fuel economy(supports MPG, UK MPG, and L/100KM)</li>
|
<li class="list-group-item">Keeps track of your fuel economy(supports MPG, UK MPG, and L/100KM)</li>
|
||||||
<li class="list-group-item">Keeps track of taxes(registration, going fast tax, etc)</li>
|
<li class="list-group-item">Keeps track of taxes(registration, going fast tax, etc)</li>
|
||||||
|
<li class="list-group-item">Keeps track of supplies(parts, fluids, etc)</li>
|
||||||
<li class="list-group-item">No limit on how many vehicles you have in your garage</li>
|
<li class="list-group-item">No limit on how many vehicles you have in your garage</li>
|
||||||
<li class="list-group-item">Import existing records from CSV(supports imports from Fuelly for Fuel Records)</li>
|
<li class="list-group-item">Import existing records from CSV(supports imports from Fuelly for Fuel Records)</li>
|
||||||
<li class="list-group-item">Attach documents for each record(receipts, invoices, etc)</li>
|
<li class="list-group-item">Attach documents for each record(receipts, invoices, etc)</li>
|
||||||
@@ -98,12 +115,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-6">
|
<div class="col-12 col-md-6">
|
||||||
<ul class="list-group">
|
<ul class="list-group">
|
||||||
|
<li class="list-group-item">Keeps track of your To-Do's(Kanban Planner)</li>
|
||||||
<li class="list-group-item">Set reminders so you never miss another scheduled maintenance</li>
|
<li class="list-group-item">Set reminders so you never miss another scheduled maintenance</li>
|
||||||
<li class="list-group-item">Dark Mode</li>
|
<li class="list-group-item">Dark Mode</li>
|
||||||
<li class="list-group-item">Mobile/Small screen support</li>
|
<li class="list-group-item">Mobile/Small screen support</li>
|
||||||
<li class="list-group-item">Basic Authentication for security</li>
|
<li class="list-group-item">Basic Authentication for security</li>
|
||||||
<li class="list-group-item">Coming Soon(API Endpoints)</li>
|
<li class="list-group-item">API Endpoints</li>
|
||||||
<li class="list-group-item">Coming Soon(Consolidated Report Export - Just like CarFax)</li>
|
<li class="list-group-item">Consolidated Report Export - Just like CarFax</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
BIN
docs/planner.png
Normal file
BIN
docs/planner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 56 KiB |
BIN
wwwroot/defaults/demo_default.zip
Normal file
BIN
wwwroot/defaults/demo_default.zip
Normal file
Binary file not shown.
@@ -71,7 +71,7 @@ function saveCollisionRecordToVehicle(isEdit) {
|
|||||||
}
|
}
|
||||||
function getAndValidateCollisionRecordValues() {
|
function getAndValidateCollisionRecordValues() {
|
||||||
var collisionDate = $("#collisionRecordDate").val();
|
var collisionDate = $("#collisionRecordDate").val();
|
||||||
var collisionMileage = $("#collisionRecordMileage").val();
|
var collisionMileage = parseInt(globalParseFloat($("#collisionRecordMileage").val())).toString();
|
||||||
var collisionDescription = $("#collisionRecordDescription").val();
|
var collisionDescription = $("#collisionRecordDescription").val();
|
||||||
var collisionCost = $("#collisionRecordCost").val();
|
var collisionCost = $("#collisionRecordCost").val();
|
||||||
var collisionNotes = $("#collisionRecordNotes").val();
|
var collisionNotes = $("#collisionRecordNotes").val();
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ function saveGasRecordToVehicle(isEdit) {
|
|||||||
}
|
}
|
||||||
function getAndValidateGasRecordValues() {
|
function getAndValidateGasRecordValues() {
|
||||||
var gasDate = $("#gasRecordDate").val();
|
var gasDate = $("#gasRecordDate").val();
|
||||||
var gasMileage = $("#gasRecordMileage").val();
|
var gasMileage = parseInt(globalParseFloat($("#gasRecordMileage").val())).toString();
|
||||||
var gasGallons = $("#gasRecordGallons").val();
|
var gasGallons = $("#gasRecordGallons").val();
|
||||||
var gasCost = $("#gasRecordCost").val();
|
var gasCost = $("#gasRecordCost").val();
|
||||||
var gasCostType = $("#gasCostType").val();
|
var gasCostType = $("#gasCostType").val();
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ function getAndValidateNoteValues() {
|
|||||||
var noteText = $("#noteTextArea").val();
|
var noteText = $("#noteTextArea").val();
|
||||||
var vehicleId = GetVehicleId().vehicleId;
|
var vehicleId = GetVehicleId().vehicleId;
|
||||||
var noteId = getNoteModelData().id;
|
var noteId = getNoteModelData().id;
|
||||||
|
var noteIsPinned = $("#noteIsPinned").is(":checked");
|
||||||
//validation
|
//validation
|
||||||
var hasError = false;
|
var hasError = false;
|
||||||
if (noteDescription.trim() == '') { //eliminates whitespace.
|
if (noteDescription.trim() == '') { //eliminates whitespace.
|
||||||
@@ -86,6 +87,7 @@ function getAndValidateNoteValues() {
|
|||||||
hasError: hasError,
|
hasError: hasError,
|
||||||
vehicleId: vehicleId,
|
vehicleId: vehicleId,
|
||||||
description: noteDescription,
|
description: noteDescription,
|
||||||
noteText: noteText
|
noteText: noteText,
|
||||||
|
pinned: noteIsPinned
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -71,7 +71,7 @@ function saveOdometerRecordToVehicle(isEdit) {
|
|||||||
}
|
}
|
||||||
function getAndValidateOdometerRecordValues() {
|
function getAndValidateOdometerRecordValues() {
|
||||||
var serviceDate = $("#odometerRecordDate").val();
|
var serviceDate = $("#odometerRecordDate").val();
|
||||||
var serviceMileage = $("#odometerRecordMileage").val();
|
var serviceMileage = parseInt(globalParseFloat($("#odometerRecordMileage").val())).toString();
|
||||||
var serviceNotes = $("#odometerRecordNotes").val();
|
var serviceNotes = $("#odometerRecordNotes").val();
|
||||||
var vehicleId = GetVehicleId().vehicleId;
|
var vehicleId = GetVehicleId().vehicleId;
|
||||||
var odometerRecordId = getOdometerRecordModelData().id;
|
var odometerRecordId = getOdometerRecordModelData().id;
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ function markDoneReminderRecord(reminderRecordId, e) {
|
|||||||
|
|
||||||
function getAndValidateReminderRecordValues() {
|
function getAndValidateReminderRecordValues() {
|
||||||
var reminderDate = $("#reminderDate").val();
|
var reminderDate = $("#reminderDate").val();
|
||||||
var reminderMileage = $("#reminderMileage").val();
|
var reminderMileage = parseInt(globalParseFloat($("#reminderMileage").val())).toString();
|
||||||
var reminderDescription = $("#reminderDescription").val();
|
var reminderDescription = $("#reminderDescription").val();
|
||||||
var reminderNotes = $("#reminderNotes").val();
|
var reminderNotes = $("#reminderNotes").val();
|
||||||
var reminderOption = $('#reminderOptions input:radio:checked').val();
|
var reminderOption = $('#reminderOptions input:radio:checked').val();
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ function saveServiceRecordToVehicle(isEdit) {
|
|||||||
}
|
}
|
||||||
function getAndValidateServiceRecordValues() {
|
function getAndValidateServiceRecordValues() {
|
||||||
var serviceDate = $("#serviceRecordDate").val();
|
var serviceDate = $("#serviceRecordDate").val();
|
||||||
var serviceMileage = $("#serviceRecordMileage").val();
|
var serviceMileage = parseInt(globalParseFloat($("#serviceRecordMileage").val())).toString();
|
||||||
var serviceDescription = $("#serviceRecordDescription").val();
|
var serviceDescription = $("#serviceRecordDescription").val();
|
||||||
var serviceCost = $("#serviceRecordCost").val();
|
var serviceCost = $("#serviceRecordCost").val();
|
||||||
var serviceNotes = $("#serviceRecordNotes").val();
|
var serviceNotes = $("#serviceRecordNotes").val();
|
||||||
|
|||||||
@@ -18,6 +18,14 @@ function showEditTaxRecordModal(taxRecordId) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
function enableTaxRecurring() {
|
||||||
|
var taxIsRecurring = $("#taxIsRecurring").is(":checked");
|
||||||
|
if (taxIsRecurring) {
|
||||||
|
$("#taxRecurringMonth").attr('disabled', false);
|
||||||
|
} else {
|
||||||
|
$("#taxRecurringMonth").attr('disabled', true);
|
||||||
|
}
|
||||||
|
}
|
||||||
function hideAddTaxRecordModal() {
|
function hideAddTaxRecordModal() {
|
||||||
$('#taxRecordModal').modal('hide');
|
$('#taxRecordModal').modal('hide');
|
||||||
}
|
}
|
||||||
@@ -76,6 +84,8 @@ function getAndValidateTaxRecordValues() {
|
|||||||
var taxNotes = $("#taxRecordNotes").val();
|
var taxNotes = $("#taxRecordNotes").val();
|
||||||
var vehicleId = GetVehicleId().vehicleId;
|
var vehicleId = GetVehicleId().vehicleId;
|
||||||
var taxRecordId = getTaxRecordModelData().id;
|
var taxRecordId = getTaxRecordModelData().id;
|
||||||
|
var taxIsRecurring = $("#taxIsRecurring").is(":checked");
|
||||||
|
var taxRecurringMonth = $("#taxRecurringMonth").val();
|
||||||
var addReminderRecord = $("#addReminderCheck").is(":checked");
|
var addReminderRecord = $("#addReminderCheck").is(":checked");
|
||||||
//validation
|
//validation
|
||||||
var hasError = false;
|
var hasError = false;
|
||||||
@@ -105,6 +115,8 @@ function getAndValidateTaxRecordValues() {
|
|||||||
description: taxDescription,
|
description: taxDescription,
|
||||||
cost: taxCost,
|
cost: taxCost,
|
||||||
notes: taxNotes,
|
notes: taxNotes,
|
||||||
|
isRecurring: taxIsRecurring,
|
||||||
|
recurringInterval: taxRecurringMonth,
|
||||||
files: uploadedFiles,
|
files: uploadedFiles,
|
||||||
addReminderRecord: addReminderRecord
|
addReminderRecord: addReminderRecord
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ function saveUpgradeRecordToVehicle(isEdit) {
|
|||||||
}
|
}
|
||||||
function getAndValidateUpgradeRecordValues() {
|
function getAndValidateUpgradeRecordValues() {
|
||||||
var upgradeDate = $("#upgradeRecordDate").val();
|
var upgradeDate = $("#upgradeRecordDate").val();
|
||||||
var upgradeMileage = $("#upgradeRecordMileage").val();
|
var upgradeMileage = parseInt(globalParseFloat($("#upgradeRecordMileage").val())).toString();
|
||||||
var upgradeDescription = $("#upgradeRecordDescription").val();
|
var upgradeDescription = $("#upgradeRecordDescription").val();
|
||||||
var upgradeCost = $("#upgradeRecordCost").val();
|
var upgradeCost = $("#upgradeRecordCost").val();
|
||||||
var upgradeNotes = $("#upgradeRecordNotes").val();
|
var upgradeNotes = $("#upgradeRecordNotes").val();
|
||||||
|
|||||||
Reference in New Issue
Block a user