Merge branch 'main' into Hargata/documentation

This commit is contained in:
DESKTOP-T0O5CDB\DESK-555BD
2024-01-25 17:01:31 -07:00
32 changed files with 323 additions and 42 deletions

View File

@@ -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>();

View File

@@ -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]

View File

@@ -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
} }

View File

@@ -2,6 +2,7 @@
{ {
public enum ReminderMonthInterval public enum ReminderMonthInterval
{ {
OneMonth = 1,
ThreeMonths = 3, ThreeMonths = 3,
SixMonths = 6, SixMonths = 6,
OneYear = 12, OneYear = 12,

View File

@@ -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;

View File

@@ -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)

View File

@@ -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}
};
}
} }
} }

View File

@@ -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; }
} }
} }

View File

@@ -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>();
} }
} }

View File

@@ -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 }; }
} }
} }

View File

@@ -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

View File

@@ -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.

View File

@@ -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')
] ]
} }
] ]

View File

@@ -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>
} }

View File

@@ -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]');
} }

View File

@@ -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]');
} }

View File

@@ -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>

View File

@@ -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>
} }

View File

@@ -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>

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

View File

@@ -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();

View File

@@ -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();

View File

@@ -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
} }
} }

View File

@@ -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;

View File

@@ -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();

View File

@@ -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();

View File

@@ -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
} }

View File

@@ -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();