Compare commits

...

27 Commits

Author SHA1 Message Date
Hargata Softworks
4a1e41cd54 Merge pull request #393 from hargata/Hargata/multi.odo.edit
Added functionality to edit multiple odometer records.
2024-03-13 10:18:13 -06:00
DESKTOP-T0O5CDB\DESK-555BD
888ec5cbbe Added functionality to edit multiple odometer records. 2024-03-13 10:16:04 -06:00
Hargata Softworks
f8f044d0cc Merge pull request #391 from hargata/Hargata/odo.changes
width issue
2024-03-11 11:42:26 -06:00
DESKTOP-GENO133\IvanPlex
36148c5539 width issue 2024-03-11 11:41:53 -06:00
Hargata Softworks
030dde0d64 Merge pull request #390 from hargata/Hargata/odo.changes
fixed admin panel text and button spacing.
2024-03-11 11:40:28 -06:00
DESKTOP-GENO133\IvanPlex
99faa951f9 fixed admin panel text and button spacing. 2024-03-11 11:39:56 -06:00
Hargata Softworks
7fb3a80ea3 Merge pull request #389 from hargata/Hargata/odo.changes
added support for meta key for MacOS users
2024-03-11 10:37:40 -06:00
DESKTOP-GENO133\IvanPlex
f277048aa9 added method to force recalculate distances 2024-03-11 10:34:42 -06:00
DESKTOP-GENO133\IvanPlex
34e3b8e145 added support for meta key for MacOS users 2024-03-11 10:01:24 -06:00
Hargata Softworks
91e8526b8e Merge pull request #388 from hargata/Hargata/odo.changes
removed logic that can cause bad data.
2024-03-10 14:19:54 -06:00
DESKTOP-GENO133\IvanPlex
926188ef64 removed logic that can cause bad data. 2024-03-10 14:18:24 -06:00
Hargata Softworks
1b5fc6729e Merge pull request #387 from hargata/Hargata/odo.changes
Odometer Changes
2024-03-10 14:13:35 -06:00
DESKTOP-GENO133\IvanPlex
3ac53ea144 added method to calculate distance. 2024-03-10 11:59:38 -06:00
DESKTOP-GENO133\IvanPlex
4dc3b4f741 fixed csv import 2024-03-10 09:05:15 -06:00
DESKTOP-GENO133\IvanPlex
7c1e6bd45c made API endpoints copyable 2024-03-10 08:56:41 -06:00
DESKTOP-GENO133\IvanPlex
00b3145e79 display distance traveled. 2024-03-10 08:02:44 -06:00
DESKTOP-GENO133\IvanPlex
def9a7770f odometer to trips improvement. 2024-03-10 06:33:39 -06:00
Hargata Softworks
5ba0bd5771 Merge pull request #381 from hargata/Hargata/fix.print
7z
2024-03-08 16:19:54 -07:00
DESKTOP-T0O5CDB\DESK-555BD
adc2de07f7 7z 2024-03-08 16:19:36 -07:00
Hargata Softworks
e12f528635 Merge pull request #380 from hargata/Hargata/fix.print
fixed the date column.
2024-03-08 16:17:46 -07:00
DESKTOP-T0O5CDB\DESK-555BD
c96ac53b91 fixed the date column. 2024-03-08 16:17:20 -07:00
Hargata Softworks
8c15bd5534 Merge pull request #378 from hargata/Hargata/fix.print
Improved Attachment Export
2024-03-08 11:49:57 -07:00
DESKTOP-T0O5CDB\DESK-555BD
90755d68b3 Improved Attachment Export 2024-03-08 11:49:30 -07:00
Hargata Softworks
94dfe9a0bb Merge pull request #377 from hargata/Hargata/fix.print
Added flex shrink
2024-03-08 11:13:36 -07:00
DESKTOP-T0O5CDB\DESK-555BD
9dbcf71856 Added flex shrink 2024-03-08 11:11:37 -07:00
Hargata Softworks
728fc3593f Merge pull request #374 from hargata/Hargata/fix.print
Fixed black border issue when printing
2024-03-07 22:49:59 -07:00
DESKTOP-T0O5CDB\DESK-555BD
1347585af2 Fixed black border issue when printing 2024-03-07 22:49:11 -07:00
28 changed files with 477 additions and 145 deletions

View File

@@ -27,6 +27,7 @@ namespace CarCareTracker.Controllers
private readonly IReminderHelper _reminderHelper;
private readonly IGasHelper _gasHelper;
private readonly IUserLogic _userLogic;
private readonly IOdometerLogic _odometerLogic;
private readonly IFileHelper _fileHelper;
private readonly IMailHelper _mailHelper;
private readonly IConfigHelper _config;
@@ -46,7 +47,8 @@ namespace CarCareTracker.Controllers
IMailHelper mailHelper,
IFileHelper fileHelper,
IConfigHelper config,
IUserLogic userLogic)
IUserLogic userLogic,
IOdometerLogic odometerLogic)
{
_dataAccess = dataAccess;
_noteDataAccess = noteDataAccess;
@@ -63,6 +65,7 @@ namespace CarCareTracker.Controllers
_gasHelper = gasHelper;
_reminderHelper = reminderHelper;
_userLogic = userLogic;
_odometerLogic = odometerLogic;
_fileHelper = fileHelper;
_config = config;
}
@@ -138,7 +141,7 @@ namespace CarCareTracker.Controllers
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
Mileage = int.Parse(input.Odometer)
};
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(odometerRecord);
_odometerLogic.AutoInsertOdometerRecord(odometerRecord);
}
response.Success = true;
response.Message = "Service Record Added";
@@ -205,7 +208,7 @@ namespace CarCareTracker.Controllers
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
Mileage = int.Parse(input.Odometer)
};
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(odometerRecord);
_odometerLogic.AutoInsertOdometerRecord(odometerRecord);
}
response.Success = true;
response.Message = "Repair Record Added";
@@ -272,7 +275,7 @@ namespace CarCareTracker.Controllers
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
Mileage = int.Parse(input.Odometer)
};
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(odometerRecord);
_odometerLogic.AutoInsertOdometerRecord(odometerRecord);
}
response.Success = true;
response.Message = "Upgrade Record Added";
@@ -353,7 +356,12 @@ namespace CarCareTracker.Controllers
public IActionResult OdometerRecords(int vehicleId)
{
var vehicleRecords = _odometerRecordDataAccess.GetOdometerRecordsByVehicleId(vehicleId);
var result = vehicleRecords.Select(x => new OdometerRecordExportModel { Date = x.Date.ToShortDateString(), Odometer = x.Mileage.ToString(), Notes = x.Notes });
//determine if conversion is needed.
if (vehicleRecords.All(x => x.InitialMileage == default))
{
vehicleRecords = _odometerLogic.AutoConvertOdometerRecord(vehicleRecords);
}
var result = vehicleRecords.Select(x => new OdometerRecordExportModel { Date = x.Date.ToShortDateString(), InitialOdometer = x.InitialMileage.ToString(), Odometer = x.Mileage.ToString(), Notes = x.Notes });
return Json(result);
}
[TypeFilter(typeof(CollaboratorFilter))]
@@ -384,6 +392,7 @@ namespace CarCareTracker.Controllers
VehicleId = vehicleId,
Date = DateTime.Parse(input.Date),
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
InitialMileage = (string.IsNullOrWhiteSpace(input.InitialOdometer) || int.Parse(input.InitialOdometer) == default) ? _odometerLogic.GetLastOdometerRecordMileage(vehicleId, new List<OdometerRecord>()) : int.Parse(input.InitialOdometer),
Mileage = int.Parse(input.Odometer)
};
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(odometerRecord);
@@ -466,7 +475,7 @@ namespace CarCareTracker.Controllers
Notes = string.IsNullOrWhiteSpace(input.Notes) ? "" : input.Notes,
Mileage = int.Parse(input.Odometer)
};
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(odometerRecord);
_odometerLogic.AutoInsertOdometerRecord(odometerRecord);
}
response.Success = true;
response.Message = "Gas Record Added";

View File

@@ -36,6 +36,7 @@ namespace CarCareTracker.Controllers
private readonly IReminderHelper _reminderHelper;
private readonly IReportHelper _reportHelper;
private readonly IUserLogic _userLogic;
private readonly IOdometerLogic _odometerLogic;
private readonly IExtraFieldDataAccess _extraFieldDataAccess;
public VehicleController(ILogger<VehicleController> logger,
@@ -57,6 +58,7 @@ namespace CarCareTracker.Controllers
IOdometerRecordDataAccess odometerRecordDataAccess,
IExtraFieldDataAccess extraFieldDataAccess,
IUserLogic userLogic,
IOdometerLogic odometerLogic,
IWebHostEnvironment webEnv,
IConfigHelper config)
{
@@ -79,6 +81,7 @@ namespace CarCareTracker.Controllers
_odometerRecordDataAccess = odometerRecordDataAccess;
_extraFieldDataAccess = extraFieldDataAccess;
_userLogic = userLogic;
_odometerLogic = odometerLogic;
_webEnv = webEnv;
_config = config;
}
@@ -265,7 +268,7 @@ namespace CarCareTracker.Controllers
var vehicleRecords = _odometerRecordDataAccess.GetOdometerRecordsByVehicleId(vehicleId);
if (vehicleRecords.Any())
{
var exportData = vehicleRecords.Select(x => new OdometerRecordExportModel { Date = x.Date.ToShortDateString(), Notes = x.Notes, Odometer = x.Mileage.ToString(), Tags = string.Join(" ", x.Tags) });
var exportData = vehicleRecords.Select(x => new OdometerRecordExportModel { Date = x.Date.ToShortDateString(), Notes = x.Notes, InitialOdometer = x.InitialMileage.ToString(), Odometer = x.Mileage.ToString(), Tags = string.Join(" ", x.Tags) });
using (var writer = new StreamWriter(fullExportFilePath))
{
using (var csv = new CsvWriter(writer, CultureInfo.InvariantCulture))
@@ -460,7 +463,7 @@ namespace CarCareTracker.Controllers
_gasRecordDataAccess.SaveGasRecordToVehicle(convertedRecord);
if (_config.GetUserConfig(User).EnableAutoOdometerInsert)
{
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
_odometerLogic.AutoInsertOdometerRecord(new OdometerRecord
{
Date = convertedRecord.Date,
VehicleId = convertedRecord.VehicleId,
@@ -485,7 +488,7 @@ namespace CarCareTracker.Controllers
_serviceRecordDataAccess.SaveServiceRecordToVehicle(convertedRecord);
if (_config.GetUserConfig(User).EnableAutoOdometerInsert)
{
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
_odometerLogic.AutoInsertOdometerRecord(new OdometerRecord
{
Date = convertedRecord.Date,
VehicleId = convertedRecord.VehicleId,
@@ -500,6 +503,7 @@ namespace CarCareTracker.Controllers
{
VehicleId = vehicleId,
Date = DateTime.Parse(importModel.Date),
InitialMileage = string.IsNullOrWhiteSpace(importModel.InitialOdometer) ? 0 : decimal.ToInt32(decimal.Parse(importModel.InitialOdometer, NumberStyles.Any)),
Mileage = decimal.ToInt32(decimal.Parse(importModel.Odometer, NumberStyles.Any)),
Notes = string.IsNullOrWhiteSpace(importModel.Notes) ? "" : importModel.Notes,
Tags = string.IsNullOrWhiteSpace(importModel.Tags) ? [] : importModel.Tags.Split(" ").ToList()
@@ -540,7 +544,7 @@ namespace CarCareTracker.Controllers
_collisionRecordDataAccess.SaveCollisionRecordToVehicle(convertedRecord);
if (_config.GetUserConfig(User).EnableAutoOdometerInsert)
{
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
_odometerLogic.AutoInsertOdometerRecord(new OdometerRecord
{
Date = convertedRecord.Date,
VehicleId = convertedRecord.VehicleId,
@@ -564,7 +568,7 @@ namespace CarCareTracker.Controllers
_upgradeRecordDataAccess.SaveUpgradeRecordToVehicle(convertedRecord);
if (_config.GetUserConfig(User).EnableAutoOdometerInsert)
{
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
_odometerLogic.AutoInsertOdometerRecord(new OdometerRecord
{
Date = convertedRecord.Date,
VehicleId = convertedRecord.VehicleId,
@@ -646,7 +650,7 @@ namespace CarCareTracker.Controllers
{
if (gasRecord.Id == default && _config.GetUserConfig(User).EnableAutoOdometerInsert)
{
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
_odometerLogic.AutoInsertOdometerRecord(new OdometerRecord
{
Date = DateTime.Parse(gasRecord.Date),
VehicleId = gasRecord.VehicleId,
@@ -734,7 +738,7 @@ namespace CarCareTracker.Controllers
{
if (serviceRecord.Id == default && _config.GetUserConfig(User).EnableAutoOdometerInsert)
{
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
_odometerLogic.AutoInsertOdometerRecord(new OdometerRecord
{
Date = DateTime.Parse(serviceRecord.Date),
VehicleId = serviceRecord.VehicleId,
@@ -811,7 +815,7 @@ namespace CarCareTracker.Controllers
{
if (collisionRecord.Id == default && _config.GetUserConfig(User).EnableAutoOdometerInsert)
{
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
_odometerLogic.AutoInsertOdometerRecord(new OdometerRecord
{
Date = DateTime.Parse(collisionRecord.Date),
VehicleId = collisionRecord.VehicleId,
@@ -1004,7 +1008,7 @@ namespace CarCareTracker.Controllers
{
MonthName = x.Key.MonthName,
Cost = x.Sum(y => y.Cost),
DistanceTraveled = x.Any(y => y.MinMileage != default) ? x.Max(y => y.MaxMileage) - x.Where(y => y.MinMileage != default).Min(y => y.MinMileage) : 0
DistanceTraveled = x.Max(y => y.DistanceTraveled)
}).ToList();
//get reminders
var reminders = GetRemindersAndUrgency(vehicleId, DateTime.Now);
@@ -1132,6 +1136,7 @@ namespace CarCareTracker.Controllers
var records = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId).Where(x => x.Files.Any());
attachmentData.AddRange(records.Select(x => new GenericReportModel
{
DataType = ImportMode.ServiceRecord,
Date = x.Date,
Odometer = x.Mileage,
Files = x.Files
@@ -1142,6 +1147,7 @@ namespace CarCareTracker.Controllers
var records = _collisionRecordDataAccess.GetCollisionRecordsByVehicleId(vehicleId).Where(x => x.Files.Any());
attachmentData.AddRange(records.Select(x => new GenericReportModel
{
DataType = ImportMode.RepairRecord,
Date = x.Date,
Odometer = x.Mileage,
Files = x.Files
@@ -1152,6 +1158,7 @@ namespace CarCareTracker.Controllers
var records = _upgradeRecordDataAccess.GetUpgradeRecordsByVehicleId(vehicleId).Where(x => x.Files.Any());
attachmentData.AddRange(records.Select(x => new GenericReportModel
{
DataType = ImportMode.UpgradeRecord,
Date = x.Date,
Odometer = x.Mileage,
Files = x.Files
@@ -1162,6 +1169,7 @@ namespace CarCareTracker.Controllers
var records = _gasRecordDataAccess.GetGasRecordsByVehicleId(vehicleId).Where(x => x.Files.Any());
attachmentData.AddRange(records.Select(x => new GenericReportModel
{
DataType = ImportMode.GasRecord,
Date = x.Date,
Odometer = x.Mileage,
Files = x.Files
@@ -1172,6 +1180,7 @@ namespace CarCareTracker.Controllers
var records = _taxRecordDataAccess.GetTaxRecordsByVehicleId(vehicleId).Where(x => x.Files.Any());
attachmentData.AddRange(records.Select(x => new GenericReportModel
{
DataType = ImportMode.TaxRecord,
Date = x.Date,
Odometer = 0,
Files = x.Files
@@ -1182,6 +1191,7 @@ namespace CarCareTracker.Controllers
var records = _odometerRecordDataAccess.GetOdometerRecordsByVehicleId(vehicleId).Where(x => x.Files.Any());
attachmentData.AddRange(records.Select(x => new GenericReportModel
{
DataType = ImportMode.OdometerRecord,
Date = x.Date,
Odometer = x.Mileage,
Files = x.Files
@@ -1221,7 +1231,8 @@ namespace CarCareTracker.Controllers
try
{
vehicleHistory.DaysOwned = (DateTime.Parse(endDate) - DateTime.Parse(vehicleHistory.VehicleData.PurchaseDate)).Days.ToString("N0");
} catch (Exception ex)
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
vehicleHistory.DaysOwned = string.Empty;
@@ -1370,7 +1381,7 @@ namespace CarCareTracker.Controllers
{
MonthName = x.Key.MonthName,
Cost = x.Sum(y => y.Cost),
DistanceTraveled = x.Any(y => y.MinMileage != default) ? x.Max(y => y.MaxMileage) - x.Where(y => y.MinMileage != default).Min(y => y.MinMileage) : 0
DistanceTraveled = x.Max(y => y.DistanceTraveled)
}).ToList();
return PartialView("_GasCostByMonthReport", groupedRecord);
}
@@ -1411,7 +1422,7 @@ namespace CarCareTracker.Controllers
private int GetMinMileage(int vehicleId)
{
var numbersArray = new List<int>();
var serviceRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId).Where(x=>x.Mileage != default);
var serviceRecords = _serviceRecordDataAccess.GetServiceRecordsByVehicleId(vehicleId).Where(x => x.Mileage != default);
if (serviceRecords.Any())
{
numbersArray.Add(serviceRecords.Min(x => x.Mileage));
@@ -1597,7 +1608,7 @@ namespace CarCareTracker.Controllers
{
if (upgradeRecord.Id == default && _config.GetUserConfig(User).EnableAutoOdometerInsert)
{
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
_odometerLogic.AutoInsertOdometerRecord(new OdometerRecord
{
Date = DateTime.Parse(upgradeRecord.Date),
VehicleId = upgradeRecord.VehicleId,
@@ -1696,13 +1707,14 @@ namespace CarCareTracker.Controllers
public IActionResult PinNotes(List<int> noteIds, bool isToggle = false, bool pinStatus = false)
{
var result = false;
foreach(int noteId in noteIds)
foreach (int noteId in noteIds)
{
var existingNote = _noteDataAccess.GetNoteById(noteId);
if (isToggle)
{
existingNote.Pinned = !existingNote.Pinned;
} else
}
else
{
existingNote.Pinned = pinStatus;
}
@@ -1962,7 +1974,7 @@ namespace CarCareTracker.Controllers
{
if (_config.GetUserConfig(User).EnableAutoOdometerInsert)
{
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(new OdometerRecord
_odometerLogic.AutoInsertOdometerRecord(new OdometerRecord
{
Date = DateTime.Now,
VehicleId = existingRecord.VehicleId,
@@ -2061,10 +2073,23 @@ namespace CarCareTracker.Controllers
#endregion
#region "Odometer Records"
[TypeFilter(typeof(CollaboratorFilter))]
[HttpPost]
public IActionResult ForceRecalculateDistanceByVehicleId(int vehicleId)
{
var result = _odometerRecordDataAccess.GetOdometerRecordsByVehicleId(vehicleId);
result = _odometerLogic.AutoConvertOdometerRecord(result);
return Json(result.Any());
}
[TypeFilter(typeof(CollaboratorFilter))]
[HttpGet]
public IActionResult GetOdometerRecordsByVehicleId(int vehicleId)
{
var result = _odometerRecordDataAccess.GetOdometerRecordsByVehicleId(vehicleId);
//determine if conversion is needed.
if (result.All(x => x.InitialMileage == default))
{
result = _odometerLogic.AutoConvertOdometerRecord(result);
}
bool _useDescending = _config.GetUserConfig(User).UseDescending;
if (_useDescending)
{
@@ -2085,9 +2110,59 @@ namespace CarCareTracker.Controllers
return Json(result);
}
[HttpGet]
public IActionResult GetAddOdometerRecordPartialView()
public IActionResult GetAddOdometerRecordPartialView(int vehicleId)
{
return PartialView("_OdometerRecordModal", new OdometerRecordInput() { ExtraFields = _extraFieldDataAccess.GetExtraFieldsById((int)ImportMode.OdometerRecord).ExtraFields });
return PartialView("_OdometerRecordModal", new OdometerRecordInput() { InitialMileage = _odometerLogic.GetLastOdometerRecordMileage(vehicleId, new List<OdometerRecord>()), ExtraFields = _extraFieldDataAccess.GetExtraFieldsById((int)ImportMode.OdometerRecord).ExtraFields });
}
[HttpPost]
public IActionResult GetOdometerRecordsEditModal(List<int> recordIds)
{
return PartialView("_OdometerRecordsModal", new OdometerRecordEditModel { RecordIds = recordIds });
}
[HttpPost]
public IActionResult SaveMultipleOdometerRecords(OdometerRecordEditModel editModel)
{
var dateIsEdited = editModel.EditRecord.Date != default;
var initialMileageIsEdited = editModel.EditRecord.InitialMileage != default;
var mileageIsEdited = editModel.EditRecord.Mileage != default;
var noteIsEdited = !string.IsNullOrWhiteSpace(editModel.EditRecord.Notes);
var tagsIsEdited = editModel.EditRecord.Tags.Any();
//handle clear overrides
if (tagsIsEdited && editModel.EditRecord.Tags.Contains("---"))
{
editModel.EditRecord.Tags = new List<string>();
}
if (noteIsEdited && editModel.EditRecord.Notes == "---")
{
editModel.EditRecord.Notes = "";
}
bool result = false;
foreach (int recordId in editModel.RecordIds)
{
var existingRecord = _odometerRecordDataAccess.GetOdometerRecordById(recordId);
if (dateIsEdited)
{
existingRecord.Date = editModel.EditRecord.Date;
}
if (initialMileageIsEdited)
{
existingRecord.InitialMileage = editModel.EditRecord.InitialMileage;
}
if (mileageIsEdited)
{
existingRecord.Mileage = editModel.EditRecord.Mileage;
}
if (noteIsEdited)
{
existingRecord.Notes = editModel.EditRecord.Notes;
}
if (tagsIsEdited)
{
existingRecord.Tags = editModel.EditRecord.Tags;
}
result = _odometerRecordDataAccess.SaveOdometerRecordToVehicle(existingRecord);
}
return Json(result);
}
[HttpGet]
public IActionResult GetOdometerRecordForEditById(int odometerRecordId)
@@ -2098,6 +2173,7 @@ namespace CarCareTracker.Controllers
{
Id = result.Id,
Date = result.Date.ToShortDateString(),
InitialMileage = result.InitialMileage,
Mileage = result.Mileage,
Notes = result.Notes,
VehicleId = result.VehicleId,
@@ -2459,13 +2535,15 @@ namespace CarCareTracker.Controllers
{
var existingPreference = existingUserColumnPreference.Single();
existingPreference.VisibleColumns = columnPreference.VisibleColumns;
} else
}
else
{
userConfig.UserColumnPreferences.Add(columnPreference);
}
var result = _config.SaveUserConfig(User, userConfig);
return Json(result);
} catch (Exception ex)
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
return Json(false);

View File

@@ -173,7 +173,7 @@ namespace CarCareTracker.Helper
foreach (UploadedFiles file in reportModel.Files)
{
var fileToCopy = GetFullFilePath(file.Location);
var destFileName = $"{tempPath}/{fileIndex}{Path.GetExtension(file.Location)}";
var destFileName = $"{tempPath}/{fileIndex}_{reportModel.DataType}_{reportModel.Date.ToString("yyyy-MM-dd")}_{file.Name}{Path.GetExtension(file.Location)}";
File.Copy(fileToCopy, destFileName);
fileIndex++;
}

View File

@@ -25,8 +25,7 @@ namespace CarCareTracker.Helper
MonthId = x.Key,
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
Cost = 0,
MaxMileage = x.Max(y => y.Mileage),
MinMileage = x.Min(y => y.Mileage)
DistanceTraveled = x.Sum(y=>y.DistanceTraveled)
});
}
public IEnumerable<CostForVehicleByMonth> GetServiceRecordSum(List<ServiceRecord> serviceRecords, int year = 0)
@@ -39,9 +38,7 @@ namespace CarCareTracker.Helper
{
MonthId = x.Key,
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
Cost = x.Sum(y => y.Cost),
MaxMileage = x.Max(y=>y.Mileage),
MinMileage = x.Min(y=>y.Mileage)
Cost = x.Sum(y => y.Cost)
});
}
public IEnumerable<CostForVehicleByMonth> GetRepairRecordSum(List<CollisionRecord> repairRecords, int year = 0)
@@ -54,9 +51,7 @@ namespace CarCareTracker.Helper
{
MonthId = x.Key,
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
Cost = x.Sum(y => y.Cost),
MaxMileage = x.Max(y => y.Mileage),
MinMileage = x.Min(y => y.Mileage)
Cost = x.Sum(y => y.Cost)
});
}
public IEnumerable<CostForVehicleByMonth> GetUpgradeRecordSum(List<UpgradeRecord> upgradeRecords, int year = 0)
@@ -69,9 +64,7 @@ namespace CarCareTracker.Helper
{
MonthId = x.Key,
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
Cost = x.Sum(y => y.Cost),
MaxMileage = x.Max(y => y.Mileage),
MinMileage = x.Min(y => y.Mileage)
Cost = x.Sum(y => y.Cost)
});
}
public IEnumerable<CostForVehicleByMonth> GetGasRecordSum(List<GasRecord> gasRecords, int year = 0)
@@ -84,9 +77,7 @@ namespace CarCareTracker.Helper
{
MonthId = x.Key,
MonthName = CultureInfo.CurrentCulture.DateTimeFormat.GetMonthName(x.Key),
Cost = x.Sum(y => y.Cost),
MaxMileage = x.Max(y => y.Mileage),
MinMileage = x.Min(y => y.Mileage)
Cost = x.Sum(y => y.Cost)
});
}
public IEnumerable<CostForVehicleByMonth> GetTaxRecordSum(List<TaxRecord> taxRecords, int year = 0)

View File

@@ -8,7 +8,7 @@ namespace CarCareTracker.Helper
/// </summary>
public static class StaticHelper
{
public static string VersionNumber = "1.2.5";
public static string VersionNumber = "1.2.6";
public static string DbName = "data/cartracker.db";
public static string UserConfigPath = "config/userConfig.json";
public static string GenericErrorMessage = "An error occurred, please try again later";

67
Logic/OdometerLogic.cs Normal file
View File

@@ -0,0 +1,67 @@
using CarCareTracker.External.Interfaces;
using CarCareTracker.Models;
namespace CarCareTracker.Logic
{
public interface IOdometerLogic
{
int GetLastOdometerRecordMileage(int vehicleId, List<OdometerRecord> odometerRecords);
bool AutoInsertOdometerRecord(OdometerRecord odometer);
List<OdometerRecord> AutoConvertOdometerRecord(List<OdometerRecord> odometerRecords);
}
public class OdometerLogic: IOdometerLogic
{
private readonly IOdometerRecordDataAccess _odometerRecordDataAccess;
private readonly ILogger<IOdometerLogic> _logger;
public OdometerLogic(IOdometerRecordDataAccess odometerRecordDataAccess, ILogger<IOdometerLogic> logger)
{
_odometerRecordDataAccess = odometerRecordDataAccess;
_logger = logger;
}
public int GetLastOdometerRecordMileage(int vehicleId, List<OdometerRecord> odometerRecords)
{
if (!odometerRecords.Any())
{
odometerRecords = _odometerRecordDataAccess.GetOdometerRecordsByVehicleId(vehicleId);
}
if (!odometerRecords.Any())
{
//no existing odometer records for this vehicle.
return 0;
}
return odometerRecords.Max(x => x.Mileage);
}
public bool AutoInsertOdometerRecord(OdometerRecord odometer)
{
var lastReportedMileage = GetLastOdometerRecordMileage(odometer.VehicleId, new List<OdometerRecord>());
odometer.InitialMileage = lastReportedMileage != default ? lastReportedMileage : odometer.Mileage;
var result = _odometerRecordDataAccess.SaveOdometerRecordToVehicle(odometer);
return result;
}
public List<OdometerRecord> AutoConvertOdometerRecord(List<OdometerRecord> odometerRecords)
{
//perform ordering
odometerRecords = odometerRecords.OrderBy(x => x.Date).ThenBy(x => x.Mileage).ToList();
int previousMileage = 0;
for (int i = 0; i < odometerRecords.Count; i++)
{
var currentObject = odometerRecords[i];
if (previousMileage == default)
{
//first record
currentObject.InitialMileage = currentObject.Mileage;
}
else
{
//subsequent records
currentObject.InitialMileage = previousMileage;
}
//save to db.
_odometerRecordDataAccess.SaveOdometerRecordToVehicle(currentObject);
previousMileage = currentObject.Mileage;
}
return odometerRecords;
}
}
}

View File

@@ -10,6 +10,7 @@ namespace CarCareTracker.MapProfile
Map(m => m.Date).Name(["date", "fuelup_date"]);
Map(m => m.DateCreated).Name(["datecreated"]);
Map(m => m.DateModified).Name(["datemodified"]);
Map(m => m.InitialOdometer).Name(["initialodometer"]);
Map(m => m.Odometer).Name(["odometer"]);
Map(m => m.FuelConsumed).Name(["gallons", "liters", "litres", "consumption", "quantity", "fuelconsumed"]);
Map(m => m.Cost).Name(["cost", "total cost", "totalcost", "total price"]);

View File

@@ -5,7 +5,9 @@
public int Id { get; set; }
public int VehicleId { get; set; }
public DateTime Date { get; set; }
public int InitialMileage { get; set; }
public int Mileage { get; set; }
public int DistanceTraveled { get { return Mileage - InitialMileage; } }
public string Notes { get; set; }
public List<string> Tags { get; set; } = new List<string>();
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();

View File

@@ -0,0 +1,8 @@
namespace CarCareTracker.Models
{
public class OdometerRecordEditModel
{
public List<int> RecordIds { get; set; } = new List<int>();
public OdometerRecord EditRecord { get; set; } = new OdometerRecord();
}
}

View File

@@ -5,11 +5,12 @@
public int Id { get; set; }
public int VehicleId { get; set; }
public string Date { get; set; } = DateTime.Now.ToShortDateString();
public int InitialMileage { get; set; }
public int Mileage { get; set; }
public string Notes { get; set; }
public List<UploadedFiles> Files { get; set; } = new List<UploadedFiles>();
public List<string> Tags { get; set; } = new List<string>();
public List<ExtraField> ExtraFields { get; set; } = new List<ExtraField>();
public OdometerRecord ToOdometerRecord() { return new OdometerRecord { Id = Id, VehicleId = VehicleId, Date = DateTime.Parse(Date), Mileage = Mileage, Notes = Notes, Files = Files, Tags = Tags, ExtraFields = ExtraFields }; }
public OdometerRecord ToOdometerRecord() { return new OdometerRecord { Id = Id, VehicleId = VehicleId, Date = DateTime.Parse(Date), Mileage = Mileage, Notes = Notes, Files = Files, Tags = Tags, ExtraFields = ExtraFields, InitialMileage = InitialMileage }; }
}
}

View File

@@ -5,8 +5,6 @@
public int MonthId { get; set; }
public string MonthName { get; set; }
public decimal Cost { get; set; }
public int MaxMileage { get; set; }
public int MinMileage { get; set; }
public int DistanceTraveled { get; set; }
}
}

View File

@@ -11,6 +11,7 @@
public string Type { get; set; }
public string Priority { get; set; }
public string Progress { get; set; }
public string InitialOdometer { get; set; }
public string Odometer { get; set; }
public string Description { get; set; }
public string Notes { get; set; }
@@ -50,6 +51,7 @@
public class OdometerRecordExportModel
{
public string Date { get; set; }
public string InitialOdometer { get; set; }
public string Odometer { get; set; }
public string Notes { get; set; }
public string Tags { get; set; }

View File

@@ -69,6 +69,7 @@ builder.Services.AddSingleton<ITranslationHelper, TranslationHelper>();
//configure logic
builder.Services.AddSingleton<ILoginLogic, LoginLogic>();
builder.Services.AddSingleton<IUserLogic, UserLogic>();
builder.Services.AddSingleton<IOdometerLogic, OdometerLogic>();
if (!Directory.Exists("data"))
{

View File

@@ -16,7 +16,7 @@
<div class="col-1">
<h6>Method</h6>
</div>
<div class="col-5">
<div class="col-5 copyable">
<h6>Endpoint</h6>
</div>
<div class="col-3">
@@ -30,7 +30,7 @@
<div class="col-1">
GET
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicles</code>
</div>
<div class="col-3">
@@ -44,7 +44,7 @@
<div class="col-1">
GET
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/servicerecords</code>
</div>
<div class="col-3">
@@ -58,7 +58,7 @@
<div class="col-1">
POST
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/servicerecords/add</code>
</div>
<div class="col-3">
@@ -80,7 +80,7 @@
<div class="col-1">
GET
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/repairrecords</code>
</div>
<div class="col-3">
@@ -94,7 +94,7 @@
<div class="col-1">
POST
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/repairrecords/add</code>
</div>
<div class="col-3">
@@ -116,7 +116,7 @@
<div class="col-1">
GET
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/upgraderecords</code>
</div>
<div class="col-3">
@@ -130,7 +130,7 @@
<div class="col-1">
POST
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/upgraderecords/add</code>
</div>
<div class="col-3">
@@ -152,7 +152,7 @@
<div class="col-1">
GET
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/taxrecords</code>
</div>
<div class="col-3">
@@ -166,7 +166,7 @@
<div class="col-1">
POST
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/taxrecords/add</code>
</div>
<div class="col-3">
@@ -187,7 +187,7 @@
<div class="col-1">
GET
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/gasrecords</code>
</div>
<div class="col-3">
@@ -205,7 +205,7 @@
<div class="col-1">
POST
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/gasrecords/add</code>
</div>
<div class="col-3">
@@ -229,7 +229,7 @@
<div class="col-1">
GET
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/reminders</code>
</div>
<div class="col-3">
@@ -245,7 +245,7 @@
<div class="col-1">
GET
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/reminders/send</code>
</div>
<div class="col-3">
@@ -260,7 +260,7 @@
<div class="col-1">
GET
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/makebackup</code>
</div>
<div class="col-3">
@@ -275,7 +275,7 @@
<div class="col-1">
GET
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/odometerrecords</code>
</div>
<div class="col-3">
@@ -289,7 +289,7 @@
<div class="col-1">
GET
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/odometerrecords/latest</code>
</div>
<div class="col-3">
@@ -303,7 +303,7 @@
<div class="col-1">
POST
</div>
<div class="col-5">
<div class="col-5 copyable">
<code>/api/vehicle/odometerrecords/add</code>
</div>
<div class="col-3">
@@ -314,8 +314,14 @@
<br />
Body(form-data): {<br />
date - Date to be entered<br />
initialOdometer - Initial Odometer reading(optional)<br />
odometer - Odometer reading<br />
notes - notes(optional)<br />
}
</div>
</div>
</div>
<script>
$('.copyable').on('click', function (e) {
copyToClipboard(e.currentTarget);
})
</script>

View File

@@ -15,11 +15,11 @@
}
@model AdminViewModel
<div class="container">
<div class="row">
<div class="col-1">
<div class="row d-flex align-items-center justify-content-between justify-content-md-start">
<div class="col-2 col-md-1">
<a href="/Home" class="btn btn-secondary btn-md mt-1 mb-1"><i class="bi bi-arrow-left-square"></i></a>
</div>
<div class="col-11">
<div class="col-6 col-md-7 text-end text-md-start">
<span class="display-6">@translator.Translate(userLanguage, "Admin Panel")</span>
</div>
</div>
@@ -120,11 +120,6 @@
}
})
}
function copyToClipboard(e) {
var textToCopy = e.textContent;
navigator.clipboard.writeText(textToCopy);
successToast("Copied to Clipboard");
}
function generateNewToken() {
Swal.fire({
title: 'Generate Token',

View File

@@ -108,13 +108,13 @@
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2 col-xl-1 flex-grow-1" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-2 flex-grow-1" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 col-xl-4 flex-grow-1" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2 flex-grow-1" data-column="cost" onclick="toggleSort('accident-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3 flex-grow-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 col-xl-4 flex-grow-1 flex-shrink-1" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="cost" onclick="toggleSort('accident-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3 flex-grow-1 flex-shrink-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields)
{
<th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
<th scope="col" style='display:none;' class="col-2 flex-grow-1 flex-shrink-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
}
</tr>
</thead>
@@ -123,13 +123,13 @@
{
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@collisionRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditCollisionRecordModal,@collisionRecord.Id)" data-tags='@string.Join(" ", collisionRecord.Tags)'>
<td class="col-2 col-xl-1 flex-grow-1" data-column="date" data-date="@StaticHelper.GetEpochFromDateTime(collisionRecord.Date)">@collisionRecord.Date.ToShortDateString()</td>
<td class="col-2 flex-grow-1" data-column="odometer">@collisionRecord.Mileage</td>
<td class="col-3 col-xl-4 flex-grow-1" data-column="description">@collisionRecord.Description</td>
<td class="col-2 flex-grow-1" data-column="cost" data-record-type="cost">@((hideZero && collisionRecord.Cost == default) ? "---" : collisionRecord.Cost.ToString("C"))</td>
<td class="col-3 flex-grow-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(collisionRecord.Notes)</td>
<td class="col-2 flex-grow-1 flex-shrink-1" data-column="odometer">@collisionRecord.Mileage</td>
<td class="col-3 col-xl-4 flex-grow-1 flex-shrink-1" data-column="description">@collisionRecord.Description</td>
<td class="col-2 flex-grow-1 flex-shrink-1" data-column="cost" data-record-type="cost">@((hideZero && collisionRecord.Cost == default) ? "---" : collisionRecord.Cost.ToString("C"))</td>
<td class="col-3 flex-grow-1 flex-shrink-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(collisionRecord.Notes)</td>
@foreach (string extraFieldColumn in extraFields)
{
<td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(collisionRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(collisionRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
}
</tr>
}

View File

@@ -172,16 +172,16 @@
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2 flex-grow-1" data-column="daterefueled">@translator.Translate(userLanguage, "Date Refueled")</th>
<th scope="col" class="col-2 flex-grow-1" data-column="odometer">@($"{translator.Translate(userLanguage, "Odometer")}({distanceUnit})")</th>
<th scope="col" class="col-1 flex-grow-1" data-column="delta" style="cursor:pointer;" onclick="toggleSort('gas-tab-pane', this)">@($"Δ({distanceUnit})")</th>
<th scope="col" class="col-2 flex-grow-1" data-column="consumption" data-gas="consumption" data-unit="@consumptionUnit" onclick="toggleSort('gas-tab-pane', this)" oncontextmenu="toggleUnits(this)" style="cursor:pointer;">@($"{translator.Translate(userLanguage, "Consumption")}({consumptionUnit})")</th>
<th scope="col" class="col-3 flex-grow-1" data-column="fueleconomy" data-gas="fueleconomy" data-unit="@fuelEconomyUnit" onclick="toggleSort('gas-tab-pane', this)" oncontextmenu="toggleUnits(this)" style="cursor:pointer;">@($"{@translator.Translate(userLanguage, "Fuel Economy")}({fuelEconomyUnit})")</th>
<th scope="col" class="col-1 flex-grow-1" data-column="cost" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-1 flex-grow-1" data-column="unitcost" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Unit Cost")</th>
<th scope="col" class="col-3 flex-grow-1" style='display:none;' data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="odometer">@($"{translator.Translate(userLanguage, "Odometer")}({distanceUnit})")</th>
<th scope="col" class="col-1 flex-grow-1 flex-shrink-1" data-column="delta" style="cursor:pointer;" onclick="toggleSort('gas-tab-pane', this)">@($"Δ({distanceUnit})")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="consumption" data-gas="consumption" data-unit="@consumptionUnit" onclick="toggleSort('gas-tab-pane', this)" oncontextmenu="toggleUnits(this)" style="cursor:pointer;">@($"{translator.Translate(userLanguage, "Consumption")}({consumptionUnit})")</th>
<th scope="col" class="col-3 flex-grow-1 flex-shrink-1" data-column="fueleconomy" data-gas="fueleconomy" data-unit="@fuelEconomyUnit" onclick="toggleSort('gas-tab-pane', this)" oncontextmenu="toggleUnits(this)" style="cursor:pointer;">@($"{@translator.Translate(userLanguage, "Fuel Economy")}({fuelEconomyUnit})")</th>
<th scope="col" class="col-1 flex-grow-1 flex-shrink-1" data-column="cost" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-1 flex-grow-1 flex-shrink-1" data-column="unitcost" onclick="toggleSort('gas-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Unit Cost")</th>
<th scope="col" class="col-3 flex-grow-1 flex-shrink-1" style='display:none;' data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields)
{
<th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
<th scope="col" style='display:none;' class="col-2 flex-grow-1 flex-shrink-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
}
</tr>
</thead>
@@ -190,16 +190,16 @@
{
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@gasRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditGasRecordModal,@gasRecord.Id)" data-tags='@string.Join(" ", gasRecord.Tags)'>
<td class="col-2 flex-grow-1" data-column="daterefueled">@gasRecord.Date</td>
<td class="col-2 flex-grow-1" data-column="odometer" data-gas-type="mileage" data-gas-aggregate="@gasRecord.DeltaMileage" data-gas-original="@gasRecord.Mileage">@gasRecord.Mileage</td>
<td class="col-1 flex-grow-1" data-column="delta">@(gasRecord.DeltaMileage == default ? "---" : gasRecord.DeltaMileage)</td>
<td class="col-2 flex-grow-1" data-column="consumption" data-gas-type="consumption" data-gas-aggregate="@gasRecord.Gallons">@gasRecord.Gallons.ToString("F")</td>
<td class="col-3 flex-grow-1" data-column="fueleconomy" data-gas-type="fueleconomy" data-aggregated='@(gasRecord.IncludeInAverage.ToString().ToLower())'>@(gasRecord.MilesPerGallon == 0 ? "---" : gasRecord.MilesPerGallon.ToString("F"))</td>
<td class="col-1 flex-grow-1" data-column="cost" data-record-type="cost">@((hideZero && gasRecord.Cost == default) ? "---" : gasRecord.Cost.ToString(gasCostFormat))</td>
<td class="col-1 flex-grow-1" data-column="unitcost" data-gas-type="unitcost">@((hideZero && gasRecord.CostPerGallon == default) ? "---" : gasRecord.CostPerGallon.ToString(gasCostFormat))</td>
<td class="col-3 flex-grow-1 text-truncate" style='display:none;' data-column="notes">@StaticHelper.TruncateStrings(gasRecord.Notes)</td>
<td class="col-2 flex-grow-1 flex-shrink-1" data-column="odometer" data-gas-type="mileage" data-gas-aggregate="@gasRecord.DeltaMileage" data-gas-original="@gasRecord.Mileage">@gasRecord.Mileage</td>
<td class="col-1 flex-grow-1 flex-shrink-1" data-column="delta">@(gasRecord.DeltaMileage == default ? "---" : gasRecord.DeltaMileage)</td>
<td class="col-2 flex-grow-1 flex-shrink-1" data-column="consumption" data-gas-type="consumption" data-gas-aggregate="@gasRecord.Gallons">@gasRecord.Gallons.ToString("F")</td>
<td class="col-3 flex-grow-1 flex-shrink-1" data-column="fueleconomy" data-gas-type="fueleconomy" data-aggregated='@(gasRecord.IncludeInAverage.ToString().ToLower())'>@(gasRecord.MilesPerGallon == 0 ? "---" : gasRecord.MilesPerGallon.ToString("F"))</td>
<td class="col-1 flex-grow-1 flex-shrink-1" data-column="cost" data-record-type="cost">@((hideZero && gasRecord.Cost == default) ? "---" : gasRecord.Cost.ToString(gasCostFormat))</td>
<td class="col-1 flex-grow-1 flex-shrink-1" data-column="unitcost" data-gas-type="unitcost">@((hideZero && gasRecord.CostPerGallon == default) ? "---" : gasRecord.CostPerGallon.ToString(gasCostFormat))</td>
<td class="col-3 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="notes">@StaticHelper.TruncateStrings(gasRecord.Notes)</td>
@foreach (string extraFieldColumn in extraFields)
{
<td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(gasRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(gasRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
}
</tr>
}

View File

@@ -22,6 +22,8 @@
<input type="text" id="odometerRecordDate" class="form-control" placeholder="@translator.Translate(userLanguage,"Date recorded")" value="@Model.Date">
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div>
<label for="initialOdometerRecordMileage">@translator.Translate(userLanguage, "Initial Odometer")</label>
<input type="number" inputmode="numeric" id="initialOdometerRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Initial Odometer reading")" value="@(Model.InitialMileage)">
<label for="odometerRecordMileage">@translator.Translate(userLanguage,"Odometer")</label>
<input type="number" inputmode="numeric" id="odometerRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"Odometer reading")" value="@(isNew ? "" : Model.Mileage)">
<label for="odometerRecordTag">@translator.Translate(userLanguage,"Tags(optional)")</label>

View File

@@ -19,6 +19,7 @@
<div class="d-flex justify-content-between">
<div class="d-flex align-items-center flex-wrap">
<span class="ms-2 badge bg-success" data-aggregate-type="count">@($"{translator.Translate(userLanguage, "# of Odometer Records")}: {Model.Count()}")</span>
<span class="ms-2 badge bg-primary" data-aggregate-type="sum-distance">@($"{translator.Translate(userLanguage, "Total Distance")}: {Model.Sum(x => x.DistanceTraveled)}")</span>
@foreach (string recordTag in recordTags)
{
<span onclick="filterTable('odometer-tab-pane', this)" class="user-select-none ms-2 rounded-pill badge bg-secondary tagfilter" style="cursor:pointer;">@recordTag</span>
@@ -52,12 +53,24 @@
<label class="form-check-label stretched-link" for="chkCol_Date">@translator.Translate(userLanguage, "Date")</label>
</div>
</li>
<li class="dropdown-item">
<div class="list-group-item">
<input class="form-check-input col-visible-toggle" data-column-toggle='initialodometer' onChange="showTableColumns(this, 'OdometerRecord')" type="checkbox" id="chkCol_InitialOdometer" checked>
<label class="form-check-label stretched-link" for="chkCol_InitialOdometer">@translator.Translate(userLanguage, "Initial Odometer")</label>
</div>
</li>
<li class="dropdown-item">
<div class="list-group-item">
<input class="form-check-input col-visible-toggle" data-column-toggle='odometer' onChange="showTableColumns(this, 'OdometerRecord')" type="checkbox" id="chkCol_Odometer" checked>
<label class="form-check-label stretched-link" for="chkCol_Odometer">@translator.Translate(userLanguage, "Odometer")</label>
</div>
</li>
<li class="dropdown-item">
<div class="list-group-item">
<input class="form-check-input col-visible-toggle" data-column-toggle='distance' onChange="showTableColumns(this, 'OdometerRecord')" type="checkbox" id="chkCol_Distance" checked>
<label class="form-check-label stretched-link" for="chkCol_Distance">@translator.Translate(userLanguage, "Distance")</label>
</div>
</li>
<li class="dropdown-item">
<div class="list-group-item">
<input class="form-check-input col-visible-toggle" data-column-toggle='notes' onChange="showTableColumns(this, 'OdometerRecord')" type="checkbox" id="chkCol_Notes" checked>
@@ -95,11 +108,13 @@
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2 col-xl-1 flex-grow-1" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-3 flex-grow-1" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-7 col-xl-8 flex-grow-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
<th scope="col" class="col-3 flex-grow-1 flex-shrink-1" data-column="initialodometer">@translator.Translate(userLanguage, "Initial Odometer")</th>
<th scope="col" class="col-3 flex-grow-1 flex-shrink-1" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="distance" onclick="toggleSort('odometer-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Distance")</th>
<th scope="col" class="col-2 col-xl-3 flex-grow-1 flex-shrink-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields)
{
<th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
<th scope="col" style='display:none;' class="col-2 flex-grow-1 flex-shrink-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
}
</tr>
</thead>
@@ -108,11 +123,13 @@
{
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@odometerRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditOdometerRecordModal,@odometerRecord.Id)" data-tags='@string.Join(" ", odometerRecord.Tags)'>
<td class="col-2 col-xl-1 flex-grow-1" data-column="date">@odometerRecord.Date.ToShortDateString()</td>
<td class="col-3 flex-grow-1" data-column="odometer" data-record-type="cost">@odometerRecord.Mileage</td>
<td class="col-7 col-xl-8 flex-grow-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(odometerRecord.Notes, 75)</td>
<td class="col-3 flex-grow-1 flex-shrink-1" data-column="initialodometer">@odometerRecord.InitialMileage</td>
<td class="col-3 flex-grow-1 flex-shrink-1" data-column="odometer">@odometerRecord.Mileage</td>
<td class="col-2 flex-grow-1 flex-shrink-1" data-column="distance" data-record-type="distance">@(odometerRecord.DistanceTraveled == default ? "---" : odometerRecord.DistanceTraveled)</td>
<td class="col-2 col-xl-3 flex-grow-1 flex-shrink-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(odometerRecord.Notes, 75)</td>
@foreach (string extraFieldColumn in extraFields)
{
<td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(odometerRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(odometerRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
}
</tr>
}
@@ -132,6 +149,9 @@
<ul class="table-context-menu dropdown-menu" style="display:none;">
<li><a class="context-menu-multiple context-menu-select-all dropdown-item" href="#" onclick="selectAllRows()">@translator.Translate(userLanguage, "Select All")</a></li>
<li><a class="context-menu-multiple context-menu-deselect-all dropdown-item" href="#" onclick="clearSelectedRows()">@translator.Translate(userLanguage, "Deselect All")</a></li>
<li><a class="context-menu-active-multiple dropdown-item" href="#" onclick="editMultipleOdometerRecords(selectedRow)">@translator.Translate(userLanguage, "Edit Multiple")</a></li>
<li><hr class="context-menu-multiple context-menu-deselect-all dropdown-divider"></li>
<li><a class="context-menu-multiple context-menu-deselect-all dropdown-item" href="#" onclick="recalculateDistance()">@translator.Translate(userLanguage, "Recalculate Distance")</a></li>
<li><hr class="context-menu-multiple dropdown-divider"></li>
<li><a class="dropdown-item" href="#" onclick="duplicateRecords(selectedRow, 'OdometerRecord')">@translator.Translate(userLanguage, "Duplicate")</a></li>
<li><a class="dropdown-item" href="#" onclick="deleteRecords(selectedRow, 'OdometerRecord')">@translator.Translate(userLanguage, "Delete")</a></li>

View File

@@ -0,0 +1,48 @@
@using CarCareTracker.Helper
@inject IConfigHelper config
@inject ITranslationHelper translator
@model OdometerRecordEditModel
@{
var userConfig = config.GetUserConfig(User);
var userLanguage = userConfig.UserLanguage;
}
<div class="modal-header">
<h5 class="modal-title">@translator.Translate(userLanguage,"Edit Multiple Odometer Records")</h5>
<button type="button" class="btn-close" onclick="hideAddOdometerRecordModal()" aria-label="Close"></button>
</div>
<div class="modal-body">
<form>
<div class="form-group">
<div class="row">
<div class="col-md-6 col-12">
<label for="odometerRecordDate">@translator.Translate(userLanguage, "Date")</label>
<div class="input-group">
<input type="text" id="odometerRecordDate" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
<span class="input-group-text"><i class="bi bi-calendar-event"></i></span>
</div>
<label for="initialOdometerRecordMileage">@translator.Translate(userLanguage, "Initial Odometer")</label>
<input type="number" inputmode="numeric" id="initialOdometerRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
<label for="odometerRecordMileage">@translator.Translate(userLanguage, "Odometer")</label>
<input type="number" inputmode="numeric" id="odometerRecordMileage" class="form-control" placeholder="@translator.Translate(userLanguage,"(multiple)")">
<label for="odometerRecordTag">@translator.Translate(userLanguage, "Tags(use --- to clear all existing tags)")</label>
<select multiple class="form-select" id="odometerRecordTag"></select>
</div>
<div class="col-md-6 col-12">
<label for="odometerRecordNotes">@translator.Translate(userLanguage, "Notes(use --- to clear all existing notes)")<a class="link-underline link-underline-opacity-0" onclick="showLinks(this)"><i class="bi bi-markdown ms-2"></i></a></label>
<textarea id="odometerRecordNotes" class="form-control" rows="5"></textarea>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" onclick="hideAddOdometerRecordModal()">@translator.Translate(userLanguage, "Cancel")</button>
<button type="button" class="btn btn-primary" onclick="saveMultipleOdometerRecordsToVehicle()">@translator.Translate(userLanguage, "Edit")</button>
</div>
<script>
var recordsToEdit = [];
@foreach(int recordId in Model.RecordIds)
{
@:recordsToEdit.push(@recordId);
}
</script>

View File

@@ -108,13 +108,13 @@
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2 col-xl-1 flex-grow-1" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-2 flex-grow-1" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 col-xl-4 flex-grow-1" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2 flex-grow-1" data-column="cost" onclick="toggleSort('servicerecord-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3 flex-grow-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 col-xl-4 flex-grow-1 flex-shrink-1" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="cost" onclick="toggleSort('servicerecord-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3 flex-grow-1 flex-shrink-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields)
{
<th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
<th scope="col" style='display:none;' class="col-2 flex-grow-1 flex-shrink-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
}
</tr>
</thead>
@@ -123,13 +123,13 @@
{
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@serviceRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditServiceRecordModal,@serviceRecord.Id)" data-tags='@string.Join(" ", serviceRecord.Tags)'>
<td class="col-2 col-xl-1 flex-grow-1" data-column="date" data-date="@StaticHelper.GetEpochFromDateTime(serviceRecord.Date)">@serviceRecord.Date.ToShortDateString()</td>
<td class="col-2 flex-grow-1" data-column="odometer">@serviceRecord.Mileage</td>
<td class="col-3 col-xl-4 flex-grow-1" data-column="description">@serviceRecord.Description</td>
<td class="col-2 flex-grow-1" data-column="cost" data-record-type="cost">@((hideZero && serviceRecord.Cost == default) ? "---" : serviceRecord.Cost.ToString("C"))</td>
<td class="col-3 text-truncate flex-grow-1" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(serviceRecord.Notes)</td>
<td class="col-2 flex-grow-1 flex-shrink-1" data-column="odometer">@serviceRecord.Mileage</td>
<td class="col-3 col-xl-4 flex-grow-1 flex-shrink-1" data-column="description">@serviceRecord.Description</td>
<td class="col-2 flex-grow-1 flex-shrink-1" data-column="cost" data-record-type="cost">@((hideZero && serviceRecord.Cost == default) ? "---" : serviceRecord.Cost.ToString("C"))</td>
<td class="col-3 text-truncate flex-grow-1 flex-shrink-1" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(serviceRecord.Notes)</td>
@foreach (string extraFieldColumn in extraFields)
{
<td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(serviceRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(serviceRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
}
</tr>
}

View File

@@ -120,15 +120,15 @@
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2 flex-grow-1 col-xl-1" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-2 flex-grow-1" data-column="partnumber">@translator.Translate(userLanguage, "Part #")</th>
<th scope="col" class="col-2 flex-grow-1" data-column="supplier">@translator.Translate(userLanguage, "Supplier")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="partnumber">@translator.Translate(userLanguage, "Part #")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="supplier">@translator.Translate(userLanguage, "Supplier")</th>
<th scope="col" class="col-2 flex-grow-1 col-xl-3" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-1 flex-grow-1" data-column="quantity" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Quantity")</th>
<th scope="col" class="col-1 flex-grow-1" data-column="cost" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-2 flex-grow-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
<th scope="col" class="col-1 flex-grow-1 flex-shrink-1" data-column="quantity" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Quantity")</th>
<th scope="col" class="col-1 flex-grow-1 flex-shrink-1" data-column="cost" onclick="toggleSort('supply-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields)
{
<th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
<th scope="col" style='display:none;' class="col-2 flex-grow-1 flex-shrink-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
}
</tr>
</thead>
@@ -137,15 +137,15 @@
{
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@supplyRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditSupplyRecordModal,@supplyRecord.Id)" data-tags='@string.Join(" ", supplyRecord.Tags)'>
<td class="col-2 flex-grow-1 col-xl-1" data-column="date">@supplyRecord.Date.ToShortDateString()</td>
<td class="col-2 flex-grow-1" data-column="partnumber">@supplyRecord.PartNumber</td>
<td class="col-2 flex-grow-1" data-column="supplier">@supplyRecord.PartSupplier</td>
<td class="col-2 flex-grow-1 flex-shrink-1" data-column="partnumber">@supplyRecord.PartNumber</td>
<td class="col-2 flex-grow-1 flex-shrink-1" data-column="supplier">@supplyRecord.PartSupplier</td>
<td class="col-2 flex-grow-1 col-xl-3" data-column="description">@supplyRecord.Description</td>
<td class="col-1 flex-grow-1" data-column="quantity">@supplyRecord.Quantity</td>
<td class="col-1 flex-grow-1" data-column="cost" data-record-type="cost">@((hideZero && supplyRecord.Cost == default) ? "---" : supplyRecord.Cost.ToString("C"))</td>
<td class="col-2 flex-grow-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(supplyRecord.Notes)</td>
<td class="col-1 flex-grow-1 flex-shrink-1" data-column="quantity">@supplyRecord.Quantity</td>
<td class="col-1 flex-grow-1 flex-shrink-1" data-column="cost" data-record-type="cost">@((hideZero && supplyRecord.Cost == default) ? "---" : supplyRecord.Cost.ToString("C"))</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(supplyRecord.Notes)</td>
@foreach (string extraFieldColumn in extraFields)
{
<td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(supplyRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(supplyRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
}
</tr>
}

View File

@@ -103,11 +103,11 @@
<tr class="d-flex">
<th scope="col" class="col-3 flex-grow-1 col-xl-1" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-4 flex-grow-1 col-xl-6" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2 flex-grow-1" data-column="cost" onclick="toggleSort('tax-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3 flex-grow-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="cost" onclick="toggleSort('tax-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3 flex-grow-1 flex-shrink-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields)
{
<th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
<th scope="col" style='display:none;' class="col-2 flex-grow-1 flex-shrink-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
}
</tr>
</thead>
@@ -117,11 +117,11 @@
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@taxRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditTaxRecordModal,@taxRecord.Id)" data-tags='@string.Join(" ", taxRecord.Tags)'>
<td class="col-3 flex-grow-1 col-xl-1" data-column="date">@taxRecord.Date.ToShortDateString()</td>
<td class="col-4 flex-grow-1 col-xl-6" data-column="description">@taxRecord.Description</td>
<td class="col-2 flex-grow-1" data-column="cost" data-record-type="cost">@((hideZero && taxRecord.Cost == default) ? "---" : taxRecord.Cost.ToString("C"))</td>
<td class="col-3 flex-grow-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(taxRecord.Notes)</td>
<td class="col-2 flex-grow-1 flex-shrink-1" data-column="cost" data-record-type="cost">@((hideZero && taxRecord.Cost == default) ? "---" : taxRecord.Cost.ToString("C"))</td>
<td class="col-3 flex-grow-1 flex-shrink-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(taxRecord.Notes)</td>
@foreach (string extraFieldColumn in extraFields)
{
<td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(taxRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(taxRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
}
</tr>
}

View File

@@ -108,13 +108,13 @@
<thead class="sticky-top">
<tr class="d-flex">
<th scope="col" class="col-2 flex-grow-1 col-xl-1" data-column="date">@translator.Translate(userLanguage, "Date")</th>
<th scope="col" class="col-2 flex-grow-1" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="odometer">@translator.Translate(userLanguage, "Odometer")</th>
<th scope="col" class="col-3 flex-grow-1 col-xl-4" data-column="description">@translator.Translate(userLanguage, "Description")</th>
<th scope="col" class="col-2 flex-grow-1" data-column="cost" onclick="toggleSort('upgrade-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3 flex-grow-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
<th scope="col" class="col-2 flex-grow-1 flex-shrink-1" data-column="cost" onclick="toggleSort('upgrade-tab-pane', this)" style="cursor:pointer;">@translator.Translate(userLanguage, "Cost")</th>
<th scope="col" class="col-3 flex-grow-1 flex-shrink-1" data-column="notes">@translator.Translate(userLanguage, "Notes")</th>
@foreach (string extraFieldColumn in extraFields)
{
<th scope="col" style='display:none;' class="col-2 flex-grow-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
<th scope="col" style='display:none;' class="col-2 flex-grow-1 flex-shrink-1" data-column="@extraFieldColumn">@extraFieldColumn</th>
}
</tr>
</thead>
@@ -123,13 +123,13 @@
{
<tr class="d-flex user-select-none" style="cursor:pointer;" onmouseup="stopEvent()" ontouchstart="detectRowLongTouch(this)" ontouchend="detectRowTouchEndPremature(this)" data-rowId="@upgradeRecord.Id" oncontextmenu="showTableContextMenu(this)" onmousemove="rangeMouseMove(this)" onclick="handleTableRowClick(this, showEditUpgradeRecordModal,@upgradeRecord.Id)" data-tags='@string.Join(" ", upgradeRecord.Tags)'>
<td class="col-2 flex-grow-1 col-xl-1" data-column="date" data-date="@StaticHelper.GetEpochFromDateTime(upgradeRecord.Date)">@upgradeRecord.Date.ToShortDateString()</td>
<td class="col-2 flex-grow-1" data-column="odometer">@upgradeRecord.Mileage</td>
<td class="col-2 flex-grow-1 flex-shrink-1" data-column="odometer">@upgradeRecord.Mileage</td>
<td class="col-3 flex-grow-1 col-xl-4" data-column="description">@upgradeRecord.Description</td>
<td class="col-2 flex-grow-1" data-column="cost" data-record-type="cost">@((hideZero && upgradeRecord.Cost == default) ? "---" : upgradeRecord.Cost.ToString("C"))</td>
<td class="col-3 flex-grow-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(upgradeRecord.Notes)</td>
<td class="col-2 flex-grow-1 flex-shrink-1" data-column="cost" data-record-type="cost">@((hideZero && upgradeRecord.Cost == default) ? "---" : upgradeRecord.Cost.ToString("C"))</td>
<td class="col-3 flex-grow-1 flex-shrink-1 text-truncate" data-column="notes">@CarCareTracker.Helper.StaticHelper.TruncateStrings(upgradeRecord.Notes)</td>
@foreach (string extraFieldColumn in extraFields)
{
<td class="col-2 flex-grow-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(upgradeRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
<td class="col-2 flex-grow-1 flex-shrink-1 text-truncate" style='display:none;' data-column="@extraFieldColumn">@(upgradeRecord.ExtraFields.Where(x => x.Name == extraFieldColumn)?.FirstOrDefault()?.Value ?? "")</td>
}
</tr>
}

View File

@@ -82,6 +82,10 @@ html {
overflow: visible;
}
body {
background-color: #fff !important;
}
table {
background-color: #fff !important;
}
@@ -366,4 +370,7 @@ input[type="file"] {
text-align: center;
position: absolute;
z-index: 10;
}
.copyable{
cursor: pointer;
}

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
function showAddOdometerRecordModal() {
$.get('/Vehicle/GetAddOdometerRecordPartialView', function (data) {
$.get(`/Vehicle/GetAddOdometerRecordPartialView?vehicleId=${GetVehicleId().vehicleId}`, function (data) {
if (data) {
$("#odometerRecordModalContent").html(data);
//initiate datepicker
@@ -78,6 +78,7 @@ function saveOdometerRecordToVehicle(isEdit) {
}
function getAndValidateOdometerRecordValues() {
var serviceDate = $("#odometerRecordDate").val();
var initialOdometerMileage = parseInt(globalParseFloat($("#initialOdometerRecordMileage").val())).toString();
var serviceMileage = parseInt(globalParseFloat($("#odometerRecordMileage").val())).toString();
var serviceNotes = $("#odometerRecordNotes").val();
var serviceTags = $("#odometerRecordTag").val();
@@ -101,15 +102,95 @@ function getAndValidateOdometerRecordValues() {
} else {
$("#odometerRecordMileage").removeClass("is-invalid");
}
if (isNaN(initialOdometerMileage) || parseInt(initialOdometerMileage) < 0) {
hasError = true;
$("#initialOdometerRecordMileage").addClass("is-invalid");
} else {
$("#initialOdometerRecordMileage").removeClass("is-invalid");
}
return {
id: odometerRecordId,
hasError: hasError,
vehicleId: vehicleId,
date: serviceDate,
initialMileage: initialOdometerMileage,
mileage: serviceMileage,
notes: serviceNotes,
tags: serviceTags,
files: uploadedFiles,
extraFields: extraFields.extraFields
}
}
function recalculateDistance() {
//force distance recalculation
//reserved for when data is incoherent with negative distances due to non-chronologica order of odometer records.
var vehicleId = GetVehicleId().vehicleId
$.post(`/Vehicle/ForceRecalculateDistanceByVehicleId?vehicleId=${vehicleId}`, function (data) {
if (data) {
successToast("Odometer Records Updated")
getVehicleOdometerRecords(vehicleId);
} else {
errorToast(genericErrorMessage());
}
});
}
function editMultipleOdometerRecords(ids) {
$.post('/Vehicle/GetOdometerRecordsEditModal', { recordIds: ids }, function (data) {
if (data) {
$("#odometerRecordModalContent").html(data);
//initiate datepicker
initDatePicker($('#odometerRecordDate'));
initTagSelector($("#odometerRecordTag"));
$('#odometerRecordModal').modal('show');
}
});
}
function saveMultipleOdometerRecordsToVehicle() {
var odometerDate = $("#odometerRecordDate").val();
var initialOdometerMileage = $("#initialOdometerRecordMileage").val();
var odometerMileage = $("#odometerRecordMileage").val();
var initialOdometerMileageToParse = parseInt(globalParseFloat($("#initialOdometerRecordMileage").val())).toString();
var odometerMileageToParse = parseInt(globalParseFloat($("#odometerRecordMileage").val())).toString();
var odometerNotes = $("#odometerRecordNotes").val();
var odometerTags = $("#odometerRecordTag").val();
//validation
var hasError = false;
if (odometerMileage.trim() != '' && (isNaN(odometerMileageToParse) || parseInt(odometerMileageToParse) < 0)) {
hasError = true;
$("#odometerRecordMileage").addClass("is-invalid");
} else {
$("#odometerRecordMileage").removeClass("is-invalid");
}
if (initialOdometerMileage.trim() != '' && (isNaN(initialOdometerMileageToParse) || parseInt(initialOdometerMileageToParse) < 0)) {
hasError = true;
$("#odometerRecordMileage").addClass("is-invalid");
} else {
$("#odometerRecordMileage").removeClass("is-invalid");
}
if (hasError) {
errorToast("Please check the form data");
return;
}
var formValues = {
recordIds: recordsToEdit,
editRecord: {
date: odometerDate,
initialMileage: initialOdometerMileageToParse,
mileage: odometerMileageToParse,
notes: odometerNotes,
tags: odometerTags
}
}
$.post('/Vehicle/SaveMultipleOdometerRecords', { editModel: formValues }, function (data) {
if (data) {
successToast("Odometer Records Updated");
hideAddOdometerRecordModal();
saveScrollPosition();
getVehicleOdometerRecords(GetVehicleId().vehicleId);
} else {
errorToast(genericErrorMessage());
}
})
}

View File

@@ -292,6 +292,16 @@ function updateAggregateLabels() {
}
sumLabel.text(`${sumLabel.text().split(':')[0]}: ${getGlobalConfig().currencySymbol}${newSum}`)
}
//Sum Distance
var sumDistanceLabel = $("[data-aggregate-type='sum-distance']");
if (sumDistanceLabel.length > 0) {
var distanceLabelsToSum = $("[data-record-type='distance']").parent(":not('.override-hide')").children("[data-record-type='distance']").toArray();
var newDistanceSum = 0;
if (distanceLabelsToSum.length > 0) {
newDistanceSum = distanceLabelsToSum.map(x => globalParseFloat(x.textContent)).reduce((a, b,) => a + b).toFixed(0);
}
sumDistanceLabel.text(`${sumDistanceLabel.text().split(':')[0]}: ${newDistanceSum}`)
}
//Count
var newCount = $("[data-record-type='cost']").parent(":not('.override-hide')").length;
var countLabel = $("[data-aggregate-type='count']");
@@ -643,7 +653,7 @@ $(window).on('mousedown', function (e) {
$(window).on('keydown', function (e) {
var userOnInput = $(e.target).is("input") || $(e.target).is("textarea");
if (!userOnInput) {
if (e.ctrlKey && e.which == 65) {
if ((e.ctrlKey || e.metaKey) && e.which == 65) {
e.preventDefault();
e.stopPropagation();
selectAllRows();
@@ -662,7 +672,7 @@ function rangeMouseDown(e) {
return;
}
var contextMenuAction = $(e.target).is(".table-context-menu > li > .dropdown-item")
if (!e.ctrlKey && !contextMenuAction) {
if (!(e.ctrlKey || e.metaKey) && !contextMenuAction) {
clearSelectedRows();
}
isDragging = true;
@@ -786,7 +796,7 @@ function getMenuPosition(mouse, direction, scrollDir) {
return position;
}
function handleTableRowClick(e, callBack, rowId) {
if (!event.ctrlKey) {
if (!(event.ctrlKey || event.metaKey)) {
callBack(rowId);
} else if (!$(e).hasClass('table-active')) {
addToSelectedRows($(e).attr('data-rowId'));
@@ -959,4 +969,9 @@ function saveUserColumnPreferences(importMode) {
errorToast(genericErrorMessage());
}
});
}
function copyToClipboard(e) {
var textToCopy = e.textContent.trim();
navigator.clipboard.writeText(textToCopy);
successToast("Copied to Clipboard");
}